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.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
fix(otel): exported logs and spans will now have matching trace IDs
|
||||
@@ -314,7 +314,9 @@ class ExternalSpanExporterWrapper {
|
||||
| { traceId: string; spanId: string; traceFlags: number; tracestate?: string }
|
||||
| undefined
|
||||
) {
|
||||
this._isExternallySampled = isTraceFlagSampled(externalTraceContext?.traceFlags);
|
||||
this._isExternallySampled = externalTraceContext
|
||||
? isTraceFlagSampled(externalTraceContext.traceFlags)
|
||||
: !!externalTraceId;
|
||||
}
|
||||
|
||||
private transformSpan(span: ReadableSpan): ReadableSpan | undefined {
|
||||
@@ -396,7 +398,9 @@ class ExternalLogRecordExporterWrapper {
|
||||
| { traceId: string; spanId: string; tracestate?: string; traceFlags: number }
|
||||
| undefined
|
||||
) {
|
||||
this._isExternallySampled = isTraceFlagSampled(externalTraceContext?.traceFlags);
|
||||
this._isExternallySampled = externalTraceContext
|
||||
? isTraceFlagSampled(externalTraceContext.traceFlags)
|
||||
: !!externalTraceId;
|
||||
}
|
||||
|
||||
export(logs: any[], resultCallback: (result: any) => void): void {
|
||||
@@ -416,16 +420,17 @@ class ExternalLogRecordExporterWrapper {
|
||||
}
|
||||
|
||||
transformLogRecord(logRecord: ReadableLogRecord): ReadableLogRecord {
|
||||
// If there's no spanContext, or if the externalTraceId is not set, return the original logRecord.
|
||||
if (!logRecord.spanContext || !this.externalTraceId || !this.externalTraceContext) {
|
||||
return logRecord;
|
||||
}
|
||||
|
||||
// Capture externalTraceId for use within the proxy's scope.
|
||||
// Use externalTraceContext.traceId if available, otherwise fall back to generated externalTraceId
|
||||
const externalTraceId = this.externalTraceContext
|
||||
? this.externalTraceContext.traceId
|
||||
: this.externalTraceId;
|
||||
|
||||
// If there's no spanContext, or if the externalTraceId is not set, return the original logRecord.
|
||||
if (!logRecord.spanContext || !externalTraceId) {
|
||||
return logRecord;
|
||||
}
|
||||
|
||||
return new Proxy(logRecord, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop === "spanContext") {
|
||||
|
||||
Reference in New Issue
Block a user