Skip to content

Commit bc020a3

Browse files
committed
Get the internal telemetry ready for test/prod
1 parent b361afb commit bc020a3

File tree

4 files changed

+74
-37
lines changed

4 files changed

+74
-37
lines changed

.env.example

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,12 @@ COORDINATOR_SECRET=coordinator-secret # generate the actual secret with `openssl
6464
# OBJECT_STORE_BASE_URL="https://{bucket}.{accountId}.r2.cloudflarestorage.com"
6565
# OBJECT_STORE_ACCESS_KEY_ID=
6666
# OBJECT_STORE_SECRET_ACCESS_KEY=
67-
# RUNTIME_WAIT_THRESHOLD_IN_MS=10000
67+
# RUNTIME_WAIT_THRESHOLD_IN_MS=10000
68+
69+
# These control the server-side internal telemetry
70+
# INTERNAL_OTEL_TRACE_EXPORTER_URL=<URL to send traces to>
71+
# INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_NAME=<Header name for the auth token>
72+
# INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_VALUE=<Auth token value>
73+
# INTERNAL_OTEL_TRACE_LOGGING_ENABLED=1
74+
# INTERNAL_OTEL_TRACE_SAMPING_RATE=20 # this means 1/20 traces or 5% of traces will be sampled (sampled = recorded)
75+
# INTERNAL_OTEL_TRACE_INSTRUMENT_PRISMA_ENABLED=0,

apps/webapp/app/env.server.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ const EnvironmentSchema = z.object({
8585

8686
//v3
8787
V3_ENABLED: z.string().default("false"),
88-
OTLP_EXPORTER_TRACES_URL: z.string().optional(),
89-
LOG_TELEMETRY: z.string().default("true"),
9088
IMAGE_REGISTRY: z.string().default("docker.io"),
9189
IMAGE_REPO: z.string().default("task"),
9290
PROVIDER_SECRET: z.string().default("provider-secret"),
@@ -117,6 +115,15 @@ const EnvironmentSchema = z.object({
117115
DEV_OTEL_LOG_EXPORT_TIMEOUT_MILLIS: z.string().default("30000"),
118116
DEV_OTEL_LOG_MAX_QUEUE_SIZE: z.string().default("512"),
119117
RUNTIME_WAIT_THRESHOLD_IN_MS: z.coerce.number().int().default(30000),
118+
119+
// Internal OTEL environment variables
120+
INTERNAL_OTEL_TRACE_EXPORTER_URL: z.string().optional(),
121+
INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_NAME: z.string().optional(),
122+
INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_VALUE: z.string().optional(),
123+
INTERNAL_OTEL_TRACE_LOGGING_ENABLED: z.string().default("1"),
124+
// this means 1/20 traces or 5% of traces will be sampled (sampled = recorded)
125+
INTERNAL_OTEL_TRACE_SAMPING_RATE: z.string().default("20"),
126+
INTERNAL_OTEL_TRACE_INSTRUMENT_PRISMA_ENABLED: z.string().default("0"),
120127
});
121128

122129
export type Environment = z.infer<typeof EnvironmentSchema>;

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

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
trace,
1010
} from "@opentelemetry/api";
1111
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
12-
import { registerInstrumentations } from "@opentelemetry/instrumentation";
12+
import { InstrumentationOption, registerInstrumentations } from "@opentelemetry/instrumentation";
1313
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
1414
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
1515
import { Resource } from "@opentelemetry/resources";
@@ -23,12 +23,13 @@ import {
2323
TraceIdRatioBasedSampler,
2424
} from "@opentelemetry/sdk-trace-base";
2525
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
26-
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
26+
import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
2727
import { PrismaInstrumentation } from "@prisma/instrumentation";
2828
import { env } from "~/env.server";
2929
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
3030
import { singleton } from "~/utils/singleton";
3131
import { LoggerSpanExporter } from "./telemetry/loggerExporter.server";
32+
3233
class CustomWebappSampler implements Sampler {
3334
constructor(private readonly _baseSampler: Sampler) {}
3435

@@ -44,10 +45,7 @@ class CustomWebappSampler implements Sampler {
4445
const parentContext = trace.getSpanContext(context);
4546

4647
// Exclude Prisma spans (adjust this logic as needed for your use case)
47-
if (
48-
!parentContext &&
49-
((attributes && attributes["model"] && attributes["method"]) || name.includes("prisma"))
50-
) {
48+
if (!parentContext && name.includes("prisma")) {
5149
return { decision: SamplingDecision.NOT_RECORD };
5250
}
5351

@@ -65,29 +63,46 @@ export const tracer = singleton("tracer", getTracer);
6563
function getTracer() {
6664
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ERROR);
6765

66+
const samplingRate = 1.0 / Math.max(parseInt(env.INTERNAL_OTEL_TRACE_SAMPING_RATE, 10), 1);
67+
6868
const provider = new NodeTracerProvider({
6969
forceFlushTimeoutMillis: 500,
7070
resource: new Resource({
71-
[SemanticResourceAttributes.SERVICE_NAME]: "trigger.dev",
71+
[SEMRESATTRS_SERVICE_NAME]: "trigger.dev",
7272
}),
7373
sampler: new ParentBasedSampler({
74-
root: new CustomWebappSampler(
75-
new TraceIdRatioBasedSampler(env.APP_ENV === "development" ? 1.0 : 0.05)
76-
), // 5% sampling
74+
root: new CustomWebappSampler(new TraceIdRatioBasedSampler(samplingRate)), // 5% sampling
7775
}), // 5% sampling
7876
});
7977

80-
if (env.OTLP_EXPORTER_TRACES_URL) {
78+
if (env.INTERNAL_OTEL_TRACE_EXPORTER_URL) {
8179
const exporter = new OTLPTraceExporter({
82-
url: env.OTLP_EXPORTER_TRACES_URL,
80+
url: env.INTERNAL_OTEL_TRACE_EXPORTER_URL,
8381
timeoutMillis: 1000,
82+
headers:
83+
env.INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_NAME &&
84+
env.INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_VALUE
85+
? {
86+
[env.INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_NAME]:
87+
env.INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_VALUE,
88+
}
89+
: undefined,
8490
});
8591

86-
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
92+
provider.addSpanProcessor(
93+
new BatchSpanProcessor(exporter, {
94+
maxExportBatchSize: 512,
95+
scheduledDelayMillis: 200,
96+
exportTimeoutMillis: 30000,
97+
maxQueueSize: 2048,
98+
})
99+
);
87100

88-
console.log(` Tracer: OTLP exporter enabled to ${env.OTLP_EXPORTER_TRACES_URL}`);
101+
console.log(`🔦 Tracer: OTLP exporter enabled to ${env.INTERNAL_OTEL_TRACE_EXPORTER_URL}`);
89102
} else {
90-
if (env.LOG_TELEMETRY === "true") {
103+
if (env.INTERNAL_OTEL_TRACE_LOGGING_ENABLED === "1") {
104+
console.log(`🔦 Tracer: Logger exporter enabled`);
105+
91106
const loggerExporter = new LoggerSpanExporter();
92107

93108
provider.addSpanProcessor(new SimpleSpanProcessor(loggerExporter));
@@ -96,13 +111,18 @@ function getTracer() {
96111

97112
provider.register();
98113

114+
let instrumentations: InstrumentationOption[] = [
115+
new HttpInstrumentation(),
116+
new ExpressInstrumentation(),
117+
];
118+
119+
if (env.INTERNAL_OTEL_TRACE_INSTRUMENT_PRISMA_ENABLED === "1") {
120+
instrumentations.push(new PrismaInstrumentation());
121+
}
122+
99123
registerInstrumentations({
100124
tracerProvider: provider,
101-
instrumentations: [
102-
new HttpInstrumentation(),
103-
new ExpressInstrumentation(),
104-
new PrismaInstrumentation(),
105-
],
125+
instrumentations,
106126
});
107127

108128
return provider.getTracer("trigger.dev", "3.0.0.dp.1");

packages/core-backend/src/logger.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,22 @@ export class Logger {
9494
...args: Array<Record<string, unknown> | undefined>
9595
) {
9696
// Get the current context from trace if it exists
97-
const currentContext = trace.getSpan(context.active());
98-
99-
const structuredLog = {
100-
...structureArgs(safeJsonClone(args) as Record<string, unknown>[], this.#filteredKeys),
101-
...this.#additionalFields(),
102-
timestamp: new Date(),
103-
name: this.#name,
104-
message,
105-
level,
106-
traceId: currentContext?.spanContext().traceId,
107-
parentSpanId: currentContext?.spanContext().spanId,
108-
};
109-
110-
loggerFunction(JSON.stringify(structuredLog, this.#jsonReplacer));
97+
const currentSpan = trace.getSpan(context.active());
98+
99+
if (!currentSpan || currentSpan.isRecording()) {
100+
const structuredLog = {
101+
...structureArgs(safeJsonClone(args) as Record<string, unknown>[], this.#filteredKeys),
102+
...this.#additionalFields(),
103+
timestamp: new Date(),
104+
name: this.#name,
105+
message,
106+
level,
107+
traceId: currentSpan?.spanContext().traceId,
108+
parentSpanId: currentSpan?.spanContext().spanId,
109+
};
110+
111+
loggerFunction(JSON.stringify(structuredLog, this.#jsonReplacer));
112+
}
111113
}
112114
}
113115

0 commit comments

Comments
 (0)