Compare

evlog vs LogTape: context and configuration

Compare LogTape categories, application-owned configuration, context propagation, and testing with evlog request events.

Do you publish a library or operate an application? Start with who should own the logging configuration. LogTape lets a library record through categories while the consuming application selects the output. evlog provides event accumulation and integrations for an application's request lifecycle.

LogTape also works in applications. The useful decision is whether you need its category and configuration model, evlog's accumulated events, or separate logging arrangements for library and application code. See LogTape's library guide for the consumer-owned configuration contract.

Compare context and configuration

ConcernevlogLogTape
Event constructionset() accumulates fields until emit()Structured records with message templates and properties
OrganizationLogger per operation, with fork() for branchesHierarchical categories with inherited configuration
Explicit contextFields on the operation loggerlogger.with() attaches reusable properties
Implicit accessuseLogger() in integrations backed by AsyncLocalStoragewithContext() when context-local storage is configured
Levelsdebug, info, warn, errortrace, debug, info, warning, error, fatal
TestingMemory drain for capturing events and inspecting themDedicated capture/assertion packages and lint rules

LogTape's context documentation distinguishes explicit and implicit contexts. Implicit context needs a compatible runtime and configured contextLocalStorage, such as Node's AsyncLocalStorage. It is not equivalent to log.fork(), which branches an accumulated event. Check runtime support before assuming implicit context works in a browser.

Compare one completed checkout

Each example below emits one business record with an explicit outcome. The checkout is simulated, so neither requires a payment provider. Install the corresponding library and run the file with Node.js 24. Pass decline to exercise the error path.

LogTape records the final outcome through a category configured by the application:

checkout-logtape.ts
import { configure, getConsoleSink, getLogger } from '@logtape/logtape'

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: 'checkout', lowestLevel: 'info', sinks: ['console'] },
    { category: ['logtape', 'meta'], lowestLevel: 'warning', sinks: ['console'] },
  ],
})

const logger = getLogger('checkout').with({ orderId: 'order-123', amount: 2999 })
try {
  if (process.argv.includes('decline')) throw new Error('Payment declined')
  logger.info('Checkout {outcome}', { outcome: 'paid' })
} catch (error) {
  logger.error('Checkout {outcome}: {error}', { outcome: 'declined', error })
  process.exitCode = 1
}

evlog accumulates the result and emits in finally:

checkout-evlog.ts
import { createLogger, initLogger } from 'evlog'

initLogger({ env: { service: 'checkout' }, pretty: false })
const log = createLogger({ orderId: 'order-123', amount: 2999 })
try {
  if (process.argv.includes('decline')) throw new Error('Payment declined')
  log.set({ outcome: 'paid' })
} catch (error) {
  log.error(error instanceof Error ? error : new Error(String(error)))
  log.set({ outcome: 'declined' })
  process.exitCode = 1
} finally {
  log.emit()
}

These examples compare ownership of context and emission, not equivalent output formats or speed. LogTape's console sink formats a record for the terminal here. evlog is configured for JSON. Neither example logs success before checking the result or duplicates a failure record.

Plan the migration around the features you use

Category-specific verbosity has no direct equivalent in evlog's operation model. Decide how to preserve that control before mapping categories to context fields. Map trace and fatal deliberately if dashboards or alerts depend on them.

LogTape offers separate redaction and testing packages. evlog applies configured redaction before its output and provides createMemoryDrain(), readMemoryLogs(), and clearMemoryLogs() through evlog/memory. A memory buffer supplies events for your assertions, not an equivalent assertion DSL. Compare those workflows against the LogTape testing guide.

A custom LogTape sink also needs an explicit delivery plan when moving to evlog. Do not assume configuration, flushing, or retry behavior transfers unchanged.

Check the bundle you will ship

Published measurements from different projects use different workloads, versions, and imports. In particular, LogTape's comparison table identifies historical versions for its figures. It does not establish the size or throughput of your current application bundle relative to evlog.

The Performance reference explains evlog's benchmark boundaries. Measure the entry points and output settings you intend to deploy before choosing on size or speed.

Keep LogTape when consumer-owned configuration, categories, or its testing tools are central to your design. Consider evlog when its wide-event lifecycle, structured errors, and framework integrations reduce code in your application. For a Node.js logger with serializers and worker transports, see evlog vs Pino. The logger overview covers the other choices, and Simple Logging introduces evlog's per-message API.