@@ -18,10 +18,10 @@ package plugin
18
18
19
19
import (
20
20
"context"
21
- "errors"
22
21
"fmt"
23
22
"io"
24
- "net/url"
23
+ "os"
24
+ "strconv"
25
25
"time"
26
26
27
27
"github.com/containerd/containerd/v2/pkg/deprecation"
@@ -37,13 +37,25 @@ import (
37
37
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
38
38
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
39
39
"go.opentelemetry.io/otel/propagation"
40
- "go.opentelemetry.io/otel/sdk/resource"
41
40
"go.opentelemetry.io/otel/sdk/trace"
42
- semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
43
41
)
44
42
45
43
const exporterPlugin = "otlp"
46
44
45
+ // OTEL and OTLP standard env vars
46
+ // See https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
47
+ const (
48
+ sdkDisabledEnv = "OTEL_SDK_DISABLED"
49
+
50
+ otlpEndpointEnv = "OTEL_EXPORTER_OTLP_ENDPOINT"
51
+ otlpTracesEndpointEnv = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
52
+ otlpProtocolEnv = "OTEL_EXPORTER_OTLP_PROTOCOL"
53
+ otlpTracesProtocolEnv = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
54
+
55
+ otelTracesExporterEnv = "OTEL_TRACES_EXPORTER"
56
+ otelServiceNameEnv = "OTEL_SERVICE_NAME"
57
+ )
58
+
47
59
func init () {
48
60
registry .Register (& plugin.Registration {
49
61
ID : exporterPlugin ,
@@ -53,38 +65,49 @@ func init() {
53
65
if err := warnOTLPConfig (ic ); err != nil {
54
66
return nil , err
55
67
}
56
- cfg := ic .Config .(* OTLPConfig )
57
- exp , err := newExporter (ic .Context , cfg )
68
+ if err := checkDisabled (); err != nil {
69
+ return nil , err
70
+ }
71
+
72
+ // If OTEL_TRACES_EXPORTER is set, it must be "otlp"
73
+ if v := os .Getenv (otelTracesExporterEnv ); v != "" && v != "otlp" {
74
+ return nil , fmt .Errorf ("unsupported traces exporter %q: %w" , v , errdefs .ErrInvalidArgument )
75
+ }
76
+
77
+ exp , err := newExporter (ic .Context )
58
78
if err != nil {
59
79
return nil , err
60
80
}
61
81
return trace .NewBatchSpanProcessor (exp ), nil
62
82
},
63
83
})
64
84
registry .Register (& plugin.Registration {
65
- ID : "tracing" ,
66
- Type : plugins .InternalPlugin ,
85
+ ID : "tracing" ,
86
+ Type : plugins .InternalPlugin ,
87
+ Config : & TraceConfig {},
67
88
Requires : []plugin.Type {
68
89
plugins .TracingProcessorPlugin ,
69
90
},
70
- Config : & TraceConfig {
71
- ServiceName : "containerd" ,
72
- TraceSamplingRatio : 1.0 ,
73
- },
74
91
InitFn : func (ic * plugin.InitContext ) (interface {}, error ) {
75
92
if err := warnTraceConfig (ic ); err != nil {
76
93
return nil , err
77
94
}
78
- // get TracingProcessorPlugin which is a dependency
95
+ if err := checkDisabled (); err != nil {
96
+ return nil , err
97
+ }
98
+
99
+ //get TracingProcessorPlugin which is a dependency
79
100
plugins , err := ic .GetByType (plugins .TracingProcessorPlugin )
80
101
if err != nil {
81
102
return nil , fmt .Errorf ("failed to get tracing processors: %w" , err )
82
103
}
104
+
83
105
procs := make ([]trace.SpanProcessor , 0 , len (plugins ))
84
106
for _ , p := range plugins {
85
107
procs = append (procs , p .(trace.SpanProcessor ))
86
108
}
87
- return newTracer (ic .Context , ic .Config .(* TraceConfig ), procs )
109
+
110
+ return newTracer (ic .Context , procs )
88
111
},
89
112
})
90
113
@@ -94,110 +117,89 @@ func init() {
94
117
95
118
// OTLPConfig holds the configurations for the built-in otlp span processor
96
119
type OTLPConfig struct {
97
- Endpoint string `toml:"endpoint"`
98
- Protocol string `toml:"protocol"`
99
- Insecure bool `toml:"insecure"`
120
+ Endpoint string `toml:"endpoint,omitempty "`
121
+ Protocol string `toml:"protocol,omitempty "`
122
+ Insecure bool `toml:"insecure,omitempty "`
100
123
}
101
124
102
125
// TraceConfig is the common configuration for open telemetry.
103
126
type TraceConfig struct {
104
- ServiceName string `toml:"service_name"`
105
- TraceSamplingRatio float64 `toml:"sampling_ratio"`
127
+ ServiceName string `toml:"service_name,omitempty "`
128
+ TraceSamplingRatio float64 `toml:"sampling_ratio,omitempty "`
106
129
}
107
130
108
- type closer struct {
109
- close func () error
131
+ func checkDisabled () error {
132
+ v := os .Getenv (sdkDisabledEnv )
133
+ if v != "" {
134
+ disable , err := strconv .ParseBool (v )
135
+ if err != nil {
136
+ return fmt .Errorf ("invalid value for %s: %w: %w" , sdkDisabledEnv , err , errdefs .ErrInvalidArgument )
137
+ }
138
+ if disable {
139
+ return fmt .Errorf ("%w: tracing disabled by env %s=%s" , plugin .ErrSkipPlugin , sdkDisabledEnv , v )
140
+ }
141
+ }
142
+
143
+ if os .Getenv (otlpEndpointEnv ) == "" && os .Getenv (otlpTracesEndpointEnv ) == "" {
144
+ return fmt .Errorf ("%w: tracing endpoint not configured" , plugin .ErrSkipPlugin )
145
+ }
146
+ return nil
110
147
}
111
148
112
- func (c * closer ) Close () error {
113
- return c .close ()
149
+ type closerFunc func () error
150
+
151
+ func (f closerFunc ) Close () error {
152
+ return f ()
114
153
}
115
154
116
155
// newExporter creates an exporter based on the given configuration.
117
156
//
118
157
// The default protocol is http/protobuf since it is recommended by
119
158
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/protocol/exporter.md#specify-protocol.
120
- func newExporter (ctx context.Context , cfg * OTLPConfig ) (* otlptrace.Exporter , error ) {
159
+ func newExporter (ctx context.Context ) (* otlptrace.Exporter , error ) {
121
160
const timeout = 5 * time .Second
122
161
162
+ v := os .Getenv (otlpTracesProtocolEnv )
163
+ if v == "" {
164
+ v = os .Getenv (otlpProtocolEnv )
165
+ }
166
+
123
167
ctx , cancel := context .WithTimeout (ctx , timeout )
124
168
defer cancel ()
125
-
126
- switch cfg .Protocol {
169
+ switch v {
127
170
case "" , "http/protobuf" :
128
- var opts []otlptracehttp.Option
129
- if cfg .Endpoint != "" {
130
- u , err := url .Parse (cfg .Endpoint )
131
- if err != nil {
132
- return nil , fmt .Errorf ("OpenTelemetry endpoint %q %w : %v" , cfg .Endpoint , errdefs .ErrInvalidArgument , err )
133
- }
134
- opts = append (opts , otlptracehttp .WithEndpoint (u .Host ))
135
- if u .Scheme == "http" {
136
- opts = append (opts , otlptracehttp .WithInsecure ())
137
- }
138
- }
139
- return otlptracehttp .New (ctx , opts ... )
171
+ return otlptracehttp .New (ctx )
140
172
case "grpc" :
141
- var opts []otlptracegrpc.Option
142
- if cfg .Endpoint != "" {
143
- opts = append (opts , otlptracegrpc .WithEndpoint (cfg .Endpoint ))
144
- }
145
- if cfg .Insecure {
146
- opts = append (opts , otlptracegrpc .WithInsecure ())
147
- }
148
- return otlptracegrpc .New (ctx , opts ... )
173
+ return otlptracegrpc .New (ctx )
149
174
default :
150
175
// Other protocols such as "http/json" are not supported.
151
- return nil , fmt .Errorf ("OpenTelemetry protocol %q : %w" , cfg . Protocol , errdefs .ErrNotImplemented )
176
+ return nil , fmt .Errorf ("OpenTelemetry protocol %q : %w" , v , errdefs .ErrNotImplemented )
152
177
}
153
178
}
154
179
155
180
// newTracer configures protocol-agonostic tracing settings such as
156
181
// its sampling ratio and returns io.Closer.
157
182
//
158
183
// Note that this function sets process-wide tracing configuration.
159
- func newTracer (ctx context.Context , config * TraceConfig , procs []trace.SpanProcessor ) (io.Closer , error ) {
160
- res , err := resource .New (ctx ,
161
- resource .WithHost (),
162
- resource .WithAttributes (
163
- // Service name used to displace traces in backends
164
- semconv .ServiceNameKey .String (config .ServiceName ),
165
- ),
166
- )
167
- if err != nil {
168
- return nil , fmt .Errorf ("failed to create resource: %w" , err )
184
+ func newTracer (ctx context.Context , procs []trace.SpanProcessor ) (io.Closer , error ) {
185
+ // Let otel configure the service name from env
186
+ if os .Getenv (otelServiceNameEnv ) == "" {
187
+ os .Setenv (otelServiceNameEnv , "containerd" )
169
188
}
170
189
171
- sampler := trace .ParentBased (trace .TraceIDRatioBased (config .TraceSamplingRatio ))
172
-
173
- opts := []trace.TracerProviderOption {
174
- trace .WithSampler (sampler ),
175
- trace .WithResource (res ),
176
- }
190
+ otel .SetTextMapPropagator (propagation .NewCompositeTextMapPropagator (propagation.TraceContext {}, propagation.Baggage {}))
177
191
192
+ opts := make ([]trace.TracerProviderOption , 0 , len (procs ))
178
193
for _ , proc := range procs {
179
194
opts = append (opts , trace .WithSpanProcessor (proc ))
180
195
}
181
-
182
196
provider := trace .NewTracerProvider (opts ... )
183
-
184
197
otel .SetTracerProvider (provider )
185
198
186
- otel .SetTextMapPropagator (propagators ())
187
-
188
- return & closer {close : func () error {
189
- for _ , p := range procs {
190
- if err := p .Shutdown (ctx ); err != nil && ! errors .Is (err , context .Canceled ) {
191
- return err
192
- }
193
- }
194
- return nil
195
- }}, nil
196
- }
199
+ return closerFunc (func () error {
200
+ return provider .Shutdown (ctx )
201
+ }), nil
197
202
198
- // Returns a composite TestMap propagator
199
- func propagators () propagation.TextMapPropagator {
200
- return propagation .NewCompositeTextMapPropagator (propagation.TraceContext {}, propagation.Baggage {})
201
203
}
202
204
203
205
func warnTraceConfig (ic * plugin.InitContext ) error {
0 commit comments