github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/util/spanlogger/spanlogger.go (about) 1 package spanlogger 2 3 import ( 4 "context" 5 6 "github.com/go-kit/log" 7 "github.com/go-kit/log/level" 8 opentracing "github.com/opentracing/opentracing-go" 9 "github.com/opentracing/opentracing-go/ext" 10 otlog "github.com/opentracing/opentracing-go/log" 11 12 "github.com/cortexproject/cortex/pkg/tenant" 13 util_log "github.com/cortexproject/cortex/pkg/util/log" 14 ) 15 16 type loggerCtxMarker struct{} 17 18 const ( 19 TenantIDTagName = "tenant_ids" 20 ) 21 22 var ( 23 loggerCtxKey = &loggerCtxMarker{} 24 ) 25 26 // SpanLogger unifies tracing and logging, to reduce repetition. 27 type SpanLogger struct { 28 log.Logger 29 opentracing.Span 30 } 31 32 // New makes a new SpanLogger, where logs will be sent to the global logger. 33 func New(ctx context.Context, method string, kvps ...interface{}) (*SpanLogger, context.Context) { 34 return NewWithLogger(ctx, util_log.Logger, method, kvps...) 35 } 36 37 // NewWithLogger makes a new SpanLogger with a custom log.Logger to send logs 38 // to. The provided context will have the logger attached to it and can be 39 // retrieved with FromContext or FromContextWithFallback. 40 func NewWithLogger(ctx context.Context, l log.Logger, method string, kvps ...interface{}) (*SpanLogger, context.Context) { 41 span, ctx := opentracing.StartSpanFromContext(ctx, method) 42 if ids, _ := tenant.TenantIDs(ctx); len(ids) > 0 { 43 span.SetTag(TenantIDTagName, ids) 44 } 45 logger := &SpanLogger{ 46 Logger: log.With(util_log.WithContext(ctx, l), "method", method), 47 Span: span, 48 } 49 if len(kvps) > 0 { 50 level.Debug(logger).Log(kvps...) 51 } 52 53 ctx = context.WithValue(ctx, loggerCtxKey, l) 54 return logger, ctx 55 } 56 57 // FromContext returns a span logger using the current parent span. If there 58 // is no parent span, the SpanLogger will only log to the logger 59 // in the context. If the context doesn't have a logger, the global logger 60 // is used. 61 func FromContext(ctx context.Context) *SpanLogger { 62 return FromContextWithFallback(ctx, util_log.Logger) 63 } 64 65 // FromContextWithFallback returns a span logger using the current parent span. 66 // IF there is no parent span, the SpanLogger will only log to the logger 67 // within the context. If the context doesn't have a logger, the fallback 68 // logger is used. 69 func FromContextWithFallback(ctx context.Context, fallback log.Logger) *SpanLogger { 70 logger, ok := ctx.Value(loggerCtxKey).(log.Logger) 71 if !ok { 72 logger = fallback 73 } 74 sp := opentracing.SpanFromContext(ctx) 75 if sp == nil { 76 sp = defaultNoopSpan 77 } 78 return &SpanLogger{ 79 Logger: util_log.WithContext(ctx, logger), 80 Span: sp, 81 } 82 } 83 84 // Log implements gokit's Logger interface; sends logs to underlying logger and 85 // also puts the on the spans. 86 func (s *SpanLogger) Log(kvps ...interface{}) error { 87 s.Logger.Log(kvps...) 88 fields, err := otlog.InterleavedKVToFields(kvps...) 89 if err != nil { 90 return err 91 } 92 s.Span.LogFields(fields...) 93 return nil 94 } 95 96 // Error sets error flag and logs the error on the span, if non-nil. Returns the err passed in. 97 func (s *SpanLogger) Error(err error) error { 98 if err == nil { 99 return nil 100 } 101 ext.Error.Set(s.Span, true) 102 s.Span.LogFields(otlog.Error(err)) 103 return err 104 }