diff --git a/commons/opentelemetry/otel.go b/commons/opentelemetry/otel.go index c8083d7..a183470 100644 --- a/commons/opentelemetry/otel.go +++ b/commons/opentelemetry/otel.go @@ -98,6 +98,22 @@ func NewTelemetry(cfg TelemetryConfig) (*Telemetry, error) { cfg.Redactor = NewDefaultRedactor() } + // Normalize endpoint: strip URL scheme and infer security mode. + // gRPC WithEndpoint() expects host:port, not a full URL. + // Consumers commonly pass OTEL_EXPORTER_OTLP_ENDPOINT as "http://host:4317". + if ep := strings.TrimSpace(cfg.CollectorExporterEndpoint); ep != "" { + switch { + case strings.HasPrefix(ep, "http://"): + cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "http://") + cfg.InsecureExporter = true + case strings.HasPrefix(ep, "https://"): + cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "https://") + default: + // No scheme — assume insecure (common in k8s internal comms). + cfg.InsecureExporter = true + } + } + if cfg.EnableTelemetry && strings.TrimSpace(cfg.CollectorExporterEndpoint) == "" { return nil, ErrEmptyEndpoint } diff --git a/commons/opentelemetry/otel_test.go b/commons/opentelemetry/otel_test.go index 94e6e60..94c6fa7 100644 --- a/commons/opentelemetry/otel_test.go +++ b/commons/opentelemetry/otel_test.go @@ -92,6 +92,75 @@ func TestNewTelemetry_DefaultPropagatorAndRedactor(t *testing.T) { assert.NotNil(t, tl.Redactor, "default redactor should be set") } +// =========================================================================== +// 1b. Endpoint normalization +// =========================================================================== + +func TestNewTelemetry_EndpointNormalization(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + endpoint string + wantEndpoint string + wantInsecure bool + insecureOverride bool // initial InsecureExporter value + }{ + { + name: "http scheme stripped and insecure inferred", + endpoint: "http://otel-collector:4317", + wantEndpoint: "otel-collector:4317", + wantInsecure: true, + }, + { + name: "https scheme stripped and insecure stays false", + endpoint: "https://otel-collector:4317", + wantEndpoint: "otel-collector:4317", + wantInsecure: false, + }, + { + name: "no scheme defaults to insecure", + endpoint: "otel-collector:4317", + wantEndpoint: "otel-collector:4317", + wantInsecure: true, + }, + { + name: "https with explicit insecure override preserved", + endpoint: "https://otel-collector:4317", + insecureOverride: true, + wantEndpoint: "otel-collector:4317", + wantInsecure: true, + }, + { + name: "http with trailing slash", + endpoint: "http://otel-collector:4317/", + wantEndpoint: "otel-collector:4317/", + wantInsecure: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + // Use telemetry disabled so we don't need a real collector. + tl, err := NewTelemetry(TelemetryConfig{ + LibraryName: "test-lib", + EnableTelemetry: false, + CollectorExporterEndpoint: tt.endpoint, + InsecureExporter: tt.insecureOverride, + Logger: log.NewNop(), + }) + require.NoError(t, err) + require.NotNil(t, tl) + assert.Equal(t, tt.wantEndpoint, tl.CollectorExporterEndpoint, + "endpoint should be normalized") + assert.Equal(t, tt.wantInsecure, tl.InsecureExporter, + "InsecureExporter should be inferred from scheme") + }) + } +} + // =========================================================================== // 2. Telemetry methods on nil receiver // ===========================================================================