Skip to content

Commit a7a4bee

Browse files
committed
otelconf: add tracer provider configuration support
Signed-off-by: alex boten <[email protected]>
1 parent 6feb7c2 commit a7a4bee

File tree

3 files changed

+1398
-0
lines changed

3 files changed

+1398
-0
lines changed

otelconf/config_common.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"go.opentelemetry.io/otel/baggage"
1313
sdklog "go.opentelemetry.io/otel/sdk/log"
14+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
1415
)
1516

1617
const (
@@ -22,6 +23,7 @@ type configOptions struct {
2223
ctx context.Context
2324
opentelemetryConfig OpenTelemetryConfiguration
2425
loggerProviderOptions []sdklog.LoggerProviderOption
26+
tracerProviderOptions []sdktrace.TracerProviderOption
2527
}
2628

2729
type shutdownFunc func(context.Context) error

otelconf/trace.go

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package otelconf // import "go.opentelemetry.io/contrib/otelconf"
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
"net/url"
11+
"time"
12+
13+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
14+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
15+
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
16+
"go.opentelemetry.io/otel/sdk/resource"
17+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
18+
"go.opentelemetry.io/otel/trace"
19+
"go.opentelemetry.io/otel/trace/noop"
20+
"google.golang.org/grpc/credentials"
21+
22+
"go.opentelemetry.io/contrib/otelconf/internal/tls"
23+
)
24+
25+
var errInvalidSamplerConfiguration = newErrInvalid("sampler configuration")
26+
27+
func tracerProvider(cfg configOptions, res *resource.Resource) (trace.TracerProvider, shutdownFunc, error) {
28+
if cfg.opentelemetryConfig.TracerProvider == nil {
29+
return noop.NewTracerProvider(), noopShutdown, nil
30+
}
31+
provider, ok := cfg.opentelemetryConfig.TracerProvider.(*TracerProviderJson)
32+
if !ok {
33+
return noop.NewTracerProvider(), noopShutdown, newErrInvalid("invalid tracer provider")
34+
}
35+
36+
opts := append(cfg.tracerProviderOptions, sdktrace.WithResource(res))
37+
38+
var errs []error
39+
for _, processor := range provider.Processors {
40+
sp, err := spanProcessor(cfg.ctx, processor)
41+
if err == nil {
42+
opts = append(opts, sdktrace.WithSpanProcessor(sp))
43+
} else {
44+
errs = append(errs, err)
45+
}
46+
}
47+
if s, err := sampler(provider.Sampler); err == nil {
48+
opts = append(opts, sdktrace.WithSampler(s))
49+
} else {
50+
errs = append(errs, err)
51+
}
52+
if len(errs) > 0 {
53+
return noop.NewTracerProvider(), noopShutdown, errors.Join(errs...)
54+
}
55+
tp := sdktrace.NewTracerProvider(opts...)
56+
return tp, tp.Shutdown, nil
57+
}
58+
59+
func parentBasedSampler(s *ParentBasedSampler) (sdktrace.Sampler, error) {
60+
var rootSampler sdktrace.Sampler
61+
var opts []sdktrace.ParentBasedSamplerOption
62+
var errs []error
63+
var err error
64+
65+
if s.Root == nil {
66+
rootSampler = sdktrace.AlwaysSample()
67+
} else {
68+
rootSampler, err = sampler(s.Root)
69+
if err != nil {
70+
errs = append(errs, err)
71+
}
72+
}
73+
if s.RemoteParentSampled != nil {
74+
remoteParentSampler, err := sampler(s.RemoteParentSampled)
75+
if err != nil {
76+
errs = append(errs, err)
77+
} else {
78+
opts = append(opts, sdktrace.WithRemoteParentSampled(remoteParentSampler))
79+
}
80+
}
81+
if s.RemoteParentNotSampled != nil {
82+
remoteParentNotSampler, err := sampler(s.RemoteParentNotSampled)
83+
if err != nil {
84+
errs = append(errs, err)
85+
} else {
86+
opts = append(opts, sdktrace.WithRemoteParentNotSampled(remoteParentNotSampler))
87+
}
88+
}
89+
if s.LocalParentSampled != nil {
90+
localParentSampler, err := sampler(s.LocalParentSampled)
91+
if err != nil {
92+
errs = append(errs, err)
93+
} else {
94+
opts = append(opts, sdktrace.WithLocalParentSampled(localParentSampler))
95+
}
96+
}
97+
if s.LocalParentNotSampled != nil {
98+
localParentNotSampler, err := sampler(s.LocalParentNotSampled)
99+
if err != nil {
100+
errs = append(errs, err)
101+
} else {
102+
opts = append(opts, sdktrace.WithLocalParentNotSampled(localParentNotSampler))
103+
}
104+
}
105+
if len(errs) > 0 {
106+
return nil, errors.Join(errs...)
107+
}
108+
return sdktrace.ParentBased(rootSampler, opts...), nil
109+
}
110+
111+
func sampler(s *Sampler) (sdktrace.Sampler, error) {
112+
if s == nil {
113+
// If omitted, parent based sampler with a root of always_on is used.
114+
return sdktrace.ParentBased(sdktrace.AlwaysSample()), nil
115+
}
116+
if s.ParentBased != nil {
117+
return parentBasedSampler(s.ParentBased)
118+
}
119+
if s.AlwaysOff != nil {
120+
return sdktrace.NeverSample(), nil
121+
}
122+
if s.AlwaysOn != nil {
123+
return sdktrace.AlwaysSample(), nil
124+
}
125+
if s.TraceIDRatioBased != nil {
126+
if s.TraceIDRatioBased.Ratio == nil {
127+
return sdktrace.TraceIDRatioBased(1), nil
128+
}
129+
return sdktrace.TraceIDRatioBased(*s.TraceIDRatioBased.Ratio), nil
130+
}
131+
return nil, errInvalidSamplerConfiguration
132+
}
133+
134+
func spanExporter(ctx context.Context, exporter SpanExporter) (sdktrace.SpanExporter, error) {
135+
exportersConfigured := 0
136+
var exportFunc func() (sdktrace.SpanExporter, error)
137+
138+
if exporter.Console != nil {
139+
exportersConfigured++
140+
exportFunc = func() (sdktrace.SpanExporter, error) {
141+
return stdouttrace.New(
142+
stdouttrace.WithPrettyPrint(),
143+
)
144+
}
145+
}
146+
if exporter.OTLPHttp != nil {
147+
exportersConfigured++
148+
exportFunc = func() (sdktrace.SpanExporter, error) {
149+
return otlpHTTPSpanExporter(ctx, exporter.OTLPHttp)
150+
}
151+
}
152+
if exporter.OTLPGrpc != nil {
153+
exportersConfigured++
154+
exportFunc = func() (sdktrace.SpanExporter, error) {
155+
return otlpGRPCSpanExporter(ctx, exporter.OTLPGrpc)
156+
}
157+
}
158+
if exporter.OTLPFileDevelopment != nil {
159+
// TODO: implement file exporter https://github.com/open-telemetry/opentelemetry-go/issues/5408
160+
return nil, newErrInvalid("otlp_file/development")
161+
}
162+
if exporter.Zipkin != nil {
163+
// TODO: implement zipkin exporter
164+
return nil, newErrInvalid("zipkin")
165+
}
166+
167+
if exportersConfigured > 1 {
168+
return nil, newErrInvalid("must not specify multiple exporters")
169+
}
170+
171+
if exportFunc != nil {
172+
return exportFunc()
173+
}
174+
return nil, newErrInvalid("no valid span exporter")
175+
}
176+
177+
func spanProcessor(ctx context.Context, processor SpanProcessor) (sdktrace.SpanProcessor, error) {
178+
if processor.Batch != nil && processor.Simple != nil {
179+
return nil, newErrInvalid("must not specify multiple span processor type")
180+
}
181+
if processor.Batch != nil {
182+
exp, err := spanExporter(ctx, processor.Batch.Exporter)
183+
if err != nil {
184+
return nil, err
185+
}
186+
return batchSpanProcessor(processor.Batch, exp)
187+
}
188+
if processor.Simple != nil {
189+
exp, err := spanExporter(ctx, processor.Simple.Exporter)
190+
if err != nil {
191+
return nil, err
192+
}
193+
return sdktrace.NewSimpleSpanProcessor(exp), nil
194+
}
195+
return nil, newErrInvalid("unsupported span processor type, must be one of simple or batch")
196+
}
197+
198+
func otlpGRPCSpanExporter(ctx context.Context, otlpConfig *OTLPGrpcExporter) (sdktrace.SpanExporter, error) {
199+
var opts []otlptracegrpc.Option
200+
201+
if otlpConfig.Endpoint != nil {
202+
u, err := url.ParseRequestURI(*otlpConfig.Endpoint)
203+
if err != nil {
204+
return nil, errors.Join(newErrInvalid("endpoint parsing failed"), err)
205+
}
206+
// ParseRequestURI leaves the Host field empty when no
207+
// scheme is specified (i.e. localhost:4317). This check is
208+
// here to support the case where a user may not specify a
209+
// scheme. The code does its best effort here by using
210+
// otlpConfig.Endpoint as-is in that case.
211+
if u.Host != "" {
212+
opts = append(opts, otlptracegrpc.WithEndpoint(u.Host))
213+
} else {
214+
opts = append(opts, otlptracegrpc.WithEndpoint(*otlpConfig.Endpoint))
215+
}
216+
217+
if u.Scheme == "http" || (u.Scheme != "https" && otlpConfig.Insecure != nil && *otlpConfig.Insecure) {
218+
opts = append(opts, otlptracegrpc.WithInsecure())
219+
}
220+
}
221+
222+
if otlpConfig.Compression != nil {
223+
switch *otlpConfig.Compression {
224+
case compressionGzip:
225+
opts = append(opts, otlptracegrpc.WithCompressor(*otlpConfig.Compression))
226+
case compressionNone:
227+
// none requires no options
228+
default:
229+
return nil, newErrInvalid(fmt.Sprintf("unsupported compression %q", *otlpConfig.Compression))
230+
}
231+
}
232+
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
233+
opts = append(opts, otlptracegrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
234+
}
235+
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
236+
if err != nil {
237+
return nil, err
238+
}
239+
if len(headersConfig) > 0 {
240+
opts = append(opts, otlptracegrpc.WithHeaders(headersConfig))
241+
}
242+
243+
if otlpConfig.CertificateFile != nil || otlpConfig.ClientCertificateFile != nil || otlpConfig.ClientKeyFile != nil {
244+
tlsConfig, err := tls.CreateConfig(otlpConfig.CertificateFile, otlpConfig.ClientCertificateFile, otlpConfig.ClientKeyFile)
245+
if err != nil {
246+
return nil, errors.Join(newErrInvalid("tls configuration"), err)
247+
}
248+
opts = append(opts, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)))
249+
}
250+
251+
return otlptracegrpc.New(ctx, opts...)
252+
}
253+
254+
func otlpHTTPSpanExporter(ctx context.Context, otlpConfig *OTLPHttpExporter) (sdktrace.SpanExporter, error) {
255+
var opts []otlptracehttp.Option
256+
257+
if otlpConfig.Endpoint != nil {
258+
u, err := url.ParseRequestURI(*otlpConfig.Endpoint)
259+
if err != nil {
260+
return nil, errors.Join(newErrInvalid("endpoint parsing failed"), err)
261+
}
262+
opts = append(opts, otlptracehttp.WithEndpoint(u.Host))
263+
264+
if u.Scheme == "http" {
265+
opts = append(opts, otlptracehttp.WithInsecure())
266+
}
267+
if u.Path != "" {
268+
opts = append(opts, otlptracehttp.WithURLPath(u.Path))
269+
}
270+
}
271+
if otlpConfig.Compression != nil {
272+
switch *otlpConfig.Compression {
273+
case compressionGzip:
274+
opts = append(opts, otlptracehttp.WithCompression(otlptracehttp.GzipCompression))
275+
case compressionNone:
276+
opts = append(opts, otlptracehttp.WithCompression(otlptracehttp.NoCompression))
277+
default:
278+
return nil, newErrInvalid(fmt.Sprintf("unsupported compression %q", *otlpConfig.Compression))
279+
}
280+
}
281+
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
282+
opts = append(opts, otlptracehttp.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
283+
}
284+
headersConfig, err := createHeadersConfig(otlpConfig.Headers, otlpConfig.HeadersList)
285+
if err != nil {
286+
return nil, err
287+
}
288+
if len(headersConfig) > 0 {
289+
opts = append(opts, otlptracehttp.WithHeaders(headersConfig))
290+
}
291+
292+
tlsConfig, err := tls.CreateConfig(otlpConfig.CertificateFile, otlpConfig.ClientCertificateFile, otlpConfig.ClientKeyFile)
293+
if err != nil {
294+
return nil, errors.Join(newErrInvalid("tls configuration"), err)
295+
}
296+
opts = append(opts, otlptracehttp.WithTLSClientConfig(tlsConfig))
297+
298+
return otlptracehttp.New(ctx, opts...)
299+
}
300+
301+
func batchSpanProcessor(bsp *BatchSpanProcessor, exp sdktrace.SpanExporter) (sdktrace.SpanProcessor, error) {
302+
var opts []sdktrace.BatchSpanProcessorOption
303+
if err := validateBatchSpanProcessor(bsp); err != nil {
304+
return nil, err
305+
}
306+
if bsp.ExportTimeout != nil {
307+
opts = append(opts, sdktrace.WithExportTimeout(time.Millisecond*time.Duration(*bsp.ExportTimeout)))
308+
}
309+
if bsp.MaxExportBatchSize != nil {
310+
opts = append(opts, sdktrace.WithMaxExportBatchSize(*bsp.MaxExportBatchSize))
311+
}
312+
if bsp.MaxQueueSize != nil {
313+
opts = append(opts, sdktrace.WithMaxQueueSize(*bsp.MaxQueueSize))
314+
}
315+
if bsp.ScheduleDelay != nil {
316+
opts = append(opts, sdktrace.WithBatchTimeout(time.Millisecond*time.Duration(*bsp.ScheduleDelay)))
317+
}
318+
return sdktrace.NewBatchSpanProcessor(exp, opts...), nil
319+
}

0 commit comments

Comments
 (0)