From 92747567f35785b142600d6a0096db71234baa5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:14:01 +0000 Subject: [PATCH 1/3] Initial plan From 89a29f010a2042767a66e243512d166d367ba7e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:30:34 +0000 Subject: [PATCH 2/3] fix: respect user-defined OTEL_SERVICE_NAME in observability config Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/observability_otlp.go | 9 ++++++++- pkg/workflow/observability_otlp_test.go | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go index e2ff2fd52f1..58ba83ca6d3 100644 --- a/pkg/workflow/observability_otlp.go +++ b/pkg/workflow/observability_otlp.go @@ -732,7 +732,14 @@ func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { // OTEL_EXPORTER_OTLP_ENDPOINT is set to the first endpoint for backward // compatibility (MCP gateway, legacy scripts). OTEL_SERVICE_NAME is // workflow-specific when WorkflowID is available. - otlpEnvLines := fmt.Sprintf(" OTEL_EXPORTER_OTLP_ENDPOINT: %s\n OTEL_SERVICE_NAME: %s", firstEndpoint, serviceName) + // If the user has already defined OTEL_SERVICE_NAME in their env block, + // we respect their value and skip injection to avoid duplicate key errors. + otlpEnvLines := " OTEL_EXPORTER_OTLP_ENDPOINT: " + firstEndpoint + if strings.Contains(workflowData.Env, "OTEL_SERVICE_NAME:") { + otlpLog.Printf("Skipping OTEL_SERVICE_NAME injection: already defined by user") + } else { + otlpEnvLines += "\n OTEL_SERVICE_NAME: " + serviceName + } otlpEnvLines += "\n OTEL_RESOURCE_ATTRIBUTES: '" + escapeYAMLSingleQuoted(otelResourceAttributes(workflowData)) + "'" // 3. Inject per-endpoint headers env vars. diff --git a/pkg/workflow/observability_otlp_test.go b/pkg/workflow/observability_otlp_test.go index 3b91aa08c24..40cf59b0c67 100644 --- a/pkg/workflow/observability_otlp_test.go +++ b/pkg/workflow/observability_otlp_test.go @@ -585,6 +585,23 @@ func TestInjectOTLPConfig(t *testing.T) { assert.Equal(t, 1, strings.Count(wd.Env, "env:"), "should have exactly one env: key") }) + t.Run("preserves user-defined OTEL_SERVICE_NAME and does not inject duplicate", func(t *testing.T) { + c := newCompiler() + wd := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + Observability: &ObservabilityConfig{ + OTLP: &OTLPConfig{Endpoint: "https://traces.example.com"}, + }, + }, + Env: "env:\n OTEL_SERVICE_NAME: my-service", + } + c.injectOTLPConfig(wd) + + assert.Contains(t, wd.Env, "OTEL_SERVICE_NAME: my-service", "user-defined service name should be preserved") + assert.Equal(t, 1, strings.Count(wd.Env, "OTEL_SERVICE_NAME:"), "OTEL_SERVICE_NAME should appear exactly once") + assert.Contains(t, wd.Env, "OTEL_EXPORTER_OTLP_ENDPOINT: https://traces.example.com", "endpoint should still be injected") + }) + t.Run("OTEL_SERVICE_NAME includes sanitized workflow ID when available", func(t *testing.T) { c := newCompiler() wd := &WorkflowData{ From 7938847de414c47f6f0ed1e50392e87161942b92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:38:55 +0000 Subject: [PATCH 3/3] fix: use regex to detect user-defined OTEL_SERVICE_NAME more robustly Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/observability_otlp.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/observability_otlp.go b/pkg/workflow/observability_otlp.go index 58ba83ca6d3..91d93f5050a 100644 --- a/pkg/workflow/observability_otlp.go +++ b/pkg/workflow/observability_otlp.go @@ -17,6 +17,7 @@ var otlpLog = logger.New("workflow:observability_otlp") var sentryEndpointExpressionPattern = regexp.MustCompile(`(?i)^\$\{\{\s*secrets\.` + regexp.QuoteMeta(constants.OTELSentryEndpointSecretName) + `\s*\}\}$`) var otlpResourceAttributeSecretRefPattern = regexp.MustCompile(`\$\{\{\s*(secrets|vars)\.`) +var otelServiceNameKeyPattern = regexp.MustCompile(`(?m)^\s*OTEL_SERVICE_NAME:`) func normalizeOTLPHeadersForEndpoint(raw any, endpoint string) string { if raw == nil { @@ -735,7 +736,7 @@ func (c *Compiler) injectOTLPConfig(workflowData *WorkflowData) { // If the user has already defined OTEL_SERVICE_NAME in their env block, // we respect their value and skip injection to avoid duplicate key errors. otlpEnvLines := " OTEL_EXPORTER_OTLP_ENDPOINT: " + firstEndpoint - if strings.Contains(workflowData.Env, "OTEL_SERVICE_NAME:") { + if otelServiceNameKeyPattern.MatchString(workflowData.Env) { otlpLog.Printf("Skipping OTEL_SERVICE_NAME injection: already defined by user") } else { otlpEnvLines += "\n OTEL_SERVICE_NAME: " + serviceName