get.porter.sh/porter@v1.3.0/pkg/tracing/tracing.go (about) 1 package tracing 2 3 import ( 4 "context" 5 6 "go.opentelemetry.io/otel/attribute" 7 "go.opentelemetry.io/otel/trace" 8 "go.opentelemetry.io/otel/trace/noop" 9 "go.uber.org/zap" 10 "go.uber.org/zap/zapcore" 11 ) 12 13 // custom type for looking up values from context.Context 14 type contextKey string 15 16 // key for retrieving the TraceLogger stored on the context 17 const contextKeyTraceLogger = contextKey("porter.traceLogger") 18 19 // stores data in context.Context to recreate a TraceLogger 20 type traceLoggerContext struct { 21 logger *zap.Logger 22 tracer trace.Tracer 23 } 24 25 // LoggerFromContext retrieves a logger from the specified context. 26 // When the context is missing a logger/tracer, no-op implementations are provided. 27 func LoggerFromContext(ctx context.Context) TraceLogger { 28 span := trace.SpanFromContext(ctx) 29 30 var logger *zap.Logger 31 var tracer trace.Tracer 32 if tl, ok := ctx.Value(contextKeyTraceLogger).(traceLoggerContext); ok { 33 logger = tl.logger 34 tracer = tl.tracer 35 } else { 36 // default to no-op 37 logger = zap.NewNop() 38 tracer = noop.NewTracerProvider().Tracer("noop") 39 } 40 41 return newTraceLogger(ctx, span, logger, NewTracer(tracer, nil)) 42 } 43 44 // StartSpan retrieves a logger from the current context and starts a new span 45 // named after the current function. 46 func StartSpan(ctx context.Context, attrs ...attribute.KeyValue) (context.Context, TraceLogger) { 47 log := LoggerFromContext(ctx) 48 return log.StartSpan(attrs...) 49 } 50 51 // StartSpanWithName retrieves a logger from the current context and starts a span with 52 // the specified name. 53 func StartSpanWithName(ctx context.Context, op string, attrs ...attribute.KeyValue) (context.Context, TraceLogger) { 54 log := LoggerFromContext(ctx) 55 return log.StartSpanWithName(op, attrs...) 56 } 57 58 func convertAttributesToFields(attrs []attribute.KeyValue) []zap.Field { 59 fields := make([]zap.Field, len(attrs)) 60 for i, attr := range attrs { 61 fields[i] = convertAttributeToField(attr) 62 } 63 return fields 64 } 65 66 func convertAttributeToField(attr attribute.KeyValue) zapcore.Field { 67 key := string(attr.Key) 68 69 switch attr.Value.Type() { 70 71 case attribute.BOOL: 72 return zap.Bool(key, attr.Value.AsBool()) 73 case attribute.BOOLSLICE: 74 return zap.Bools(key, attr.Value.AsBoolSlice()) 75 case attribute.FLOAT64: 76 return zap.Float64(key, attr.Value.AsFloat64()) 77 case attribute.FLOAT64SLICE: 78 return zap.Float64s(key, attr.Value.AsFloat64Slice()) 79 case attribute.INT64: 80 return zap.Int64(key, attr.Value.AsInt64()) 81 case attribute.INT64SLICE: 82 return zap.Int64s(key, attr.Value.AsInt64Slice()) 83 case attribute.STRING: 84 return zap.String(key, attr.Value.AsString()) 85 case attribute.STRINGSLICE: 86 return zap.Strings(key, attr.Value.AsStringSlice()) 87 default: 88 // Give up and do our best 89 b, _ := attr.Value.MarshalJSON() 90 return zap.String(key, string(b)) 91 } 92 }