github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/tracing/tracer.go (about) 1 package tracing 2 3 import ( 4 "context" 5 "io" 6 "os" 7 "strings" 8 9 instana "github.com/instana/go-sensor" 10 "github.com/uber/jaeger-client-go" 11 12 "github.com/opentracing/opentracing-go" 13 "github.com/pkg/errors" 14 15 "github.com/ory/x/logrusx" 16 17 zipkinOT "github.com/openzipkin-contrib/zipkin-go-opentracing" 18 "github.com/openzipkin/zipkin-go" 19 zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http" 20 21 jaegerConf "github.com/uber/jaeger-client-go/config" 22 jaegerZipkin "github.com/uber/jaeger-client-go/zipkin" 23 24 datadogOpentracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer" 25 datadogTracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" 26 27 "go.opentelemetry.io/otel" 28 otelOpentracing "go.opentelemetry.io/otel/bridge/opentracing" 29 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" 30 "go.opentelemetry.io/otel/sdk/resource" 31 otelSdkTrace "go.opentelemetry.io/otel/sdk/trace" 32 semconv "go.opentelemetry.io/otel/semconv/v1.7.0" 33 34 "go.elastic.co/apm" 35 "go.elastic.co/apm/module/apmot" 36 ) 37 38 // Tracer encapsulates tracing abilities. 39 type Tracer struct { 40 Config *Config 41 42 l *logrusx.Logger 43 tracer opentracing.Tracer 44 closer io.Closer 45 } 46 47 func New(l *logrusx.Logger, c *Config) (*Tracer, error) { 48 t := &Tracer{Config: c, l: l} 49 50 if err := t.setup(); err != nil { 51 return nil, err 52 } 53 54 return t, nil 55 } 56 57 // setup sets up the tracer. Currently supports jaeger. 58 func (t *Tracer) setup() error { 59 switch strings.ToLower(t.Config.Provider) { 60 case "jaeger": 61 jc, err := jaegerConf.FromEnv() 62 63 if err != nil { 64 return err 65 } 66 67 if t.Config.Providers.Jaeger.Sampling.ServerURL != "" { 68 jc.Sampler.SamplingServerURL = t.Config.Providers.Jaeger.Sampling.ServerURL 69 } 70 71 if t.Config.Providers.Jaeger.Sampling.Type != "" { 72 jc.Sampler.Type = t.Config.Providers.Jaeger.Sampling.Type 73 } 74 75 if t.Config.Providers.Jaeger.Sampling.Value != 0 { 76 jc.Sampler.Param = t.Config.Providers.Jaeger.Sampling.Value 77 } 78 79 if t.Config.Providers.Jaeger.LocalAgentAddress != "" { 80 jc.Reporter.LocalAgentHostPort = t.Config.Providers.Jaeger.LocalAgentAddress 81 } 82 83 var configs []jaegerConf.Option 84 85 if t.Config.Providers.Jaeger.MaxTagValueLength != jaeger.DefaultMaxTagValueLength { 86 configs = append(configs, jaegerConf.MaxTagValueLength(t.Config.Providers.Jaeger.MaxTagValueLength)) 87 } 88 89 // This works in other jaeger clients, but is not part of jaeger-client-go 90 if t.Config.Providers.Jaeger.Propagation == "b3" { 91 zipkinPropagator := jaegerZipkin.NewZipkinB3HTTPHeaderPropagator() 92 configs = append( 93 configs, 94 jaegerConf.Injector(opentracing.HTTPHeaders, zipkinPropagator), 95 jaegerConf.Extractor(opentracing.HTTPHeaders, zipkinPropagator), 96 ) 97 } 98 99 closer, err := jc.InitGlobalTracer( 100 t.Config.ServiceName, 101 configs..., 102 ) 103 104 if err != nil { 105 return err 106 } 107 108 t.closer = closer 109 t.tracer = opentracing.GlobalTracer() 110 t.l.Infof("Jaeger tracer configured!") 111 case "zipkin": 112 if t.Config.Providers.Zipkin.ServerURL == "" { 113 return errors.Errorf("Zipkin's server url is required") 114 } 115 116 reporter := zipkinHttp.NewReporter(t.Config.Providers.Zipkin.ServerURL) 117 118 endpoint, err := zipkin.NewEndpoint(t.Config.ServiceName, "") 119 120 if err != nil { 121 return err 122 } 123 124 nativeTracer, err := zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(endpoint)) 125 126 if err != nil { 127 return err 128 } 129 130 opentracing.SetGlobalTracer(zipkinOT.Wrap(nativeTracer)) 131 132 t.closer = reporter 133 t.tracer = opentracing.GlobalTracer() 134 t.l.Infof("Zipkin tracer configured!") 135 case "datadog": 136 var serviceName = os.Getenv("DD_SERVICE") 137 if serviceName == "" { 138 serviceName = t.Config.ServiceName 139 } 140 141 opentracing.SetGlobalTracer(datadogOpentracer.New(datadogTracer.WithService(serviceName))) 142 143 t.closer = datadogCloser{} 144 t.tracer = opentracing.GlobalTracer() 145 t.l.Infof("DataDog tracer configured!") 146 case "elastic-apm": 147 var serviceName = os.Getenv("ELASTIC_APM_SERVICE_NAME") 148 if serviceName == "" { 149 serviceName = t.Config.ServiceName 150 } 151 152 tr, err := apm.NewTracer(serviceName, "") 153 if err != nil { 154 return err 155 } 156 opentracing.SetGlobalTracer(apmot.New(apmot.WithTracer(tr))) 157 158 //t.closer = tr.Close 159 t.tracer = opentracing.GlobalTracer() 160 t.l.Infof("Elastic APM tracer configured!") 161 162 case "instana": 163 opts := instana.DefaultOptions() 164 var serviceName = os.Getenv("INSTANA_SERVICE_NAME") 165 if serviceName == "" { 166 serviceName = t.Config.ServiceName 167 } 168 opts.Service = serviceName 169 // all other settings can be configured using environment variables 170 171 t.tracer = instana.NewTracerWithOptions(opts) 172 opentracing.SetGlobalTracer(t.tracer) 173 174 t.l.Infof("Instana tracer configured!") 175 case "otel": 176 ctx := context.Background() 177 var serviceName = os.Getenv("OTEL_SERVICE_NAME") 178 if serviceName == "" { 179 serviceName = t.Config.ServiceName 180 } 181 182 res, err := resource.New(ctx, 183 resource.WithAttributes( 184 semconv.ServiceNameKey.String(serviceName), 185 ), 186 ) 187 if err != nil { 188 return errors.Wrap(err, "new otel resource") 189 } 190 191 exporter, err := otlptracehttp.New(ctx) 192 if err != nil { 193 return errors.Wrap(err, "new otel exporter") 194 } 195 196 tp := otelSdkTrace.NewTracerProvider( 197 otelSdkTrace.WithResource(res), 198 otelSdkTrace.WithSpanProcessor( 199 otelSdkTrace.NewSimpleSpanProcessor(exporter), 200 ), 201 ) 202 203 otel.SetTracerProvider(tp) 204 205 bridge := otelOpentracing.NewBridgeTracer() 206 bridge.SetOpenTelemetryTracer(otel.Tracer("")) 207 208 t.tracer = bridge 209 opentracing.SetGlobalTracer(t.tracer) 210 211 t.l.Infof("OTEL tracer configured!") 212 case "": 213 t.l.Infof("No tracer configured - skipping tracing setup") 214 default: 215 return errors.Errorf("unknown tracer: %s", t.Config.Provider) 216 } 217 return nil 218 } 219 220 // IsLoaded returns true if the tracer has been loaded. 221 func (t *Tracer) IsLoaded() bool { 222 if t == nil || t.tracer == nil { 223 return false 224 } 225 return true 226 } 227 228 // Tracer returns the wrapped tracer 229 func (t *Tracer) Tracer() opentracing.Tracer { 230 return t.tracer 231 } 232 233 // Close closes the tracer. 234 func (t *Tracer) Close() { 235 if t.closer != nil { 236 err := t.closer.Close() 237 if err != nil { 238 t.l.WithError(err).Error("Unable to close tracer.") 239 } 240 } 241 }