From f453c51b2328344f262146d86154ccf150484ae1 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Thu, 24 Aug 2023 09:03:33 -0700 Subject: [PATCH] add OTLP export support for internal traces (#8119) This PR adds support for exporting internal collector traces via the OTel Go OTLP exporters. Closes https://github.com/open-telemetry/opentelemetry-collector/issues/8106 ~Follows https://github.com/open-telemetry/opentelemetry-collector/pull/8117~ --------- Signed-off-by: Alex Boten Co-authored-by: Anthony Mirabella --- .../codeboten_add-otlp-trace-exporter.yaml | 16 ++ cmd/otelcorecol/go.mod | 3 + cmd/otelcorecol/go.sum | 8 +- go.mod | 3 + go.sum | 8 +- service/internal/proctelemetry/config.go | 153 ++++++++-- service/internal/proctelemetry/config_test.go | 264 +++++++++++++++++- service/telemetry/config.go | 39 ++- service/telemetry/config_test.go | 8 + 9 files changed, 461 insertions(+), 41 deletions(-) create mode 100755 .chloggen/codeboten_add-otlp-trace-exporter.yaml diff --git a/.chloggen/codeboten_add-otlp-trace-exporter.yaml b/.chloggen/codeboten_add-otlp-trace-exporter.yaml new file mode 100755 index 00000000000..f3b754d4997 --- /dev/null +++ b/.chloggen/codeboten_add-otlp-trace-exporter.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: service + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add OTLP export for internal traces + +# One or more tracking issues or pull requests related to the change +issues: [8106] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 2c68dff7849..b6c03888243 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -99,6 +99,9 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.39.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 // indirect diff --git a/cmd/otelcorecol/go.sum b/cmd/otelcorecol/go.sum index 9c9f62ebf79..d65a2677cdf 100644 --- a/cmd/otelcorecol/go.sum +++ b/cmd/otelcorecol/go.sum @@ -936,6 +936,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0 h1:rm+ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0/go.mod h1:sWFbI3jJ+6JdjOVepA5blpv/TJ20Hw+26561iMbWcwU= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.39.0 h1:IZXpCEtI7BbX01DRQEWTGDkvjMB6hEhiEZXS+eg2YqY= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.39.0/go.mod h1:xY111jIZtWb+pUUgT4UiiSonAaY2cD2Ts5zvuKLki3o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 h1:iqjq9LAB8aK++sKVcELezzn655JnBNdsDhghU4G/So8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0/go.mod h1:hGXzO5bhhSHZnKvrDaXB82Y9DRFour0Nz/KrBh7reWw= go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.39.0 h1:fl2WmyenEf6LYYlfHAtCUEDyGcpwJNqD4dHGO7PVm4w= @@ -954,7 +960,7 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= diff --git a/go.mod b/go.mod index c4582e106f6..7b506775e2e 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,8 @@ require ( go.opentelemetry.io/otel/bridge/opencensus v0.39.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.39.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 go.opentelemetry.io/otel/exporters/prometheus v0.39.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.39.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 @@ -84,6 +86,7 @@ require ( go.opentelemetry.io/contrib/zpages v0.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect golang.org/x/exp v0.0.0-20230711023510-fffb14384f22 // indirect golang.org/x/net v0.14.0 // indirect diff --git a/go.sum b/go.sum index e9a4e943efb..b0fffcc03da 100644 --- a/go.sum +++ b/go.sum @@ -332,6 +332,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0 h1:rm+ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0/go.mod h1:sWFbI3jJ+6JdjOVepA5blpv/TJ20Hw+26561iMbWcwU= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.39.0 h1:IZXpCEtI7BbX01DRQEWTGDkvjMB6hEhiEZXS+eg2YqY= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.39.0/go.mod h1:xY111jIZtWb+pUUgT4UiiSonAaY2cD2Ts5zvuKLki3o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 h1:iqjq9LAB8aK++sKVcELezzn655JnBNdsDhghU4G/So8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0/go.mod h1:hGXzO5bhhSHZnKvrDaXB82Y9DRFour0Nz/KrBh7reWw= go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.39.0 h1:fl2WmyenEf6LYYlfHAtCUEDyGcpwJNqD4dHGO7PVm4w= @@ -349,7 +355,7 @@ go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLk go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= diff --git a/service/internal/proctelemetry/config.go b/service/internal/proctelemetry/config.go index ee20660e296..67d3122f980 100644 --- a/service/internal/proctelemetry/config.go +++ b/service/internal/proctelemetry/config.go @@ -20,6 +20,8 @@ import ( "go.opentelemetry.io/otel/bridge/opencensus" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" otelprom "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" @@ -83,7 +85,7 @@ func InitMetricReader(ctx context.Context, reader telemetry.MetricReader, asyncE return nil, nil, fmt.Errorf("unsupported metric reader type %v", reader) } -func InitSpanProcessor(_ context.Context, processor telemetry.SpanProcessor) (sdktrace.SpanProcessor, error) { +func InitSpanProcessor(ctx context.Context, processor telemetry.SpanProcessor) (sdktrace.SpanProcessor, error) { if processor.Batch != nil { if processor.Batch.Exporter.Console != nil { exp, err := stdouttrace.New( @@ -92,32 +94,23 @@ func InitSpanProcessor(_ context.Context, processor telemetry.SpanProcessor) (sd if err != nil { return nil, err } - opts := []sdktrace.BatchSpanProcessorOption{} - if processor.Batch.ExportTimeout != nil { - if *processor.Batch.ExportTimeout < 0 { - return nil, fmt.Errorf("invalid export timeout %d", *processor.Batch.ExportTimeout) - } - opts = append(opts, sdktrace.WithExportTimeout(time.Millisecond*time.Duration(*processor.Batch.ExportTimeout))) - } - if processor.Batch.MaxExportBatchSize != nil { - if *processor.Batch.MaxExportBatchSize < 0 { - return nil, fmt.Errorf("invalid batch size %d", *processor.Batch.MaxExportBatchSize) - } - opts = append(opts, sdktrace.WithMaxExportBatchSize(*processor.Batch.MaxExportBatchSize)) - } - if processor.Batch.MaxQueueSize != nil { - if *processor.Batch.MaxQueueSize < 0 { - return nil, fmt.Errorf("invalid queue size %d", *processor.Batch.MaxQueueSize) - } - opts = append(opts, sdktrace.WithMaxQueueSize(*processor.Batch.MaxQueueSize)) + return initBatchSpanProcessor(processor.Batch, exp) + } + if processor.Batch.Exporter.Otlp != nil { + var err error + var exp sdktrace.SpanExporter + switch processor.Batch.Exporter.Otlp.Protocol { + case protocolProtobufHTTP: + exp, err = initOTLPHTTPSpanExporter(ctx, processor.Batch.Exporter.Otlp) + case protocolProtobufGRPC: + exp, err = initOTLPgRPCSpanExporter(ctx, processor.Batch.Exporter.Otlp) + default: + return nil, fmt.Errorf("unsupported protocol %q", processor.Batch.Exporter.Otlp.Protocol) } - if processor.Batch.ScheduleDelay != nil { - if *processor.Batch.ScheduleDelay < 0 { - return nil, fmt.Errorf("invalid schedule delay %d", *processor.Batch.ScheduleDelay) - } - opts = append(opts, sdktrace.WithBatchTimeout(time.Millisecond*time.Duration(*processor.Batch.ScheduleDelay))) + if err != nil { + return nil, err } - return sdktrace.NewBatchSpanProcessor(exp, opts...), nil + return initBatchSpanProcessor(processor.Batch, exp) } return nil, errNoValidSpanExporter } @@ -287,7 +280,14 @@ func initOTLPgRPCExporter(ctx context.Context, otlpConfig *telemetry.OtlpMetric) } if otlpConfig.Compression != nil { - opts = append(opts, otlpmetricgrpc.WithCompressor(*otlpConfig.Compression)) + switch *otlpConfig.Compression { + case "gzip": + opts = append(opts, otlpmetricgrpc.WithCompressor(*otlpConfig.Compression)) + case "none": + break + default: + return nil, fmt.Errorf("unsupported compression %q", *otlpConfig.Compression) + } } if otlpConfig.Timeout != nil { opts = append(opts, otlpmetricgrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout))) @@ -335,3 +335,104 @@ func initOTLPHTTPExporter(ctx context.Context, otlpConfig *telemetry.OtlpMetric) return otlpmetrichttp.New(ctx, opts...) } + +func initOTLPgRPCSpanExporter(ctx context.Context, otlpConfig *telemetry.Otlp) (sdktrace.SpanExporter, error) { + opts := []otlptracegrpc.Option{} + + if len(otlpConfig.Endpoint) > 0 { + u, err := url.ParseRequestURI(normalizeEndpoint(otlpConfig.Endpoint)) + if err != nil { + return nil, err + } + opts = append(opts, otlptracegrpc.WithEndpoint(u.Host)) + if u.Scheme == "http" { + opts = append(opts, otlptracegrpc.WithInsecure()) + } + } + + if otlpConfig.Compression != nil { + switch *otlpConfig.Compression { + case "gzip": + opts = append(opts, otlptracegrpc.WithCompressor(*otlpConfig.Compression)) + case "none": + break + default: + return nil, fmt.Errorf("unsupported compression %q", *otlpConfig.Compression) + } + } + if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 { + opts = append(opts, otlptracegrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout))) + } + if len(otlpConfig.Headers) > 0 { + opts = append(opts, otlptracegrpc.WithHeaders(otlpConfig.Headers)) + } + + return otlptracegrpc.New(ctx, opts...) +} + +func initOTLPHTTPSpanExporter(ctx context.Context, otlpConfig *telemetry.Otlp) (sdktrace.SpanExporter, error) { + opts := []otlptracehttp.Option{} + + if len(otlpConfig.Endpoint) > 0 { + u, err := url.ParseRequestURI(normalizeEndpoint(otlpConfig.Endpoint)) + if err != nil { + return nil, err + } + opts = append(opts, otlptracehttp.WithEndpoint(u.Host)) + + if u.Scheme == "http" { + opts = append(opts, otlptracehttp.WithInsecure()) + } + if len(u.Path) > 0 { + opts = append(opts, otlptracehttp.WithURLPath(u.Path)) + } + } + if otlpConfig.Compression != nil { + switch *otlpConfig.Compression { + case "gzip": + opts = append(opts, otlptracehttp.WithCompression(otlptracehttp.GzipCompression)) + case "none": + opts = append(opts, otlptracehttp.WithCompression(otlptracehttp.NoCompression)) + default: + return nil, fmt.Errorf("unsupported compression %q", *otlpConfig.Compression) + } + } + if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 { + opts = append(opts, otlptracehttp.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout))) + } + if len(otlpConfig.Headers) > 0 { + opts = append(opts, otlptracehttp.WithHeaders(otlpConfig.Headers)) + } + + return otlptracehttp.New(ctx, opts...) +} + +func initBatchSpanProcessor(bsp *telemetry.BatchSpanProcessor, exp sdktrace.SpanExporter) (sdktrace.SpanProcessor, error) { + opts := []sdktrace.BatchSpanProcessorOption{} + if bsp.ExportTimeout != nil { + if *bsp.ExportTimeout < 0 { + return nil, fmt.Errorf("invalid export timeout %d", *bsp.ExportTimeout) + } + opts = append(opts, sdktrace.WithExportTimeout(time.Millisecond*time.Duration(*bsp.ExportTimeout))) + } + if bsp.MaxExportBatchSize != nil { + if *bsp.MaxExportBatchSize < 0 { + return nil, fmt.Errorf("invalid batch size %d", *bsp.MaxExportBatchSize) + } + opts = append(opts, sdktrace.WithMaxExportBatchSize(*bsp.MaxExportBatchSize)) + } + if bsp.MaxQueueSize != nil { + if *bsp.MaxQueueSize < 0 { + return nil, fmt.Errorf("invalid queue size %d", *bsp.MaxQueueSize) + } + opts = append(opts, sdktrace.WithMaxQueueSize(*bsp.MaxQueueSize)) + } + if bsp.ScheduleDelay != nil { + if *bsp.ScheduleDelay < 0 { + return nil, fmt.Errorf("invalid schedule delay %d", *bsp.ScheduleDelay) + } + opts = append(opts, sdktrace.WithBatchTimeout(time.Millisecond*time.Duration(*bsp.ScheduleDelay))) + } + return sdktrace.NewBatchSpanProcessor(exp, opts...), nil + +} diff --git a/service/internal/proctelemetry/config_test.go b/service/internal/proctelemetry/config_test.go index 1a1fec4e4b6..cb09754e52c 100644 --- a/service/internal/proctelemetry/config_test.go +++ b/service/internal/proctelemetry/config_test.go @@ -162,7 +162,7 @@ func TestMetricReader(t *testing.T) { Otlp: &telemetry.OtlpMetric{ Protocol: "grpc/protobuf", Endpoint: "http://localhost:4317", - Compression: strPtr("gzip"), + Compression: strPtr("none"), Timeout: intPtr(1000), Headers: map[string]string{ "test": "test1", @@ -226,6 +226,7 @@ func TestMetricReader(t *testing.T) { }, }, }, + err: errors.New("unsupported compression \"invalid\""), }, { name: "periodic/otlp-http-exporter", @@ -427,6 +428,267 @@ func TestSpanProcessor(t *testing.T) { }, }, }, + { + name: "batch/otlp-exporter-invalid-protocol", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: *strPtr("http/invalid"), + }, + }, + }, + }, + err: errors.New("unsupported protocol \"http/invalid\""), + }, + { + name: "batch/otlp-grpc-exporter-no-endpoint", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "grpc/protobuf", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-grpc-exporter", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "grpc/protobuf", + Endpoint: "http://localhost:4317", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-grpc-exporter-no-scheme", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "grpc/protobuf", + Endpoint: "localhost:4317", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-grpc-invalid-endpoint", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "grpc/protobuf", + Endpoint: " ", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + err: &url.Error{Op: "parse", URL: "http:// ", Err: url.InvalidHostError(" ")}, + }, + { + name: "batch/otlp-grpc-invalid-compression", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "grpc/protobuf", + Endpoint: "localhost:4317", + Compression: strPtr("invalid"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + err: errors.New("unsupported compression \"invalid\""), + }, + { + name: "batch/otlp-http-exporter", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "http/protobuf", + Endpoint: "http://localhost:4318", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-http-exporter-with-path", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "http/protobuf", + Endpoint: "http://localhost:4318/path/123", + Compression: strPtr("none"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-http-exporter-no-endpoint", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "http/protobuf", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-http-exporter-no-scheme", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "http/protobuf", + Endpoint: "localhost:4318", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + }, + { + name: "batch/otlp-http-invalid-endpoint", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "http/protobuf", + Endpoint: " ", + Compression: strPtr("gzip"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + err: &url.Error{Op: "parse", URL: "http:// ", Err: url.InvalidHostError(" ")}, + }, + { + name: "batch/otlp-http-invalid-compression", + processor: telemetry.SpanProcessor{ + Batch: &telemetry.BatchSpanProcessor{ + MaxExportBatchSize: intPtr(0), + ExportTimeout: intPtr(0), + MaxQueueSize: intPtr(0), + ScheduleDelay: intPtr(0), + Exporter: telemetry.SpanExporter{ + Otlp: &telemetry.Otlp{ + Protocol: "http/protobuf", + Endpoint: "localhost:4318", + Compression: strPtr("invalid"), + Timeout: intPtr(1000), + Headers: map[string]string{ + "test": "test1", + }, + }, + }, + }, + }, + err: errors.New("unsupported compression \"invalid\""), + }, } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { diff --git a/service/telemetry/config.go b/service/telemetry/config.go index ac91a6494b0..514ae03278a 100644 --- a/service/telemetry/config.go +++ b/service/telemetry/config.go @@ -150,14 +150,19 @@ func (sp *SpanProcessor) Unmarshal(conf *confmap.Conf) error { } if sp.Batch != nil { - if sp.Batch.Exporter.Console == nil { - return fmt.Errorf("invalid exporter configuration") - } - return nil + return sp.Batch.Exporter.Validate() } return fmt.Errorf("unsupported span processor type %s", conf.AllKeys()) } +// Validate checks for valid exporters to be configured for the SpanExporter +func (se *SpanExporter) Validate() error { + if se.Console == nil && se.Otlp == nil { + return fmt.Errorf("invalid exporter configuration") + } + return nil +} + func (mr *MetricReader) Unmarshal(conf *confmap.Conf) error { if !obsreportconfig.UseOtelWithSDKConfigurationForInternalTelemetryFeatureGate.IsEnabled() { // only unmarshal if feature gate is enabled @@ -173,17 +178,27 @@ func (mr *MetricReader) Unmarshal(conf *confmap.Conf) error { } if mr.Pull != nil { - if mr.Pull.Exporter.Prometheus == nil { - return fmt.Errorf("invalid exporter configuration") - } - return nil + return mr.Pull.Validate() } if mr.Periodic != nil { - if mr.Periodic.Exporter.Otlp == nil && mr.Periodic.Exporter.Console == nil { - return fmt.Errorf("invalid exporter configuration") - } - return nil + return mr.Periodic.Validate() } return fmt.Errorf("unsupported metric reader type %s", conf.AllKeys()) } + +// Validate checks for valid exporters to be configured for the PullMetricReader +func (pmr *PullMetricReader) Validate() error { + if pmr.Exporter.Prometheus == nil { + return fmt.Errorf("invalid exporter configuration") + } + return nil +} + +// Validate checks for valid exporters to be configured for the PeriodicMetricReader +func (pmr *PeriodicMetricReader) Validate() error { + if pmr.Exporter.Otlp == nil && pmr.Exporter.Console == nil { + return fmt.Errorf("invalid exporter configuration") + } + return nil +} diff --git a/service/telemetry/config_test.go b/service/telemetry/config_test.go index a2412a5d17b..8af1616fb92 100644 --- a/service/telemetry/config_test.go +++ b/service/telemetry/config_test.go @@ -220,6 +220,14 @@ func TestUnmarshalSpanProcessor(t *testing.T) { }, }}), }, + { + name: "valid batch processor type, valid otlp exporter", + cfg: confmap.NewFromStringMap(map[string]any{"batch": BatchSpanProcessor{ + Exporter: SpanExporter{ + Otlp: &Otlp{}, + }, + }}), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {