github.com/m3db/m3@v1.5.0/src/x/opentracing/tracing.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package opentracing 22 23 import ( 24 "context" 25 "fmt" 26 "io" 27 "runtime" 28 "strings" 29 "time" 30 31 lightstep "github.com/lightstep/lightstep-tracer-go" 32 "github.com/opentracing/opentracing-go" 33 "github.com/uber-go/tally" 34 jaegercfg "github.com/uber/jaeger-client-go/config" 35 jaegerzap "github.com/uber/jaeger-client-go/log/zap" 36 jaegertally "github.com/uber/jaeger-lib/metrics/tally" 37 "go.opentelemetry.io/otel/attribute" 38 otelopentracing "go.opentelemetry.io/otel/bridge/opentracing" 39 sdktrace "go.opentelemetry.io/otel/sdk/trace" 40 "go.uber.org/zap" 41 42 "github.com/m3db/m3/src/x/instrument" 43 "github.com/m3db/m3/src/x/opentelemetry" 44 ) 45 46 const ( 47 spanTagBuildRevision = "build.revision" 48 spanTagBuildVersion = "build.version" 49 spanTagBuildBranch = "build.branch" 50 spanTagBuildDate = "build.date" 51 spanTagBuildTimeUnix = "build.time_unix" 52 spanTagBuildGoVersion = "build.go_version" 53 ) 54 55 var ( 56 // TracingBackendOpenTelemetry indicates the OpenTelemetry backend should be used. 57 TracingBackendOpenTelemetry = "opentelemetry" 58 // TracingBackendJaeger indicates the Jaeger backend should be used. 59 TracingBackendJaeger = "jaeger" 60 // TracingBackendLightstep indicates the LightStep backend should be used. 61 TracingBackendLightstep = "lightstep" 62 63 supportedBackends = []string{ 64 TracingBackendOpenTelemetry, 65 TracingBackendJaeger, 66 TracingBackendLightstep, 67 } 68 69 tracerSpanTags = map[string]string{ 70 spanTagBuildRevision: instrument.Revision, 71 spanTagBuildBranch: instrument.Branch, 72 spanTagBuildVersion: instrument.Version, 73 spanTagBuildDate: instrument.BuildDate, 74 spanTagBuildTimeUnix: instrument.BuildTimeUnix, 75 spanTagBuildGoVersion: runtime.Version(), 76 } 77 ) 78 79 // TracingConfiguration configures an opentracing backend for m3query to use. Currently only jaeger is supported. 80 // Tracing is disabled if no backend is specified. 81 type TracingConfiguration struct { 82 ServiceName string `yaml:"serviceName"` 83 Backend string `yaml:"backend"` 84 OpenTelemetry opentelemetry.Configuration `yaml:"opentelemetry"` 85 Jaeger jaegercfg.Configuration `yaml:"jaeger"` 86 Lightstep lightstep.Options `yaml:"lightstep"` 87 } 88 89 // NewTracer returns a tracer configured with the configuration provided by this struct. The tracer's concrete 90 // type is determined by cfg.Backend. Currently only `"jaeger"` is supported. `""` implies 91 // disabled (NoopTracer). 92 func (cfg *TracingConfiguration) NewTracer( 93 defaultServiceName string, 94 scope tally.Scope, 95 logger *zap.Logger, 96 ) (opentracing.Tracer, io.Closer, error) { 97 switch cfg.Backend { 98 case "": 99 return opentracing.NoopTracer{}, noopCloser{}, nil 100 101 case TracingBackendOpenTelemetry: 102 logger.Info("initializing OpenTelemetry tracer") 103 return cfg.newOpenTelemetryTracer(defaultServiceName, scope) 104 105 case TracingBackendJaeger: 106 logger.Info("initializing Jaeger tracer") 107 return cfg.newJaegerTracer(defaultServiceName, scope, logger) 108 109 case TracingBackendLightstep: 110 logger.Info("initializing LightStep tracer") 111 return cfg.newLightstepTracer(defaultServiceName) 112 113 default: 114 return nil, nil, fmt.Errorf("unknown tracing backend: %s. Supported backends are: [%s]", 115 cfg.Backend, 116 strings.Join(supportedBackends, ",")) 117 } 118 } 119 120 func (cfg *TracingConfiguration) newOpenTelemetryTracer( 121 defaultServiceName string, 122 scope tally.Scope, 123 ) (opentracing.Tracer, io.Closer, error) { 124 if cfg.OpenTelemetry.ServiceName == "" { 125 cfg.OpenTelemetry.ServiceName = defaultServiceName 126 } 127 128 ctx := context.Background() 129 attrs := make([]attribute.KeyValue, 0, len(tracerSpanTags)) 130 for k, v := range tracerSpanTags { 131 attrs = append(attrs, attribute.String(k, v)) 132 } 133 opts := opentelemetry.TracerProviderOptions{Attributes: attrs} 134 tracerProvider, err := cfg.OpenTelemetry.NewTracerProvider(ctx, scope, opts) 135 if err != nil { 136 return nil, nil, err 137 } 138 139 tracer := tracerProvider.Tracer("") 140 ctx, openTracingBridgeTracer, _ := otelopentracing.NewTracerPairWithContext(ctx, tracer) 141 closer := newTraceProviderCloser(ctx, tracerProvider) 142 return openTracingBridgeTracer, closer, err 143 } 144 145 type traceProviderCloser struct { 146 ctx context.Context 147 tracerProvider *sdktrace.TracerProvider 148 } 149 150 func newTraceProviderCloser( 151 ctx context.Context, 152 tracerProvider *sdktrace.TracerProvider, 153 ) io.Closer { 154 return &traceProviderCloser{ 155 ctx: ctx, 156 tracerProvider: tracerProvider, 157 } 158 } 159 160 func (c *traceProviderCloser) Close() error { 161 return c.tracerProvider.Shutdown(c.ctx) 162 } 163 164 func (cfg *TracingConfiguration) newJaegerTracer( 165 defaultServiceName string, 166 scope tally.Scope, 167 logger *zap.Logger, 168 ) (opentracing.Tracer, io.Closer, error) { 169 if cfg.Jaeger.ServiceName == "" { 170 cfg.Jaeger.ServiceName = defaultServiceName 171 } 172 173 for k, v := range tracerSpanTags { 174 cfg.Jaeger.Tags = append(cfg.Jaeger.Tags, opentracing.Tag{ 175 Key: k, 176 Value: v, 177 }) 178 } 179 180 tracer, jaegerCloser, err := cfg.Jaeger.NewTracer( 181 jaegercfg.Logger(jaegerzap.NewLogger(logger)), 182 jaegercfg.Metrics(jaegertally.Wrap(scope))) 183 if err != nil { 184 return nil, nil, fmt.Errorf("failed to initialize jaeger: %s", err.Error()) 185 } 186 187 return tracer, jaegerCloser, nil 188 } 189 190 func (cfg *TracingConfiguration) newLightstepTracer( 191 serviceName string, 192 ) (opentracing.Tracer, io.Closer, error) { 193 if cfg.Lightstep.Tags == nil { 194 cfg.Lightstep.Tags = opentracing.Tags{} 195 } 196 197 tags := cfg.Lightstep.Tags 198 if _, ok := tags[lightstep.ComponentNameKey]; !ok { 199 tags[lightstep.ComponentNameKey] = serviceName 200 } 201 202 for k, v := range tracerSpanTags { 203 tags[k] = v 204 } 205 206 tracer, err := lightstep.CreateTracer(cfg.Lightstep) 207 if err != nil { 208 return nil, nil, fmt.Errorf("failed to create lightstep tracer: %v", err) 209 } 210 211 closer := &lightstepCloser{tracer: tracer} 212 return tracer, closer, nil 213 } 214 215 type lightstepCloser struct { 216 tracer lightstep.Tracer 217 } 218 219 func (l *lightstepCloser) Close() error { 220 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 221 l.tracer.Close(ctx) 222 cancel() 223 return ctx.Err() 224 } 225 226 type noopCloser struct{} 227 228 func (noopCloser) Close() error { 229 return nil 230 }