github.com/thanos-io/thanos@v0.32.5/internal/cortex/util/spanlogger/spanlogger.go (about)

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