From 8ace24cb394e9761bd40506d0d339141dbabf807 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 13 May 2026 16:53:54 -0700 Subject: [PATCH] fix(otel): address staging pr comments for trigger otel --- apps/sim/instrumentation-node.ts | 26 ++------------------------ apps/sim/lib/monitoring/otlp.ts | 27 +++++++++++++++++++++++++++ apps/sim/trigger.config.ts | 19 +------------------ 3 files changed, 30 insertions(+), 42 deletions(-) create mode 100644 apps/sim/lib/monitoring/otlp.ts diff --git a/apps/sim/instrumentation-node.ts b/apps/sim/instrumentation-node.ts index 54804e1955c..68ef65d0e6d 100644 --- a/apps/sim/instrumentation-node.ts +++ b/apps/sim/instrumentation-node.ts @@ -14,6 +14,7 @@ import type { import { createLogger } from '@sim/logger' import { TraceAttr } from '@/lib/copilot/generated/trace-attributes-v1' import { env } from './lib/core/config/env' +import { parseOtlpHeaders } from './lib/monitoring/otlp' diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ERROR) @@ -47,29 +48,6 @@ function isBusinessSpan(spanName: string): boolean { return ALLOWED_SPAN_PREFIXES.some((prefix) => spanName.startsWith(prefix)) } -// Parse `OTEL_EXPORTER_OTLP_HEADERS`: `key1=value1,key2=value2` -// (URL-encoded values, whitespace tolerated). -function parseOtlpHeadersEnv(raw: string): Record { - const out: Record = {} - if (!raw) return out - for (const part of raw.split(',')) { - const trimmed = part.trim() - if (!trimmed) continue - const eq = trimmed.indexOf('=') - if (eq <= 0) continue - const key = trimmed.slice(0, eq).trim() - const rawVal = trimmed.slice(eq + 1).trim() - let val = rawVal - try { - val = decodeURIComponent(rawVal) - } catch { - // value wasn't URL-encoded; keep as-is. - } - if (key) out[key] = val - } - return out -} - // Append `/v1/traces` to the OTLP base URL unless already present. // The HTTP exporter doesn't auto-suffix the signal path even though // the spec says the env var is a base URL. @@ -214,7 +192,7 @@ async function initializeOpenTelemetry() { }, }) - const otlpHeaders = parseOtlpHeadersEnv(process.env.OTEL_EXPORTER_OTLP_HEADERS || '') + const otlpHeaders = parseOtlpHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS || '') const exporterUrl = normalizeOtlpTracesUrl(telemetryConfig.endpoint) const exporter = new OTLPTraceExporter({ diff --git a/apps/sim/lib/monitoring/otlp.ts b/apps/sim/lib/monitoring/otlp.ts new file mode 100644 index 00000000000..bb21c0d4aab --- /dev/null +++ b/apps/sim/lib/monitoring/otlp.ts @@ -0,0 +1,27 @@ +/** + * Parse OTLP headers per the OTEL spec format `key1=value1,key2=value2`. + * Values are URL-decoded; the raw value is preserved if decoding fails. + * Keys and values are trimmed; empty or malformed entries are skipped. + * @see https://opentelemetry.io/docs/specs/otel/protocol/exporter/ + */ +export function parseOtlpHeaders(raw: string): Record { + const out: Record = {} + if (!raw) return out + for (const part of raw.split(',')) { + const trimmed = part.trim() + if (!trimmed) continue + const eq = trimmed.indexOf('=') + if (eq <= 0) continue + const key = trimmed.slice(0, eq).trim() + if (!key) continue + const rawVal = trimmed.slice(eq + 1).trim() + let val = rawVal + try { + val = decodeURIComponent(rawVal) + } catch { + // value wasn't URL-encoded; keep as-is. + } + out[key] = val + } + return out +} diff --git a/apps/sim/trigger.config.ts b/apps/sim/trigger.config.ts index 1439f5af2fd..3917f341d10 100644 --- a/apps/sim/trigger.config.ts +++ b/apps/sim/trigger.config.ts @@ -5,6 +5,7 @@ import { resourceFromAttributes } from '@opentelemetry/resources' import { additionalFiles, additionalPackages } from '@trigger.dev/build/extensions/core' import { defineConfig } from '@trigger.dev/sdk' import { env } from './lib/core/config/env' +import { parseOtlpHeaders } from './lib/monitoring/otlp' const grafanaEndpoint = env.GRAFANA_OTLP_ENDPOINT const grafanaHeaders = env.GRAFANA_OTLP_HEADERS @@ -20,24 +21,6 @@ if (grafanaConfigured && !grafanaFullyConfigured) { ) } -/** - * Parse OTLP headers per the OTEL spec format `key1=value1,key2=value2`. - * Values are URL-decoded; keys/values are trimmed; empty entries are skipped. - * @see https://opentelemetry.io/docs/specs/otel/protocol/exporter/ - */ -function parseOtlpHeaders(raw: string): Record { - const out: Record = {} - for (const pair of raw.split(',')) { - const eq = pair.indexOf('=') - if (eq === -1) continue - const key = pair.slice(0, eq).trim() - const value = pair.slice(eq + 1).trim() - if (!key) continue - out[key] = decodeURIComponent(value) - } - return out -} - const grafanaTelemetry = grafanaFullyConfigured ? (() => { const baseUrl = grafanaEndpoint!.replace(/\/+$/, '')