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  }