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 }