Skip to content

Commit 5fdad0f

Browse files
fix(telemetry): disable OTLP exporters for empty endpoints (cherry-pick #16898 for 4.1) (#16930)
Signed-off-by: Eric Wyles <23637493+ericwyles@users.noreply.github.com> Co-authored-by: Eric Wyles <23637493+ericwyles@users.noreply.github.com>
1 parent d2cbf6b commit 5fdad0f

5 files changed

Lines changed: 92 additions & 17 deletions

File tree

‎docs/telemetry-configuration.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ To enable the OpenTelemetry protocol you must set the environment variable `OTEL
1616
This must be set on the workflow-controller, and ideally on all the containers (`main`, `init`, and `wait`) in the workload.
1717
You can use the [OpenTelemetry operator](https://opentelemetry.io/docs/kubernetes/operator/) to setup the collector and instrument the workflow-controller.
1818
The OpenTelemetry operator can also instrument your workload pods so that they emit spans as part of workflow tracing and this is the recommended setup.
19-
It will not be enabled if left blank, unlike some other implementations.
19+
It will not be enabled unless `OTEL_EXPORTER_OTLP_ENDPOINT` or the applicable signal-specific endpoint is configured and non-empty.
2020

2121
You can configure the protocol using the environment variables documented in the [OpenTelemetry standard environment variables](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/).
2222

‎util/telemetry/metrics.go‎

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,13 @@ func (m *Metrics) Shutdown(ctx context.Context) error {
6060
return nil
6161
}
6262

63+
// NewMetrics creates a metrics provider configured with the enabled OTLP and Prometheus exporters.
6364
func NewMetrics(ctx context.Context, serviceName, prometheusName string, config *MetricsConfig, extraOpts ...metricsdk.Option) (*Metrics, error) {
6465
options := make([]metricsdk.Option, 0)
6566
options = append(options, metricsdk.WithResource(workflowsResource(ctx, serviceName)))
66-
_, otlpEnabled := os.LookupEnv(`OTEL_EXPORTER_OTLP_ENDPOINT`)
67-
_, otlpMetricsEnabled := os.LookupEnv(`OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`)
6867
logger := logging.RequireLoggerFromContext(ctx)
6968

70-
if otlpEnabled || otlpMetricsEnabled {
69+
if endpoint := resolveOTLPEndpoint(otlpMetricsEndpointEnv); endpoint != "" {
7170
// NOTE: The OTel SDK default changed from gRPC to http/protobuf. For backwards compatibility,
7271
// gRPC is preserved as the default in workflows controller, but http/protobuf can be opted-in
7372
// to by setting the _PROTOCOL env var explicitly.
@@ -76,11 +75,6 @@ func NewMetrics(ctx context.Context, serviceName, prometheusName string, config
7675
if otlpProtocol == "" {
7776
otlpProtocol = os.Getenv(`OTEL_EXPORTER_OTLP_PROTOCOL`)
7877
}
79-
endpoint := os.Getenv(`OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`)
80-
if endpoint == "" {
81-
endpoint = os.Getenv(`OTEL_EXPORTER_OTLP_ENDPOINT`)
82-
}
83-
8478
switch {
8579
case otlpProtocol == "" || otlpProtocol == "grpc":
8680
logger.WithFields(logging.Fields{"protocol": "grpc", "endpoint": endpoint}).Info(ctx, "Starting OTLP metrics exporter")

‎util/telemetry/otlp.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package telemetry
2+
3+
import (
4+
"os"
5+
"strings"
6+
)
7+
8+
const (
9+
otlpEndpointEnv = "OTEL_EXPORTER_OTLP_ENDPOINT"
10+
otlpMetricsEndpointEnv = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"
11+
otlpTracesEndpointEnv = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
12+
)
13+
14+
// resolveOTLPEndpoint returns the signal-specific endpoint, falling back to the common OTLP endpoint.
15+
func resolveOTLPEndpoint(signalEndpointEnv string) string {
16+
if endpoint := strings.TrimSpace(os.Getenv(signalEndpointEnv)); endpoint != "" {
17+
return endpoint
18+
}
19+
return strings.TrimSpace(os.Getenv(otlpEndpointEnv))
20+
}

‎util/telemetry/otlp_test.go‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package telemetry
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
// TestResolveOTLPEndpoint verifies endpoint precedence and handling of empty values.
10+
func TestResolveOTLPEndpoint(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
commonEndpoint string
14+
signalSpecificEndpoint string
15+
expectedEndpoint string
16+
}{
17+
{
18+
name: "empty values",
19+
},
20+
{
21+
name: "common endpoint",
22+
commonEndpoint: "common:4317",
23+
expectedEndpoint: "common:4317",
24+
},
25+
{
26+
name: "signal-specific endpoint",
27+
signalSpecificEndpoint: "signal:4317",
28+
expectedEndpoint: "signal:4317",
29+
},
30+
{
31+
name: "signal-specific endpoint takes precedence",
32+
commonEndpoint: "common:4317",
33+
signalSpecificEndpoint: "signal:4317",
34+
expectedEndpoint: "signal:4317",
35+
},
36+
{
37+
name: "whitespace-only values",
38+
commonEndpoint: " ",
39+
signalSpecificEndpoint: "\t",
40+
},
41+
{
42+
name: "endpoints with surrounding whitespace",
43+
commonEndpoint: " common:4317 ",
44+
signalSpecificEndpoint: " signal:4317 ",
45+
expectedEndpoint: "signal:4317",
46+
},
47+
{
48+
name: "whitespace-only signal-specific endpoint falls back to common endpoint",
49+
commonEndpoint: "common:4317",
50+
signalSpecificEndpoint: " ",
51+
expectedEndpoint: "common:4317",
52+
},
53+
}
54+
55+
for _, signalEndpointEnv := range []string{otlpMetricsEndpointEnv, otlpTracesEndpointEnv} {
56+
t.Run(signalEndpointEnv, func(t *testing.T) {
57+
for _, test := range tests {
58+
t.Run(test.name, func(t *testing.T) {
59+
t.Setenv(otlpEndpointEnv, test.commonEndpoint)
60+
t.Setenv(signalEndpointEnv, test.signalSpecificEndpoint)
61+
62+
require.Equal(t, test.expectedEndpoint, resolveOTLPEndpoint(signalEndpointEnv))
63+
})
64+
}
65+
})
66+
}
67+
}

‎util/telemetry/tracing.go‎

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,13 @@ type Tracing struct {
8686
tracer trace.Tracer
8787
}
8888

89+
// NewTracing creates a tracing provider configured with the enabled OTLP exporter.
8990
func NewTracing(ctx context.Context, serviceName string, extraOpts ...tracesdk.TracerProviderOption) (*Tracing, error) {
9091
options := make([]tracesdk.TracerProviderOption, 0)
9192
options = append(options, tracesdk.WithResource(workflowsResource(ctx, serviceName)))
9293
options = append(options, extraOpts...)
93-
_, otlpEnabled := os.LookupEnv(`OTEL_EXPORTER_OTLP_ENDPOINT`)
94-
_, otlpTracingEnabled := os.LookupEnv(`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`)
9594

96-
if otlpEnabled || otlpTracingEnabled {
95+
if endpoint := resolveOTLPEndpoint(otlpTracesEndpointEnv); endpoint != "" {
9796
// NOTE: The OTel SDK default changed from gRPC to http/protobuf. For alignment with metrics
9897
// gRPC is preserved as the default in workflows controller, but http/protobuf can be opted-in
9998
// to by setting the _PROTOCOL env var explicitly.
@@ -104,11 +103,6 @@ func NewTracing(ctx context.Context, serviceName string, extraOpts ...tracesdk.T
104103
}
105104

106105
logger := logging.RequireLoggerFromContext(ctx)
107-
endpoint := os.Getenv(`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`)
108-
if endpoint == "" {
109-
endpoint = os.Getenv(`OTEL_EXPORTER_OTLP_ENDPOINT`)
110-
}
111-
112106
switch {
113107
case otlpProtocol == "" || otlpProtocol == "grpc":
114108
logger.WithFields(logging.Fields{"protocol": "grpc", "endpoint": endpoint}).Info(ctx, "Starting OTLP tracing exporter")

0 commit comments

Comments
 (0)