github.com/mailgun/holster/v4@v4.20.0/tracing/level.go (about)

     1  package tracing
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"go.opentelemetry.io/otel/attribute"
     9  	sdktrace "go.opentelemetry.io/otel/sdk/trace"
    10  	"go.opentelemetry.io/otel/trace"
    11  )
    12  
    13  type Level uint64
    14  
    15  // LevelTracerProvider wraps a TracerProvider to apply log level processing.
    16  // Tag spans with `log.level` and `log.levelNum=n`, where `n` is numeric log
    17  // level 0-6 (Panic, Fatal, Error, Warn, Info, Debug, Trace).
    18  // If span log level is lower severity than threshold, create a `DummySpan`
    19  // instead.
    20  // `DummySpan` behaves like an alias of its next non-dummy ancestor, but gets
    21  // filtered out and omitted from export.  Nested spans containing a mix of real
    22  // and `DummySpan` will be linked as if the `DummySpan` never happened.
    23  type LevelTracerProvider struct {
    24  	*sdktrace.TracerProvider
    25  	level Level
    26  }
    27  
    28  // LevelTracer is created by `LevelTracerProvider`.
    29  type LevelTracer struct {
    30  	trace.Tracer
    31  	level Level
    32  }
    33  
    34  // LogLevelKey is the span attribute key for storing numeric log level.
    35  const LogLevelKey = "log.level"
    36  
    37  const (
    38  	PanicLevel Level = iota
    39  	FatalLevel
    40  	ErrorLevel
    41  	WarnLevel
    42  	InfoLevel
    43  	DebugLevel
    44  	TraceLevel
    45  )
    46  
    47  var logLevelCtxKey struct{}
    48  var logLevelNames = []string{
    49  	"PANIC",
    50  	"FATAL",
    51  	"ERROR",
    52  	"WARNING",
    53  	"INFO",
    54  	"DEBUG",
    55  	"TRACE",
    56  }
    57  
    58  var logLevelMap = map[string]Level{
    59  	"PANIC":   PanicLevel,
    60  	"FATAL":   FatalLevel,
    61  	"ERROR":   ErrorLevel,
    62  	"WARNING": WarnLevel,
    63  	"INFO":    InfoLevel,
    64  	"DEBUG":   DebugLevel,
    65  	"TRACE":   TraceLevel,
    66  }
    67  
    68  func NewLevelTracerProvider(level Level, opts ...sdktrace.TracerProviderOption) *LevelTracerProvider {
    69  	tp := sdktrace.NewTracerProvider(opts...)
    70  
    71  	return &LevelTracerProvider{
    72  		TracerProvider: tp,
    73  		level:          level,
    74  	}
    75  }
    76  
    77  func (tp *LevelTracerProvider) Tracer(libraryName string, opts ...trace.TracerOption) trace.Tracer {
    78  	tracer := tp.TracerProvider.Tracer(libraryName, opts...)
    79  	return &LevelTracer{
    80  		Tracer: tracer,
    81  		level:  tp.level,
    82  	}
    83  }
    84  
    85  func (t *LevelTracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
    86  	// Check log level.
    87  	ctxLevel, hasLevel := ctx.Value(logLevelCtxKey).(Level)
    88  	if hasLevel {
    89  		// Prevent log level parameter from propagating to child spans.
    90  		ctx = context.WithValue(ctx, logLevelCtxKey, nil)
    91  
    92  		if ctxLevel > t.level {
    93  			return newDummySpan(ctx)
    94  		}
    95  	} else {
    96  		ctxLevel = InfoLevel
    97  	}
    98  
    99  	// Pass-through.
   100  	spanCtx, span := t.Tracer.Start(ctx, spanName, opts...)
   101  	span.SetAttributes(
   102  		attribute.String(LogLevelKey, ctxLevel.String()),
   103  	)
   104  
   105  	return spanCtx, span
   106  }
   107  
   108  func (level Level) String() string {
   109  	if level <= TraceLevel {
   110  		return logLevelNames[level]
   111  	}
   112  	return ""
   113  }
   114  
   115  func ParseLogLevel(levelStr string) (Level, error) {
   116  	level, ok := logLevelMap[strings.ToUpper(levelStr)]
   117  	if !ok {
   118  		return Level(0), fmt.Errorf("unknown log level %q", levelStr)
   119  	}
   120  	return level, nil
   121  }