Logging, Tracing & Metrics
The run log shows exactly what happened in every task run — logs, traces, and spans are all captured.
Logs
Use standard console.log(), console.error(), etc. — all output is captured in your run log.
Structured Logging with logger
Use the logger object for structured logs that are easier to search and filter:
import { task, logger } from "@trigger.dev/sdk";
export const loggingExample = task({
id: "logging-example",
run: async (payload: { data: Record<string, string> }) => {
logger.debug("Debug message", payload.data);
logger.log("Log message", payload.data);
logger.info("Info message", payload.data);
logger.warn("You've been warned", payload.data);
logger.error("Error message", payload.data);
},
});Tracing and Spans
Trigger.dev uses OpenTelemetry tracing. Automatic tracing covers:
- Task triggers
- Task attempts
- HTTP requests
Add Instrumentations
Configure in trigger.config.ts. Example: Prisma instrumentation auto-traces all queries.
import { defineConfig } from "@trigger.dev/sdk";
import { PrismaInstrumentation } from "@prisma/instrumentation";
export default defineConfig({
instrumentations: [new PrismaInstrumentation()],
});Custom Traces
import { logger, task } from "@trigger.dev/sdk";
export const customTrace = task({
id: "custom-trace",
run: async (payload) => {
const user = await logger.trace("fetch-user", async (span) => {
span.setAttribute("user.id", "1");
return { id: "1", name: "John Doe", fetchedAt: new Date() };
});
},
});Custom Metrics (SDK 4.4.1+)
Import otel from @trigger.dev/sdk. Create instruments at module level (outside run):
import { task, otel } from "@trigger.dev/sdk";
const meter = otel.metrics.getMeter("my-app");
// Create instruments at module level
const itemsProcessed = meter.createCounter("items.processed", {
description: "Total items processed",
unit: "items",
});
const itemDuration = meter.createHistogram("item.duration", {
description: "Time per item",
unit: "ms",
});
const queueDepth = meter.createUpDownCounter("queue.depth", {
description: "Current queue depth",
unit: "items",
});
export const processQueue = task({
id: "process-queue",
run: async (payload: { items: string[] }) => {
queueDepth.add(payload.items.length);
for (const item of payload.items) {
const start = performance.now();
// process item...
itemsProcessed.add(1, { "item.type": "order" });
itemDuration.record(performance.now() - start, { "item.type": "order" });
queueDepth.add(-1);
}
},
});Instrument Types
| Instrument | Method | Use case |
|---|---|---|
| Counter | meter.createCounter() | Monotonically increasing values |
| Histogram | meter.createHistogram() | Distributions (durations, sizes) |
| UpDownCounter | meter.createUpDownCounter() | Values that go up and down (queue depth) |
Automatic System Metrics (SDK 4.4.1+)
| Metric | Type | Unit | Description |
|---|---|---|---|
process.cpu.utilization | gauge | ratio | CPU usage (0–1) |
process.cpu.time | counter | seconds | CPU time consumed |
process.memory.usage | gauge | bytes | Process memory |
nodejs.event_loop.utilization | gauge | ratio | Event loop utilization |
nodejs.event_loop.delay.p95 | gauge | seconds | p95 event loop delay |
nodejs.heap.used | gauge | bytes | V8 heap used |
nodejs.heap.total | gauge | bytes | V8 heap total |
All metrics include context attributes: run_id, task_identifier, attempt_number, machine_name, worker_version, environment_type.
Querying Metrics (TRQL)
SELECT timeBucket(), avg(value) AS avg_cpu
FROM metrics
WHERE metric_name = 'process.cpu.utilization'
GROUP BY timeBucket
ORDER BY timeBucket
LIMIT 1000Exporting Metrics
Configure telemetry exporters in trigger.config.ts to send to Axiom, Honeycomb, Datadog, or any OTLP-compatible service.