Skip to content

Commit 9ea7211

Browse files
committed
Add ability to "passthrough" the external trace back out into external systems
1 parent 272dfb6 commit 9ea7211

File tree

20 files changed

+303
-75
lines changed

20 files changed

+303
-75
lines changed

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
waitUntil,
3131
WorkerManifest,
3232
WorkerToExecutorMessageCatalog,
33+
traceContext,
3334
} from "@trigger.dev/core/v3";
3435
import { TriggerTracer } from "@trigger.dev/core/v3/tracer";
3536
import {
@@ -53,6 +54,7 @@ import {
5354
TracingSDK,
5455
usage,
5556
UsageTimeoutManager,
57+
StandardTraceContextManager,
5658
} from "@trigger.dev/core/v3/workers";
5759
import { ZodIpcConnection } from "@trigger.dev/core/v3/zodIpc";
5860
import { readFile } from "node:fs/promises";
@@ -127,6 +129,9 @@ timeout.setGlobalManager(usageTimeoutManager);
127129
const standardResourceCatalog = new StandardResourceCatalog();
128130
resourceCatalog.setGlobalResourceCatalog(standardResourceCatalog);
129131

132+
const standardTraceContextManager = new StandardTraceContextManager();
133+
traceContext.setGlobalManager(standardTraceContextManager);
134+
130135
const durableClock = new DurableClock();
131136
clock.setGlobalClock(durableClock);
132137
const runMetadataManager = new StandardMetadataManager(
@@ -165,7 +170,7 @@ async function loadWorkerManifest() {
165170
return WorkerManifest.parse(raw);
166171
}
167172

168-
async function doBootstrap(traceContext: Record<string, unknown>) {
173+
async function doBootstrap() {
169174
return await runTimelineMetrics.measureMetric("trigger.dev/start", "bootstrap", {}, async () => {
170175
log("Bootstrapping worker");
171176

@@ -182,7 +187,6 @@ async function doBootstrap(traceContext: Record<string, unknown>) {
182187
logExporters: config.telemetry?.logExporters ?? [],
183188
diagLogLevel: (env.TRIGGER_OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none",
184189
forceFlushTimeoutMillis: 30_000,
185-
externalTraceContext: traceContext.external,
186190
});
187191

188192
const otelTracer: Tracer = tracingSDK.getTracer("trigger-dev-worker", VERSION);
@@ -266,9 +270,9 @@ let bootstrapCache:
266270
}
267271
| undefined;
268272

269-
async function bootstrap(traceContext: Record<string, unknown>) {
273+
async function bootstrap() {
270274
if (!bootstrapCache) {
271-
bootstrapCache = await doBootstrap(traceContext);
275+
bootstrapCache = await doBootstrap();
272276
}
273277

274278
return bootstrapCache;
@@ -303,6 +307,7 @@ function resetExecutionEnvironment() {
303307
_sharedWorkerRuntime?.reset();
304308
durableClock.reset();
305309
taskContext.disable();
310+
standardTraceContextManager.reset();
306311

307312
log(`[${new Date().toISOString()}] Reset execution environment`);
308313
}
@@ -339,6 +344,7 @@ const zodIpc = new ZodIpcConnection({
339344

340345
resetExecutionEnvironment();
341346

347+
standardTraceContextManager.traceContext = traceContext;
342348
standardRunTimelineMetricsManager.registerMetricsFromExecution(metrics, isWarmStart);
343349

344350
if (_isRunning) {
@@ -372,9 +378,8 @@ const zodIpc = new ZodIpcConnection({
372378
});
373379

374380
try {
375-
const { tracer, tracingSDK, consoleInterceptor, config, workerManifest } = await bootstrap(
376-
traceContext
377-
);
381+
const { tracer, tracingSDK, consoleInterceptor, config, workerManifest } =
382+
await bootstrap();
378383

379384
_tracingSDK = tracingSDK;
380385

@@ -527,7 +532,7 @@ const zodIpc = new ZodIpcConnection({
527532

528533
const signal = AbortSignal.any([_cancelController.signal, timeoutController.signal]);
529534

530-
const { result } = await executor.execute(execution, ctx, traceContext, signal);
535+
const { result } = await executor.execute(execution, ctx, signal);
531536

532537
if (_isRunning && !_isCancelled) {
533538
const usageSample = usage.stop(_executionMeasurement);

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
waitUntil,
3030
WorkerManifest,
3131
WorkerToExecutorMessageCatalog,
32+
traceContext,
3233
} from "@trigger.dev/core/v3";
3334
import { TriggerTracer } from "@trigger.dev/core/v3/tracer";
3435
import {
@@ -53,6 +54,7 @@ import {
5354
TracingSDK,
5455
usage,
5556
UsageTimeoutManager,
57+
StandardTraceContextManager,
5658
} from "@trigger.dev/core/v3/workers";
5759
import { ZodIpcConnection } from "@trigger.dev/core/v3/zodIpc";
5860
import { readFile } from "node:fs/promises";
@@ -120,6 +122,9 @@ resourceCatalog.setGlobalResourceCatalog(standardResourceCatalog);
120122
const durableClock = new DurableClock();
121123
clock.setGlobalClock(durableClock);
122124

125+
const standardTraceContextManager = new StandardTraceContextManager();
126+
traceContext.setGlobalManager(standardTraceContextManager);
127+
123128
const runMetadataManager = new StandardMetadataManager(
124129
apiClientManager.clientOrThrow(),
125130
getEnvVar("TRIGGER_STREAM_URL", getEnvVar("TRIGGER_API_URL")) ?? "https://api.trigger.dev"
@@ -156,7 +161,7 @@ async function loadWorkerManifest() {
156161
return WorkerManifest.parse(raw);
157162
}
158163

159-
async function doBootstrap(traceContext: Record<string, unknown>) {
164+
async function doBootstrap() {
160165
return await runTimelineMetrics.measureMetric("trigger.dev/start", "bootstrap", {}, async () => {
161166
const workerManifest = await loadWorkerManifest();
162167

@@ -173,7 +178,6 @@ async function doBootstrap(traceContext: Record<string, unknown>) {
173178
forceFlushTimeoutMillis: 30_000,
174179
exporters: config.telemetry?.exporters ?? [],
175180
logExporters: config.telemetry?.logExporters ?? [],
176-
externalTraceContext: traceContext.external,
177181
});
178182

179183
const otelTracer: Tracer = tracingSDK.getTracer("trigger-dev-worker", VERSION);
@@ -255,9 +259,9 @@ let bootstrapCache:
255259
}
256260
| undefined;
257261

258-
async function bootstrap(traceContext: Record<string, unknown>) {
262+
async function bootstrap() {
259263
if (!bootstrapCache) {
260-
bootstrapCache = await doBootstrap(traceContext);
264+
bootstrapCache = await doBootstrap();
261265
}
262266

263267
return bootstrapCache;
@@ -289,6 +293,7 @@ function resetExecutionEnvironment() {
289293
_sharedWorkerRuntime?.reset();
290294
durableClock.reset();
291295
taskContext.disable();
296+
standardTraceContextManager.reset();
292297

293298
console.log(`[${new Date().toISOString()}] Reset execution environment`);
294299
}
@@ -330,6 +335,8 @@ const zodIpc = new ZodIpcConnection({
330335

331336
resetExecutionEnvironment();
332337

338+
standardTraceContextManager.traceContext = traceContext;
339+
333340
const prodManager = initializeUsageManager({
334341
usageIntervalMs: getEnvVar("USAGE_HEARTBEAT_INTERVAL_MS"),
335342
usageEventUrl: getEnvVar("USAGE_EVENT_URL"),
@@ -376,9 +383,8 @@ const zodIpc = new ZodIpcConnection({
376383
});
377384

378385
try {
379-
const { tracer, tracingSDK, consoleInterceptor, config, workerManifest } = await bootstrap(
380-
traceContext
381-
);
386+
const { tracer, tracingSDK, consoleInterceptor, config, workerManifest } =
387+
await bootstrap();
382388

383389
_tracingSDK = tracingSDK;
384390

@@ -525,7 +531,7 @@ const zodIpc = new ZodIpcConnection({
525531

526532
const signal = AbortSignal.any([_cancelController.signal, timeoutController.signal]);
527533

528-
const { result } = await executor.execute(execution, ctx, traceContext, signal);
534+
const { result } = await executor.execute(execution, ctx, signal);
529535

530536
if (_isRunning && !_isCancelled) {
531537
const usageSample = usage.stop(_executionMeasurement);

packages/core/src/v3/apiClient/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { RetryOptions } from "../schemas/index.js";
44
import { calculateNextRetryDelay } from "../utils/retries.js";
55
import { ApiConnectionError, ApiError, ApiSchemaValidationError } from "./errors.js";
66

7-
import { Attributes, context, propagation, Span } from "@opentelemetry/api";
7+
import { Attributes, context, propagation, Span, trace } from "@opentelemetry/api";
88
import { suppressTracing } from "@opentelemetry/core";
99
import { SemanticInternalAttributes } from "../semanticInternalAttributes.js";
1010
import type { TriggerTracer } from "../tracer.js";

packages/core/src/v3/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from "./limits.js";
99
export * from "./logger-api.js";
1010
export * from "./runtime-api.js";
1111
export * from "./task-context-api.js";
12+
export * from "./trace-context-api.js";
1213
export * from "./apiClientManager-api.js";
1314
export * from "./usage-api.js";
1415
export * from "./run-metadata-api.js";

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

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import {
2-
DiagConsoleLogger,
3-
DiagLogLevel,
4-
SpanContext,
5-
TracerProvider,
6-
diag,
7-
} from "@opentelemetry/api";
1+
import { DiagConsoleLogger, DiagLogLevel, TracerProvider, diag } from "@opentelemetry/api";
82
import { logs } from "@opentelemetry/api-logs";
3+
import { TraceState } from "@opentelemetry/core";
94
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
105
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
116
import { registerInstrumentations, type Instrumentation } from "@opentelemetry/instrumentation";
@@ -39,13 +34,13 @@ import {
3934
OTEL_SPAN_EVENT_COUNT_LIMIT,
4035
} from "../limits.js";
4136
import { SemanticInternalAttributes } from "../semanticInternalAttributes.js";
37+
import { taskContext } from "../task-context-api.js";
4238
import {
4339
TaskContextLogProcessor,
4440
TaskContextSpanProcessor,
4541
} from "../taskContext/otelProcessors.js";
42+
import { traceContext } from "../trace-context-api.js";
4643
import { getEnvVar } from "../utils/getEnv.js";
47-
import { taskContext } from "../task-context-api.js";
48-
import { TraceState } from "@opentelemetry/core";
4944

5045
export type TracingDiagnosticLogLevel =
5146
| "none"
@@ -63,7 +58,6 @@ export type TracingSDKConfig = {
6358
exporters?: SpanExporter[];
6459
logExporters?: LogRecordExporter[];
6560
diagLogLevel?: TracingDiagnosticLogLevel;
66-
externalTraceContext?: unknown;
6761
};
6862

6963
const idGenerator = new RandomIdGenerator();
@@ -129,7 +123,7 @@ export class TracingSDK {
129123
);
130124

131125
const externalTraceId = idGenerator.generateTraceId();
132-
const externalTraceContext = extractExternalTraceContext(config.externalTraceContext);
126+
const externalTraceContext = traceContext.getExternalTraceContext();
133127

134128
for (const exporter of config.exporters ?? []) {
135129
spanProcessors.push(
@@ -408,30 +402,3 @@ class ExternalLogRecordExporterWrapper {
408402
});
409403
}
410404
}
411-
412-
function extractExternalTraceContext(traceContext: unknown) {
413-
if (typeof traceContext !== "object" || traceContext === null) {
414-
return undefined;
415-
}
416-
417-
const tracestate =
418-
"tracestate" in traceContext && typeof traceContext.tracestate === "string"
419-
? traceContext.tracestate
420-
: undefined;
421-
422-
if ("traceparent" in traceContext && typeof traceContext.traceparent === "string") {
423-
const [version, traceId, spanId] = traceContext.traceparent.split("-");
424-
425-
if (!traceId || !spanId) {
426-
return undefined;
427-
}
428-
429-
return {
430-
traceId,
431-
spanId,
432-
tracestate: tracestate,
433-
};
434-
}
435-
436-
return undefined;
437-
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Split module-level variable definition into separate files to allow
2+
// tree-shaking on each api instance.
3+
import { TraceContextAPI } from "./traceContext/api.js";
4+
/** Entrypoint for trace context API */
5+
export const traceContext = TraceContextAPI.getInstance();
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Context } from "@opentelemetry/api";
2+
import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js";
3+
import { TraceContextManager } from "./types.js";
4+
5+
const API_NAME = "trace-context";
6+
7+
class NoopTraceContextManager implements TraceContextManager {
8+
getTraceContext() {
9+
return {};
10+
}
11+
12+
reset() {}
13+
14+
getExternalTraceContext() {
15+
return undefined;
16+
}
17+
18+
extractContext(): Context {
19+
throw new Error("extractContext is not implemented");
20+
}
21+
22+
withExternalTrace<T>(fn: () => T): T {
23+
return fn();
24+
}
25+
}
26+
27+
const NOOP_TRACE_CONTEXT_MANAGER = new NoopTraceContextManager();
28+
29+
export class TraceContextAPI implements TraceContextManager {
30+
private static _instance?: TraceContextAPI;
31+
32+
private constructor() {}
33+
34+
public static getInstance(): TraceContextAPI {
35+
if (!this._instance) {
36+
this._instance = new TraceContextAPI();
37+
}
38+
39+
return this._instance;
40+
}
41+
42+
public setGlobalManager(manager: TraceContextManager): boolean {
43+
return registerGlobal(API_NAME, manager);
44+
}
45+
46+
public disable() {
47+
unregisterGlobal(API_NAME);
48+
}
49+
50+
public reset() {
51+
this.#getManager().reset();
52+
this.disable();
53+
}
54+
55+
public getTraceContext() {
56+
return this.#getManager().getTraceContext();
57+
}
58+
59+
public getExternalTraceContext() {
60+
return this.#getManager().getExternalTraceContext();
61+
}
62+
63+
public extractContext() {
64+
return this.#getManager().extractContext();
65+
}
66+
67+
public withExternalTrace<T>(fn: () => T): T {
68+
return this.#getManager().withExternalTrace(fn);
69+
}
70+
71+
#getManager(): TraceContextManager {
72+
return getGlobal(API_NAME) ?? NOOP_TRACE_CONTEXT_MANAGER;
73+
}
74+
}

0 commit comments

Comments
 (0)