github.com/OpsMx/go-app-base@v0.0.24/tracer/tracer.go (about) 1 // Copyright 2022 OpsMx, Inc 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tracer 16 17 import ( 18 "context" 19 "io" 20 "log" 21 "os" 22 "time" 23 24 "go.opentelemetry.io/otel" 25 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" 26 "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" 27 "go.opentelemetry.io/otel/propagation" 28 "go.opentelemetry.io/otel/sdk/resource" 29 "go.opentelemetry.io/otel/sdk/trace" 30 tracesdk "go.opentelemetry.io/otel/sdk/trace" 31 semconv "go.opentelemetry.io/otel/semconv/v1.9.0" 32 ) 33 34 // TracerProvider holds the state for a provider, and provides an 35 // easy way to shut it down without exposing the specific type 36 // of tracer returned. 37 type TracerProvider struct { 38 Provider *tracesdk.TracerProvider 39 } 40 41 // NewTracerProvider returns an OpenTelemetry TracerProvider configured to use 42 // the Jaeger exporter that will send spans to the provided url. The returned 43 // TracerProvider will also use a Resource configured with all the information 44 // about the application. 45 // 46 // If no error is returned, `defer provider.Shutdown(ctx)` should be set up 47 // to ensure flushing occurs. 48 // 49 // If the otlpEndpoint URL is empty, the OpenTelemetry 50 // TracerProvider will be configured to not report to an external tracer. 51 // 52 // If traceToStdout is true, traces will be sent to stdout. 53 func NewTracerProvider(ctx context.Context, otlpEndpoint string, traceToStdout bool, githash string, appname string, traceRatio float64) (*TracerProvider, error) { 54 res, err := resource.New(ctx, 55 // add detectors here if needed 56 resource.WithTelemetrySDK(), 57 resource.WithAttributes( 58 semconv.ServiceNameKey.String(appname), 59 semconv.ServiceVersionKey.String(githash), 60 )) 61 if err != nil { 62 log.Fatalf("resource.New: %v", err) 63 } 64 65 opts := []tracesdk.TracerProviderOption{ 66 tracesdk.WithResource(res), 67 tracesdk.WithSampler(tracesdk.ParentBased(tracesdk.TraceIDRatioBased(traceRatio))), 68 } 69 70 exporterCount := 0 71 if otlpEndpoint != "" { 72 exp, err := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint(otlpEndpoint)) 73 if err != nil { 74 return nil, err 75 } 76 opts = append(opts, tracesdk.WithBatcher(exp)) 77 exporterCount++ 78 } 79 80 if traceToStdout { 81 exp, err := newConsoleExporter(os.Stdout) 82 if err != nil { 83 return nil, err 84 } 85 opts = append(opts, tracesdk.WithBatcher(exp)) 86 exporterCount++ 87 } 88 89 if exporterCount == 0 { 90 exp, err := newNullExporter() 91 if err != nil { 92 return nil, err 93 } 94 opts = append(opts, tracesdk.WithBatcher(exp)) 95 } 96 97 tp := tracesdk.NewTracerProvider(opts...) 98 99 otel.SetTracerProvider(tp) 100 101 otel.SetTextMapPropagator(propagation.TraceContext{}) 102 return &TracerProvider{Provider: tp}, nil 103 } 104 105 // Shutdown should be deferred immediately after NewTracerProvider() 106 // when no error is returned. This will ensure that on app termination 107 // it will flush any buffered traces, if possible. A maximum time 108 // of 5 seconds will be allowed before we give up, to prevent a hang 109 // at shutdown. 110 func (p *TracerProvider) Shutdown(ctx context.Context) { 111 ctx, cancel := context.WithTimeout(ctx, time.Second*5) 112 defer cancel() 113 if err := p.Provider.Shutdown(ctx); err != nil { 114 log.Printf("shutting down tracing: %v", err) 115 } 116 } 117 118 func newConsoleExporter(w io.Writer) (trace.SpanExporter, error) { 119 return stdouttrace.New( 120 stdouttrace.WithWriter(w), 121 stdouttrace.WithPrettyPrint(), 122 ) 123 } 124 125 var _ trace.SpanExporter = &nullExporter{} 126 127 // dummy null exporter 128 func newNullExporter() (trace.SpanExporter, error) { 129 return nullExporter{}, nil 130 } 131 132 type nullExporter struct{} 133 134 func (nullExporter) ExportSpans(_ context.Context, _ []trace.ReadOnlySpan) error { 135 return nil 136 } 137 138 func (nullExporter) Shutdown(_ context.Context) error { 139 return nil 140 }