Skip to content

Commit 9f27422

Browse files
authored
fix(otel): exported logs and spans will now have matching trace IDs (#2724)
When using custom OTLP exporters via `telemetry.exporters` and This occurred when tasks were triggered **without** a parent trace context (e.g., via API or dashboard). In this scenario: - Spans were correctly rewritten to use the generated `externalTraceId` - Logs kept their original internal trace ID due to a bug in the early return logic ### Root Cause In `ExternalLogRecordExporterWrapper.transformLogRecord()`, the early return condition incorrectly included `!this.externalTraceContext`: ```typescript if (!logRecord.spanContext || !this.externalTraceId || !this.externalTraceContext) { return logRecord; // Bug: Returns early when externalTraceContext is undefined } // This fallback logic was never reached: const externalTraceId = this.externalTraceContext ? this.externalTraceContext.traceId : this.externalTraceId; ``` ### Fix 1. **Reordered logic in `transformLogRecord()`**: Move the 1. `externalTraceId` calculation before the early return, and check the 1. culated value instead of `this.externalTraceContext`: ```typescript const externalTraceId = this.externalTraceContext ? this.externalTraceContext.traceId : this.externalTraceId; if (!logRecord.spanContext || !externalTraceId) { return logRecord; } ``` 2. **Clarified `_isExternallySampled` logic**: Updated both 2. `ExternalSpanExporterWrapper` and `ExternalLogRecordExporterWrapper` 2. explicitly handle the case where there's no external trace context 2. a generated `externalTraceId` exists: ```typescript this._isExternallySampled = externalTraceContext ? isTraceFlagSampled(externalTraceContext.traceFlags) : !!externalTraceId; ``` ### Impact Logs and spans from the same task run will now have matching trace IDs when exported to external observability tools, enabling proper trace correlation regardless of whether the task was triggered with or without a parent trace context. `telemetry.logExporters` in `trigger.config.ts`, logs and spans were exported with **different trace IDs**, breaking trace correlation in external observability tools like Datadog.
1 parent 2f1a72b commit 9f27422

File tree

2 files changed

+17
-7
lines changed

2 files changed

+17
-7
lines changed

.changeset/brown-pots-beg.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
fix(otel): exported logs and spans will now have matching trace IDs

packages/core/src/v3/otel/tracingSDK.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,9 @@ class ExternalSpanExporterWrapper {
314314
| { traceId: string; spanId: string; traceFlags: number; tracestate?: string }
315315
| undefined
316316
) {
317-
this._isExternallySampled = isTraceFlagSampled(externalTraceContext?.traceFlags);
317+
this._isExternallySampled = externalTraceContext
318+
? isTraceFlagSampled(externalTraceContext.traceFlags)
319+
: !!externalTraceId;
318320
}
319321

320322
private transformSpan(span: ReadableSpan): ReadableSpan | undefined {
@@ -396,7 +398,9 @@ class ExternalLogRecordExporterWrapper {
396398
| { traceId: string; spanId: string; tracestate?: string; traceFlags: number }
397399
| undefined
398400
) {
399-
this._isExternallySampled = isTraceFlagSampled(externalTraceContext?.traceFlags);
401+
this._isExternallySampled = externalTraceContext
402+
? isTraceFlagSampled(externalTraceContext.traceFlags)
403+
: !!externalTraceId;
400404
}
401405

402406
export(logs: any[], resultCallback: (result: any) => void): void {
@@ -416,16 +420,17 @@ class ExternalLogRecordExporterWrapper {
416420
}
417421

418422
transformLogRecord(logRecord: ReadableLogRecord): ReadableLogRecord {
419-
// If there's no spanContext, or if the externalTraceId is not set, return the original logRecord.
420-
if (!logRecord.spanContext || !this.externalTraceId || !this.externalTraceContext) {
421-
return logRecord;
422-
}
423-
424423
// Capture externalTraceId for use within the proxy's scope.
424+
// Use externalTraceContext.traceId if available, otherwise fall back to generated externalTraceId
425425
const externalTraceId = this.externalTraceContext
426426
? this.externalTraceContext.traceId
427427
: this.externalTraceId;
428428

429+
// If there's no spanContext, or if the externalTraceId is not set, return the original logRecord.
430+
if (!logRecord.spanContext || !externalTraceId) {
431+
return logRecord;
432+
}
433+
429434
return new Proxy(logRecord, {
430435
get(target, prop, receiver) {
431436
if (prop === "spanContext") {

0 commit comments

Comments
 (0)