github.com/thanos-io/thanos@v0.32.5/pkg/tracing/stackdriver/tracer.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package stackdriver 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "os" 11 12 trace "cloud.google.com/go/trace/apiv1" 13 pb "cloud.google.com/go/trace/apiv1/tracepb" 14 "github.com/go-kit/log" 15 "github.com/go-kit/log/level" 16 "github.com/googleapis/gax-go" 17 gcloudtracer "github.com/lovoo/gcloud-opentracing" 18 "github.com/opentracing/basictracer-go" 19 "github.com/opentracing/opentracing-go" 20 "github.com/prometheus/common/version" 21 22 "github.com/thanos-io/thanos/pkg/tracing" 23 ) 24 25 type tracer struct { 26 serviceName string 27 wrapped opentracing.Tracer 28 } 29 30 // GetTraceIDFromSpanContext return TraceID from span.Context. 31 func (t *tracer) GetTraceIDFromSpanContext(ctx opentracing.SpanContext) (string, bool) { 32 if c, ok := ctx.(basictracer.SpanContext); ok { 33 // "%016x%016x" - ugly hack for gcloud find traces by ID https://console.cloud.google.com/traces/traces?project=<project_id>&tid=<62119f61b7c2663962119f61b7c26639>. 34 return fmt.Sprintf("%016x%016x", c.TraceID, c.TraceID), true 35 } 36 return "", false 37 } 38 39 func (t *tracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { 40 span := t.wrapped.StartSpan(operationName, opts...) 41 42 if t.serviceName != "" { 43 span.SetTag("service_name", t.serviceName) 44 } 45 46 // Set common tags. 47 if hostname := os.Getenv("HOSTNAME"); hostname != "" { 48 span.SetTag("hostname", hostname) 49 } 50 51 span.SetTag("binary_revision", version.Revision) 52 if len(os.Args) > 1 { 53 span.SetTag("binary_cmd", os.Args[1]) 54 } 55 56 return span 57 } 58 59 func (t *tracer) Extract(format, carrier interface{}) (opentracing.SpanContext, error) { 60 return t.wrapped.Extract(format, carrier) 61 } 62 63 func (t *tracer) Inject(sm opentracing.SpanContext, format, carrier interface{}) error { 64 return t.wrapped.Inject(sm, format, carrier) 65 } 66 67 type forceRecorder struct { 68 wrapped basictracer.SpanRecorder 69 } 70 71 // RecordSpan invokes wrapper SpanRecorder only if Sampled field is true or ForceTracingBaggageKey item is set in span's context. 72 // NOTE(bplotka): Currently only HTTP supports ForceTracingBaggageKey injection on ForceTracingBaggageKey header existence. 73 func (r *forceRecorder) RecordSpan(sp basictracer.RawSpan) { 74 if force := sp.Context.Baggage[tracing.ForceTracingBaggageKey]; force != "" { 75 sp.Context.Sampled = true 76 } 77 78 // All recorder implementation should support handling sp.Context.Sampled. 79 r.wrapped.RecordSpan(sp) 80 } 81 82 type gcloudRecorderLogger struct { 83 logger log.Logger 84 } 85 86 func (l *gcloudRecorderLogger) Infof(format string, args ...interface{}) { 87 level.Info(l.logger).Log("msg", fmt.Sprintf(format, args...)) 88 } 89 90 func (l *gcloudRecorderLogger) Errorf(format string, args ...interface{}) { 91 level.Error(l.logger).Log("msg", fmt.Sprintf(format, args...)) 92 } 93 94 // TODO(bwplotka): gcloudtracer is archived. Find replacement. For now wrap traceClient for compatibility. 95 type compTraceWrapper struct { 96 cl *trace.Client 97 } 98 99 func (w *compTraceWrapper) PatchTraces(ctx context.Context, r *pb.PatchTracesRequest, _ ...gax.CallOption) error { 100 // Opts are never used in `gcloudtracer.NewRecorder`. 101 return w.cl.PatchTraces(ctx, r) 102 } 103 104 func (w *compTraceWrapper) Close() error { 105 return w.cl.Close() 106 } 107 108 func newGCloudTracer(ctx context.Context, logger log.Logger, gcloudTraceProjectID string, sampleFactor uint64, serviceName string) (opentracing.Tracer, io.Closer, error) { 109 traceClient, err := trace.NewClient(ctx) 110 if err != nil { 111 return nil, nil, err 112 } 113 114 // TODO(bwplotka): gcloudtracer is archived. Find replacement. For now wrap traceClient for compatibility. 115 r, err := gcloudtracer.NewRecorder( 116 ctx, 117 gcloudTraceProjectID, 118 &compTraceWrapper{cl: traceClient}, 119 gcloudtracer.WithLogger(&gcloudRecorderLogger{logger: logger})) 120 if err != nil { 121 return nil, nil, err 122 } 123 124 shouldSample := func(traceID uint64) bool { 125 // Set the sampling rate. 126 return traceID%sampleFactor == 0 127 } 128 if sampleFactor < 1 { 129 level.Debug(logger).Log("msg", "Tracing is enabled, but sampling is 0 which means only spans with 'force tracing' baggage will enable tracing.") 130 shouldSample = func(_ uint64) bool { 131 return false 132 } 133 } 134 return &tracer{ 135 serviceName: serviceName, 136 wrapped: basictracer.NewWithOptions(basictracer.Options{ 137 ShouldSample: shouldSample, 138 Recorder: &forceRecorder{wrapped: r}, 139 MaxLogsPerSpan: 100, 140 }), 141 }, r, nil 142 }