Durabull Documentation

Log Formatting and Highlighting

Write job logs that the Logs tab can highlight and search.

Write logs with BullMQ's job.log; console output is not automatically captured. Use consistent field names and put important fields first. Prefer UTC ISO 8601 timestamps and compact JSON.

[<timestamp>] [<LEVEL>] [<OPTIONAL_CONTEXT>] message | key=value | key2=value2
[2026-02-13T21:25:43.232Z] [ERROR] [SYNC] Request failed | jobId=42 | requestId=req_123 | status=404

Highlighting rules

PartBehavior
TimestampA bracketed timestamp or plain timestamp prefix is recognized.
LevelA bracketed level after the timestamp or a plain prefix such as ERROR: is recognized.
ContextUp to four additional bracketed tokens become context tags.
MessageRemaining text forms the message; `
Fieldskey=value segments and JSON fragments receive highlighting.

Recognized levels are TRACE, DEBUG, INFO, WARN / WARNING, ERROR, FATAL, SUCCESS, and CONTEXT.

Search, expand, and copy

Search is case-insensitive and checks the full raw text of loaded lines. Scroll to load more pages before assuming a missing result is absent from all logs.

Long messages are truncated for display; use Expand log line to see the full body. Copy includes the full raw text of the currently loaded, filtered lines. Keep large payloads and stack traces out of individual log lines to make them easier to scan.

Worker Helper Pattern (TypeScript)

type LogLevel = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL' | 'SUCCESS'

export function emitJobLog(
  log: (line: string) => Promise<unknown>,
  level: LogLevel,
  message: string,
  fields: Record<string, string | number | boolean> = {},
  context?: string
) {
  const timestamp = new Date().toISOString()
  const contextPart = context ? ` [${context}]` : ''
  const fieldText = Object.entries(fields)
    .map(([key, value]) => `${key}=${String(value)}`)
    .join(' | ')

  const suffix = fieldText ? ` | ${fieldText}` : ''
  return log(`[${timestamp}] [${level}]${contextPart} ${message}${suffix}`)
}

Use it with BullMQ's job.log and await the write:

await emitJobLog((line) => job.log(line), 'INFO', 'Processing job', { attempt: 1 })

Console logs are not automatically captured by BullMQ; write to job.log for the Durabull Logs tab.