Sources

Middleware toolkit

createMiddlewareLogger and the lower-level primitives — for non-HTTP runtimes (queue workers, CLIs, durable engines) or when defineFrameworkIntegration is too declarative for your case.

The framework integration helpers (defineFrameworkIntegration, evlog() middlewares) cover the typical "HTTP request goes in → wide event comes out" case. When that abstraction doesn't fit — a queue worker, a cron job, a durable workflow with no request object — drop down to the toolkit primitives.

What is this and when do I want it?

You're here when:

  • The unit of work is not a request (a job, a saga step, a CLI invocation)
  • You need to control logger lifetime explicitly (createRequestLogger / emit / _forceKeep)
  • You're wiring evlog into framework internals from scratch

For 95% of HTTP framework integrations, defineFrameworkIntegration is the right tool. Use this page only when that doesn't fit.

Minimal example — a queue worker

import { createRequestLogger } from 'evlog/toolkit'

async function processJob(job: Job) {
  const logger = createRequestLogger({
    method: 'JOB',
    path: `/jobs/${job.type}`,
    requestId: job.id,
  })

  try {
    logger.set({ jobId: job.id, queue: job.queue })
    await doWork(job)
    logger.set({ status: 'success' })
  } catch (err) {
    logger.error(err as Error)
    logger.set({ status: 'failed' })
    throw err
  } finally {
    // Emit a single wide event for the job
    logger.emit({ _forceKeep: true })
  }
}

The _forceKeep flag bypasses sampling — for jobs you typically want every execution recorded.

Full API reference

The canonical home for the toolkit lives at Toolkit reference. Key exports:

ExportWhat
createRequestLogger(opts)Create a request-scoped logger outside the framework integration path
getGlobalPluginRunner()Access the global plugin runner (register plugins, run lifecycle hooks)
defineFrameworkIntegration()High-level helper — see Custom framework
attachForkToLogger()Wire log.fork() to a request-scoped logger

Common pitfalls

  • Don't forget to emit() — without it, the logger accumulates context but no wide event ever leaves
  • Use _forceKeep: true for short-running batch jobs unless your sampling rules already keep them
  • Wrap in try/finally — emit even on error so failed jobs produce events

Going further

Most readers should start with the Frameworks overview and stay on the high-level integration path. Drop here only when that path doesn't accommodate your runtime shape.