Skip to content

Commit 36ecdb5

Browse files
committed
refactor: inline payload builder into trace service, extract traceparent helper
1 parent 91f9fa3 commit 36ecdb5

6 files changed

Lines changed: 91 additions & 96 deletions

File tree

apps/supervisor/src/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { PodCleaner } from "./services/podCleaner.js";
2727
import { FailedPodHandler } from "./services/failedPodHandler.js";
2828
import { getWorkerToken } from "./workerToken.js";
2929
import { OtlpTraceService } from "./services/otlpTraceService.js";
30+
import { extractTraceparent } from "./util.js";
3031

3132
if (env.METRICS_COLLECT_DEFAULTS) {
3233
collectDefaultMetrics({ register });
@@ -255,12 +256,7 @@ class ManagedSupervisor {
255256
// (cold create, restore, warm start). Re-registration on restore is safe
256257
// since dequeue always provides fresh context.
257258
if (this.computeManager?.traceSpansEnabled) {
258-
const traceparent =
259-
message.run.traceContext &&
260-
"traceparent" in message.run.traceContext &&
261-
typeof message.run.traceContext.traceparent === "string"
262-
? message.run.traceContext.traceparent
263-
: undefined;
259+
const traceparent = extractTraceparent(message.run.traceContext);
264260

265261
if (traceparent) {
266262
this.workloadServer.registerRunTraceContext(message.run.friendlyId, {

apps/supervisor/src/otlpPayload.ts

Lines changed: 0 additions & 63 deletions
This file was deleted.

apps/supervisor/src/services/otlpTraceService.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { describe, it, expect } from "vitest";
2-
import { buildOtlpTracePayload } from "../otlpPayload.js";
2+
import { buildPayload } from "./otlpTraceService.js";
33

4-
describe("buildOtlpTracePayload", () => {
4+
describe("buildPayload", () => {
55
it("builds valid OTLP JSON with timing attributes", () => {
6-
const payload = buildOtlpTracePayload({
6+
const payload = buildPayload({
77
traceId: "abcd1234abcd1234abcd1234abcd1234",
88
parentSpanId: "1234567890abcdef",
99
spanName: "compute.provision",
@@ -55,7 +55,7 @@ describe("buildOtlpTracePayload", () => {
5555
});
5656

5757
it("generates a valid 16-char hex span ID", () => {
58-
const payload = buildOtlpTracePayload({
58+
const payload = buildPayload({
5959
traceId: "abcd1234abcd1234abcd1234abcd1234",
6060
spanName: "test",
6161
startTimeMs: 1000,
@@ -69,7 +69,7 @@ describe("buildOtlpTracePayload", () => {
6969
});
7070

7171
it("converts timestamps to nanoseconds", () => {
72-
const payload = buildOtlpTracePayload({
72+
const payload = buildPayload({
7373
traceId: "abcd1234abcd1234abcd1234abcd1234",
7474
spanName: "test",
7575
startTimeMs: 1000,
@@ -84,7 +84,7 @@ describe("buildOtlpTracePayload", () => {
8484
});
8585

8686
it("omits parentSpanId when not provided", () => {
87-
const payload = buildOtlpTracePayload({
87+
const payload = buildPayload({
8888
traceId: "abcd1234abcd1234abcd1234abcd1234",
8989
spanName: "test",
9090
startTimeMs: 1000,
@@ -98,7 +98,7 @@ describe("buildOtlpTracePayload", () => {
9898
});
9999

100100
it("handles double values for non-integer numbers", () => {
101-
const payload = buildOtlpTracePayload({
101+
const payload = buildPayload({
102102
traceId: "abcd1234abcd1234abcd1234abcd1234",
103103
spanName: "test",
104104
startTimeMs: 1000,

apps/supervisor/src/services/otlpTraceService.ts

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
1+
import { randomBytes } from "crypto";
12
import { SimpleStructuredLogger } from "@trigger.dev/core/v3/utils/structuredLogger";
2-
import { buildOtlpTracePayload, type OtlpTraceOptions } from "../otlpPayload.js";
33

44
export type OtlpTraceServiceOptions = {
55
endpointUrl: string;
66
timeoutMs?: number;
77
};
88

9+
export type OtlpTraceSpan = {
10+
traceId: string;
11+
parentSpanId?: string;
12+
spanName: string;
13+
startTimeMs: number;
14+
endTimeMs: number;
15+
resourceAttributes: Record<string, string | number | boolean>;
16+
spanAttributes: Record<string, string | number | boolean>;
17+
};
18+
919
export class OtlpTraceService {
1020
private readonly logger = new SimpleStructuredLogger("otlp-trace");
1121

1222
constructor(private opts: OtlpTraceServiceOptions) {}
1323

1424
/** Fire-and-forget: build payload and send to the configured OTLP endpoint */
15-
emit(opts: OtlpTraceOptions): void {
16-
const payload = buildOtlpTracePayload(opts);
25+
emit(span: OtlpTraceSpan): void {
26+
const payload = buildPayload(span);
1727

1828
fetch(`${this.opts.endpointUrl}/v1/traces`, {
1929
method: "POST",
@@ -27,3 +37,57 @@ export class OtlpTraceService {
2737
});
2838
}
2939
}
40+
41+
// ── Payload builder (internal) ───────────────────────────────────────────────
42+
43+
/** @internal Exported for tests only */
44+
export function buildPayload(span: OtlpTraceSpan) {
45+
const spanId = randomBytes(8).toString("hex");
46+
47+
return {
48+
resourceSpans: [
49+
{
50+
resource: {
51+
attributes: [
52+
{ key: "$trigger", value: { boolValue: true } },
53+
...toOtlpAttributes(span.resourceAttributes),
54+
],
55+
},
56+
scopeSpans: [
57+
{
58+
scope: { name: "supervisor.compute" },
59+
spans: [
60+
{
61+
traceId: span.traceId,
62+
spanId,
63+
parentSpanId: span.parentSpanId,
64+
name: span.spanName,
65+
kind: 3, // SPAN_KIND_CLIENT
66+
startTimeUnixNano: String(span.startTimeMs * 1_000_000),
67+
endTimeUnixNano: String(span.endTimeMs * 1_000_000),
68+
attributes: toOtlpAttributes(span.spanAttributes),
69+
status: { code: 1 }, // STATUS_CODE_OK
70+
},
71+
],
72+
},
73+
],
74+
},
75+
],
76+
};
77+
}
78+
79+
function toOtlpAttributes(
80+
attrs: Record<string, string | number | boolean>
81+
): Array<{ key: string; value: Record<string, unknown> }> {
82+
return Object.entries(attrs).map(([key, value]) => ({
83+
key,
84+
value: toOtlpValue(value),
85+
}));
86+
}
87+
88+
function toOtlpValue(value: string | number | boolean): Record<string, unknown> {
89+
if (typeof value === "string") return { stringValue: value };
90+
if (typeof value === "boolean") return { boolValue: value };
91+
if (Number.isInteger(value)) return { intValue: value };
92+
return { doubleValue: value };
93+
}

apps/supervisor/src/util.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ export function getDockerHostDomain() {
1414
return isMacOS || isWindows ? "host.docker.internal" : "localhost";
1515
}
1616

17+
/** Extract the W3C traceparent string from an untyped trace context record */
18+
export function extractTraceparent(traceContext?: Record<string, unknown>): string | undefined {
19+
if (
20+
traceContext &&
21+
"traceparent" in traceContext &&
22+
typeof traceContext.traceparent === "string"
23+
) {
24+
return traceContext.traceparent;
25+
}
26+
return undefined;
27+
}
28+
1729
export function getRunnerId(runId: string, attemptNumber?: number) {
1830
const parts = ["runner", runId.replace("run_", "")];
1931

apps/supervisor/src/workloadManager/compute.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type WorkloadManagerOptions,
88
} from "./types.js";
99
import { ComputeClient, stripImageDigest } from "@internal/compute";
10-
import { getRunnerId } from "../util.js";
10+
import { extractTraceparent, getRunnerId } from "../util.js";
1111
import type { OtlpTraceService } from "../services/otlpTraceService.js";
1212
import { tryCatch } from "@trigger.dev/core";
1313

@@ -213,14 +213,7 @@ export class ComputeWorkloadManager implements WorkloadManager {
213213
#emitProvisionSpan(opts: WorkloadManagerCreateOptions, startMs: number, timing?: unknown) {
214214
if (!this.traceSpansEnabled) return;
215215

216-
const traceparent =
217-
opts.traceContext &&
218-
"traceparent" in opts.traceContext &&
219-
typeof opts.traceContext.traceparent === "string"
220-
? opts.traceContext.traceparent
221-
: undefined;
222-
223-
const parsed = parseTraceparent(traceparent);
216+
const parsed = parseTraceparent(extractTraceparent(opts.traceContext));
224217
if (!parsed) return;
225218

226219
const endMs = performance.now();
@@ -342,14 +335,7 @@ export class ComputeWorkloadManager implements WorkloadManager {
342335
) {
343336
if (!this.traceSpansEnabled) return;
344337

345-
const traceparent =
346-
opts.traceContext &&
347-
"traceparent" in opts.traceContext &&
348-
typeof opts.traceContext.traceparent === "string"
349-
? opts.traceContext.traceparent
350-
: undefined;
351-
352-
const parsed = parseTraceparent(traceparent);
338+
const parsed = parseTraceparent(extractTraceparent(opts.traceContext));
353339
if (!parsed || !opts.envId || !opts.orgId || !opts.projectId) return;
354340

355341
const endMs = performance.now();

0 commit comments

Comments
 (0)