Skip to content

Commit 72e286a

Browse files
authored
feat(otel): support for custom resource attributes via config#telemetry.resource and OTEL_RESOURCE_ATTRIBUTES env var (#2704)
1 parent f7240a9 commit 72e286a

File tree

18 files changed

+328
-12
lines changed

18 files changed

+328
-12
lines changed

.changeset/lucky-cameras-shave.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@trigger.dev/sdk": patch
3+
"trigger.dev": patch
4+
---
5+
6+
feat: add ability to set custom resource properties through trigger.config.ts or via the OTEL_RESOURCE_ATTRIBUTES env var

apps/webapp/app/presenters/v3/SpanPresenter.server.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,18 @@ export class SpanPresenter extends BasePresenter {
497497
duration: span.duration,
498498
events: span.events,
499499
style: span.style,
500-
properties: span.properties ? JSON.stringify(span.properties, null, 2) : undefined,
500+
properties:
501+
span.properties &&
502+
typeof span.properties === "object" &&
503+
Object.keys(span.properties).length > 0
504+
? JSON.stringify(span.properties, null, 2)
505+
: undefined,
506+
resourceProperties:
507+
span.resourceProperties &&
508+
typeof span.resourceProperties === "object" &&
509+
Object.keys(span.resourceProperties).length > 0
510+
? JSON.stringify(span.resourceProperties, null, 2)
511+
: undefined,
501512
entity: span.entity,
502513
metadata: span.metadata,
503514
triggeredRuns,

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,17 @@ function SpanEntity({ span }: { span: Span }) {
10711071
showOpenInModal
10721072
/>
10731073
) : null}
1074+
{span.resourceProperties !== undefined ? (
1075+
<CodeBlock
1076+
rowTitle="Resource properties"
1077+
code={span.resourceProperties}
1078+
maxLines={20}
1079+
showLineNumbers={false}
1080+
showCopyButton
1081+
showTextWrapping
1082+
showOpenInModal
1083+
/>
1084+
) : null}
10741085
</div>
10751086
);
10761087
}

apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,12 +825,16 @@ export async function resolveVariablesForEnvironment(
825825
runtimeEnvironment: RuntimeEnvironmentForEnvRepo,
826826
parentEnvironment?: RuntimeEnvironmentForEnvRepo
827827
) {
828-
const projectSecrets = await environmentVariablesRepository.getEnvironmentVariables(
828+
let projectSecrets = await environmentVariablesRepository.getEnvironmentVariables(
829829
runtimeEnvironment.projectId,
830830
runtimeEnvironment.id,
831831
parentEnvironment?.id
832832
);
833833

834+
projectSecrets = renameVariables(projectSecrets, {
835+
OTEL_RESOURCE_ATTRIBUTES: "CUSTOM_OTEL_RESOURCE_ATTRIBUTES",
836+
});
837+
834838
const overridableTriggerVariables = await resolveOverridableTriggerVariables(runtimeEnvironment);
835839

836840
const builtInVariables =
@@ -853,6 +857,15 @@ export async function resolveVariablesForEnvironment(
853857
return result;
854858
}
855859

860+
function renameVariables(variables: EnvironmentVariable[], renameMap: Record<string, string>) {
861+
return variables.map((variable) => {
862+
return {
863+
...variable,
864+
key: renameMap[variable.key] ?? variable.key,
865+
};
866+
});
867+
}
868+
856869
async function resolveOverridableTriggerVariables(
857870
runtimeEnvironment: RuntimeEnvironmentForEnvRepo
858871
) {

apps/webapp/app/v3/eventRepository/clickhouseEventRepository.server.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,10 @@ export class ClickhouseEventRepository implements IEventRepository {
184184
message: event.message,
185185
kind: this.createEventToTaskEventV1InputKind(event),
186186
status: this.createEventToTaskEventV1InputStatus(event),
187-
attributes: this.createEventToTaskEventV1InputAttributes(event.properties),
187+
attributes: this.createEventToTaskEventV1InputAttributes(
188+
event.properties,
189+
event.resourceProperties
190+
),
188191
metadata: this.createEventToTaskEventV1InputMetadata(event),
189192
expires_at: convertDateToClickhouseDateTime(
190193
new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year
@@ -392,7 +395,24 @@ export class ClickhouseEventRepository implements IEventRepository {
392395
return "OK";
393396
}
394397

395-
private createEventToTaskEventV1InputAttributes(attributes: Attributes): Record<string, unknown> {
398+
private createEventToTaskEventV1InputAttributes(
399+
attributes: Attributes,
400+
resourceAttributes?: Attributes
401+
): Record<string, unknown> {
402+
if (!attributes && !resourceAttributes) {
403+
return {};
404+
}
405+
406+
return {
407+
...this.createAttributesToInputAttributes(attributes),
408+
...this.createAttributesToInputAttributes(resourceAttributes, "$resource"),
409+
};
410+
}
411+
412+
private createAttributesToInputAttributes(
413+
attributes: Attributes | undefined,
414+
key?: string
415+
): Record<string, unknown> {
396416
if (!attributes) {
397417
return {};
398418
}
@@ -406,6 +426,12 @@ export class ClickhouseEventRepository implements IEventRepository {
406426
const unflattenedAttributes = unflattenAttributes(publicAttributes);
407427

408428
if (unflattenedAttributes && typeof unflattenedAttributes === "object") {
429+
if (key) {
430+
return {
431+
[key]: unflattenedAttributes,
432+
};
433+
}
434+
409435
return {
410436
...unflattenedAttributes,
411437
};
@@ -1103,6 +1129,7 @@ export class ClickhouseEventRepository implements IEventRepository {
11031129
events: [],
11041130
style: {},
11051131
properties: undefined,
1132+
resourceProperties: undefined,
11061133
entity: {
11071134
type: undefined,
11081135
id: undefined,
@@ -1177,8 +1204,19 @@ export class ClickhouseEventRepository implements IEventRepository {
11771204
}
11781205
}
11791206

1180-
if (!span.properties && typeof record.attributes_text === "string") {
1181-
span.properties = this.#parseAttributes(record.attributes_text);
1207+
if (
1208+
(span.properties == null ||
1209+
(typeof span.properties === "object" && Object.keys(span.properties).length === 0)) &&
1210+
typeof record.attributes_text === "string"
1211+
) {
1212+
const parsedAttributes = this.#parseAttributes(record.attributes_text);
1213+
const resourceAttributes = parsedAttributes["$resource"];
1214+
1215+
// Remove the $resource key from the attributes
1216+
delete parsedAttributes["$resource"];
1217+
1218+
span.properties = parsedAttributes;
1219+
span.resourceProperties = resourceAttributes as Record<string, unknown> | undefined;
11821220
}
11831221
}
11841222

apps/webapp/app/v3/eventRepository/eventRepository.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export type CreateEventInput = Omit<
5353
| "links"
5454
> & {
5555
properties: Attributes;
56+
resourceProperties?: Attributes;
5657
metadata: Attributes | undefined;
5758
style: Attributes | undefined;
5859
};
@@ -209,6 +210,7 @@ export type SpanDetail = {
209210
events: SpanEvents; // Timeline events, SpanEvents component
210211
style: TaskEventStyle; // Icons, variants, accessories (RunIcon, SpanTitle)
211212
properties: Record<string, unknown> | string | number | boolean | null | undefined; // Displayed as JSON in span properties (CodeBlock)
213+
resourceProperties?: Record<string, unknown> | string | number | boolean | null | undefined; // Displayed as JSON in span resource properties (CodeBlock)
212214

213215
// ============================================================================
214216
// Entity & Relationships

apps/webapp/app/v3/otlpExporter.server.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,24 @@ function convertLogsToCreateableEvents(
204204

205205
const resourceProperties = extractEventProperties(resourceAttributes);
206206

207+
const userDefinedResourceAttributes = truncateAttributes(
208+
convertKeyValueItemsToMap(resourceAttributes ?? [], [], undefined, [
209+
SemanticInternalAttributes.USAGE,
210+
SemanticInternalAttributes.SPAN,
211+
SemanticInternalAttributes.METADATA,
212+
SemanticInternalAttributes.STYLE,
213+
SemanticInternalAttributes.METRIC_EVENTS,
214+
SemanticInternalAttributes.TRIGGER,
215+
"process",
216+
"sdk",
217+
"service",
218+
"ctx",
219+
"cli",
220+
"cloud",
221+
]),
222+
spanAttributeValueLengthLimit
223+
);
224+
207225
const taskEventStore =
208226
extractStringAttribute(resourceAttributes, [SemanticInternalAttributes.TASK_EVENT_STORE]) ??
209227
env.EVENT_REPOSITORY_DEFAULT_STORE;
@@ -249,6 +267,7 @@ function convertLogsToCreateableEvents(
249267
status: logLevelToEventStatus(log.severityNumber),
250268
startTime: log.timeUnixNano,
251269
properties,
270+
resourceProperties: userDefinedResourceAttributes,
252271
style: convertKeyValueItemsToMap(
253272
pickAttributes(log.attributes ?? [], SemanticInternalAttributes.STYLE),
254273
[]
@@ -285,6 +304,24 @@ function convertSpansToCreateableEvents(
285304

286305
const resourceProperties = extractEventProperties(resourceAttributes);
287306

307+
const userDefinedResourceAttributes = truncateAttributes(
308+
convertKeyValueItemsToMap(resourceAttributes ?? [], [], undefined, [
309+
SemanticInternalAttributes.USAGE,
310+
SemanticInternalAttributes.SPAN,
311+
SemanticInternalAttributes.METADATA,
312+
SemanticInternalAttributes.STYLE,
313+
SemanticInternalAttributes.METRIC_EVENTS,
314+
SemanticInternalAttributes.TRIGGER,
315+
"process",
316+
"sdk",
317+
"service",
318+
"ctx",
319+
"cli",
320+
"cloud",
321+
]),
322+
spanAttributeValueLengthLimit
323+
);
324+
288325
const taskEventStore =
289326
extractStringAttribute(resourceAttributes, [SemanticInternalAttributes.TASK_EVENT_STORE]) ??
290327
env.EVENT_REPOSITORY_DEFAULT_STORE;
@@ -336,6 +373,7 @@ function convertSpansToCreateableEvents(
336373
events: spanEventsToEventEvents(span.events ?? []),
337374
duration: span.endTimeUnixNano - span.startTimeUnixNano,
338375
properties,
376+
resourceProperties: userDefinedResourceAttributes,
339377
style: convertKeyValueItemsToMap(
340378
pickAttributes(span.attributes ?? [], SemanticInternalAttributes.STYLE),
341379
[]

packages/cli-v3/src/dev/devSupervisor.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,9 +455,6 @@ class DevSupervisor implements WorkerRuntime {
455455
TRIGGER_API_URL: this.options.client.apiURL,
456456
TRIGGER_SECRET_KEY: this.options.client.accessToken!,
457457
OTEL_EXPORTER_OTLP_COMPRESSION: "none",
458-
OTEL_RESOURCE_ATTRIBUTES: JSON.stringify({
459-
[SemanticInternalAttributes.PROJECT_DIR]: this.options.config.workingDir,
460-
}),
461458
OTEL_IMPORT_HOOK_INCLUDES,
462459
};
463460
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ async function doBootstrap() {
209209
logExporters: config.telemetry?.logExporters ?? [],
210210
diagLogLevel: (env.TRIGGER_OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none",
211211
forceFlushTimeoutMillis: 30_000,
212+
resource: config.telemetry?.resource,
212213
});
213214

214215
const otelTracer: Tracer = tracingSDK.getTracer("trigger-dev-worker", VERSION);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ async function doBootstrap() {
188188
forceFlushTimeoutMillis: 30_000,
189189
exporters: config.telemetry?.exporters ?? [],
190190
logExporters: config.telemetry?.logExporters ?? [],
191+
resource: config.telemetry?.resource,
191192
});
192193

193194
const otelTracer: Tracer = tracingSDK.getTracer("trigger-dev-worker", VERSION);

0 commit comments

Comments
 (0)