Skip to content

Commit 722e86a

Browse files
committed
Generate the external traceID correctly and exporter 3rd party logs with the external traceID as well
1 parent 07e3b01 commit 722e86a

File tree

8 files changed

+97
-11
lines changed

8 files changed

+97
-11
lines changed

packages/cli-v3/src/entryPoints/dev-run-worker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ async function bootstrap() {
175175
const tracer = new TriggerTracer({ tracer: otelTracer, logger: otelLogger });
176176
const consoleInterceptor = new ConsoleInterceptor(
177177
otelLogger,
178-
typeof config.enableConsoleLogging === "boolean" ? config.enableConsoleLogging : true
178+
typeof config.enableConsoleLogging === "boolean" ? config.enableConsoleLogging : true,
179+
typeof config.disableConsoleInterceptor === "boolean" ? config.disableConsoleInterceptor : false
179180
);
180181

181182
const configLogLevel = triggerLogLevel ?? config.logLevel ?? "info";

packages/cli-v3/src/entryPoints/managed-run-worker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ async function bootstrap() {
173173
const tracer = new TriggerTracer({ tracer: otelTracer, logger: otelLogger });
174174
const consoleInterceptor = new ConsoleInterceptor(
175175
otelLogger,
176-
typeof config.enableConsoleLogging === "boolean" ? config.enableConsoleLogging : true
176+
typeof config.enableConsoleLogging === "boolean" ? config.enableConsoleLogging : true,
177+
typeof config.disableConsoleInterceptor === "boolean" ? config.disableConsoleInterceptor : false
177178
);
178179

179180
const configLogLevel = triggerLogLevel ?? config.logLevel ?? "info";

packages/core/src/v3/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ export type TriggerConfig = {
139139
*/
140140
enableConsoleLogging?: boolean;
141141

142+
/**
143+
* Disable the console interceptor. This will prevent logs from being sent to the trigger.dev backend.
144+
*/
145+
disableConsoleInterceptor?: boolean;
146+
142147
build?: {
143148
/**
144149
* Add custom conditions to the esbuild build. For example, if you are importing `ai/rsc`, you'll need to add "react-server" condition.

packages/core/src/v3/consoleInterceptor.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ import { clock } from "./clock-api.js";
1010
export class ConsoleInterceptor {
1111
constructor(
1212
private readonly logger: logsAPI.Logger,
13-
private readonly sendToStdIO: boolean
13+
private readonly sendToStdIO: boolean,
14+
private readonly interceptingDisabled: boolean
1415
) {}
1516

1617
// Intercept the console and send logs to the OpenTelemetry logger
1718
// during the execution of the callback
1819
async intercept<T>(console: Console, callback: () => Promise<T>): Promise<T> {
20+
if (this.interceptingDisabled) {
21+
return await callback();
22+
}
23+
1924
// Save the original console methods
2025
const originalConsole = {
2126
log: console.log,

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

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DiagConsoleLogger, DiagLogLevel, TracerProvider, diag } from "@opentelemetry/api";
2+
import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base";
23
import { logs } from "@opentelemetry/api-logs";
34
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
45
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
@@ -16,6 +17,7 @@ import {
1617
BatchLogRecordProcessor,
1718
LoggerProvider,
1819
LogRecordExporter,
20+
ReadableLogRecord,
1921
SimpleLogRecordProcessor,
2022
} from "@opentelemetry/sdk-logs";
2123
import {
@@ -92,6 +94,8 @@ export type TracingSDKConfig = {
9294
diagLogLevel?: TracingDiagnosticLogLevel;
9395
};
9496

97+
const idGenerator = new RandomIdGenerator();
98+
9599
export class TracingSDK {
96100
public readonly asyncResourceDetector = new AsyncResourceDetector();
97101
private readonly _logProvider: LoggerProvider;
@@ -160,7 +164,7 @@ export class TracingSDK {
160164
)
161165
);
162166

163-
const externalTraceId = crypto.randomUUID();
167+
const externalTraceId = idGenerator.generateTraceId();
164168

165169
for (const exporter of config.exporters ?? []) {
166170
traceProvider.addSpanProcessor(
@@ -215,13 +219,22 @@ export class TracingSDK {
215219
for (const externalLogExporter of config.logExporters ?? []) {
216220
loggerProvider.addLogRecordProcessor(
217221
getEnvVar("OTEL_BATCH_PROCESSING_ENABLED") === "1"
218-
? new BatchLogRecordProcessor(externalLogExporter, {
219-
maxExportBatchSize: parseInt(getEnvVar("OTEL_LOG_MAX_EXPORT_BATCH_SIZE") ?? "64"),
220-
scheduledDelayMillis: parseInt(getEnvVar("OTEL_LOG_SCHEDULED_DELAY_MILLIS") ?? "200"),
221-
exportTimeoutMillis: parseInt(getEnvVar("OTEL_LOG_EXPORT_TIMEOUT_MILLIS") ?? "30000"),
222-
maxQueueSize: parseInt(getEnvVar("OTEL_LOG_MAX_QUEUE_SIZE") ?? "512"),
223-
})
224-
: new SimpleLogRecordProcessor(externalLogExporter)
222+
? new BatchLogRecordProcessor(
223+
new ExternalLogRecordExporterWrapper(externalLogExporter, externalTraceId),
224+
{
225+
maxExportBatchSize: parseInt(getEnvVar("OTEL_LOG_MAX_EXPORT_BATCH_SIZE") ?? "64"),
226+
scheduledDelayMillis: parseInt(
227+
getEnvVar("OTEL_LOG_SCHEDULED_DELAY_MILLIS") ?? "200"
228+
),
229+
exportTimeoutMillis: parseInt(
230+
getEnvVar("OTEL_LOG_EXPORT_TIMEOUT_MILLIS") ?? "30000"
231+
),
232+
maxQueueSize: parseInt(getEnvVar("OTEL_LOG_MAX_QUEUE_SIZE") ?? "512"),
233+
}
234+
)
235+
: new SimpleLogRecordProcessor(
236+
new ExternalLogRecordExporterWrapper(externalLogExporter, externalTraceId)
237+
)
225238
);
226239
}
227240

@@ -321,3 +334,50 @@ class ExternalSpanExporterWrapper {
321334
: Promise.resolve();
322335
}
323336
}
337+
338+
class ExternalLogRecordExporterWrapper {
339+
constructor(
340+
private underlyingExporter: LogRecordExporter,
341+
private externalTraceId: string
342+
) {}
343+
344+
export(logs: any[], resultCallback: (result: any) => void): void {
345+
const modifiedLogs = logs.map(this.transformLogRecord.bind(this));
346+
347+
this.underlyingExporter.export(modifiedLogs, resultCallback);
348+
}
349+
350+
shutdown(): Promise<void> {
351+
return this.underlyingExporter.shutdown();
352+
}
353+
354+
transformLogRecord(logRecord: ReadableLogRecord): ReadableLogRecord {
355+
// If there's no spanContext, or if the externalTraceId is not set, return the original logRecord.
356+
if (!logRecord.spanContext || !this.externalTraceId) {
357+
return logRecord;
358+
}
359+
360+
// Capture externalTraceId for use within the proxy's scope.
361+
const { externalTraceId } = this;
362+
363+
return new Proxy(logRecord, {
364+
get(target, prop, receiver) {
365+
if (prop === "spanContext") {
366+
// Intercept access to spanContext.
367+
const originalSpanContext = target.spanContext;
368+
// Ensure originalSpanContext exists (it should, due to the check above, but good for safety).
369+
if (originalSpanContext) {
370+
return {
371+
...originalSpanContext,
372+
traceId: externalTraceId, // Override traceId.
373+
};
374+
}
375+
// Fallback if, for some reason, originalSpanContext is undefined here.
376+
return undefined;
377+
}
378+
// For all other properties, defer to the original object.
379+
return Reflect.get(target, prop, receiver);
380+
},
381+
});
382+
}
383+
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

references/d3-chat/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@trigger.dev/react-hooks": "workspace:*",
2929
"@trigger.dev/sdk": "workspace:*",
3030
"@opentelemetry/exporter-logs-otlp-http": "0.52.1",
31+
"@opentelemetry/exporter-trace-otlp-http": "0.52.1",
3132
"@vercel/postgres": "^0.10.0",
3233
"ai": "4.2.5",
3334
"class-variance-authority": "^0.7.1",

references/d3-chat/trigger.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineConfig } from "@trigger.dev/sdk";
22
import { pythonExtension } from "@trigger.dev/python/extension";
33
import { installPlaywrightChromium } from "./src/extensions/playwright";
44
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
5+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
56

67
export default defineConfig({
78
project: "proj_cdmymsrobxmcgjqzhdkq",
@@ -16,6 +17,15 @@ export default defineConfig({
1617
},
1718
}),
1819
],
20+
exporters: [
21+
new OTLPTraceExporter({
22+
url: "https://api.axiom.co/v1/traces",
23+
headers: {
24+
Authorization: `Bearer ${process.env.AXIOM_TOKEN}`,
25+
"X-Axiom-Dataset": "d3-chat-tester",
26+
},
27+
}),
28+
],
1929
},
2030
maxDuration: 3600,
2131
build: {

0 commit comments

Comments
 (0)