Skip to content

Logging

Queuert emits structured log entries for every lifecycle event — worker start/stop, job creation, attempts, failures, completions, chain lifecycle, blockers, and adapter errors. Logging is part of queuert core and does not require the @queuert/otel package.

Pass a log function to createClient to receive log entries:

import { createClient, type Log } from "queuert";
const log: Log = (entry) => {
console.log(`[${entry.level}] ${entry.message}`, entry.data);
};
const client = await createClient({
stateAdapter,
notifyAdapter,
log,
jobTypes,
});

Every log entry is a typed object with the following shape:

{
type: string; // Machine-readable event identifier (e.g. "job_created")
level: LogLevel; // "info" | "warn" | "error"
message: string; // Human-readable description
data: { ... }; // Structured data specific to the event
error?: unknown; // Present only on error/warn entries that carry an error
}

All entries are strongly typed — the type field determines the exact shape of data, the level, and the message. This means consumers can switch on type for type-safe handling.

Type Level Message Data
worker_started info Started worker workerId, jobTypeNames
worker_error error Worker error workerId; top-level error
worker_stopping info Stopping worker… workerId
worker_stopped info Worker has been stopped workerId
Type Level Message Data
job_created info Job created id, typeName, chainId, chainTypeName, input, blockers, scheduledAt
job_completed info Job completed id, typeName, chainId, chainTypeName, status, attempt, output?, continuedWith?
job_blocked info Job blocked by incomplete chains id, typeName, chainId, chainTypeName, blockedByChains
job_rescheduled info Job rescheduled id, typeName, chainId, chainTypeName, scheduledAt
job_unblocked info Job unblocked id, typeName, chainId, chainTypeName, unblockedByChain
Type Level Message Data
job_attempt_started info Job attempt started id, typeName, chainId, chainTypeName, status, attempt, workerId
job_attempt_completed info Job attempt completed id, typeName, chainId, chainTypeName, status, attempt, output?, continuedWith?, workerId
job_attempt_failed error Job attempt failed id, typeName, chainId, chainTypeName, status, attempt, workerId; top-level error
job_attempt_taken_by_another_worker warn Job taken by another worker id, typeName, chainId, chainTypeName, status, attempt, attemptAt, attemptBy, attemptUntil, workerId
job_attempt_already_completed warn Job already completed by another worker id, typeName, chainId, chainTypeName, status, attempt, completedBy, workerId
job_attempt_expired warn Job attempt expired id, typeName, chainId, chainTypeName, status, attempt, attemptAt, attemptBy, attemptUntil, workerId
job_attempt_extended info Job attempt extended id, typeName, chainId, chainTypeName, status, attempt, attemptAt, attemptBy, attemptUntil, workerId
job_attempt_reclaimed info Reclaimed expired job attempt id, typeName, chainId, chainTypeName, status, attempt, attemptAt, attemptBy, attemptUntil, workerId (attempt fields are null; see below)

job_attempt_reclaimed reports the job as it is after the reclaim: status is pending and attemptAt, attemptBy, and attemptUntil are null, so the entry does not identify the worker that previously held the attempt.

Type Level Message Data
chain_created info Chain created id, typeName, input
chain_completed info Chain completed id, typeName, output
chain_deleted info Chain deleted id, typeName
Type Level Message Data
notify_adapter_error warn Notify adapter error operation; top-level error
state_adapter_error warn State adapter error operation; top-level error
Type Level Message Data
job_type_validation_error error (dynamic) code, typeName, plus error-specific details; top-level error

Log entry data fields compose from a few base shapes:

  • JobBasicData — id, typeName, chainId, chainTypeName
  • JobProcessingData — extends JobBasicData with status, attempt
  • JobAttemptData — extends JobProcessingData with attemptAt, attemptBy, attemptUntil
  • ChainBasicData — id, typeName

The log function and the ObservabilityAdapter are independent. The internal ObservabilityHelper calls both on each event, ensuring logs and metrics/traces stay consistent. You can use either or both:

  • log only — structured logging without OTel dependency
  • ObservabilityAdapter only — metrics and traces without logging
  • Both — full observability
  • OTEL Metrics — OpenTelemetry counters, histograms, and gauges
  • OTEL Tracing — Span hierarchy and distributed tracing
  • Adapters — Overall adapter design philosophy