---
title: "evlog vs LogTape: context and configuration"
description: "Compare LogTape categories, application-owned configuration, context propagation, and testing with evlog request events."
canonical_url: "https://www.evlog.dev/compare/logtape"
---
# 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](https://logtape.org/manual/library) for the consumer-owned configuration contract.

## Compare context and configuration

<table>
<thead>
  <tr>
    <th>
      Concern
    </th>
    
    <th>
      evlog
    </th>
    
    <th>
      LogTape
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Event construction
    </td>
    
    <td>
      <code>
        set()
      </code>
      
       accumulates fields until <code>
        emit()
      </code>
    </td>
    
    <td>
      Structured records with message templates and properties
    </td>
  </tr>
  
  <tr>
    <td>
      Organization
    </td>
    
    <td>
      Logger per operation, with <code>
        fork()
      </code>
      
       for branches
    </td>
    
    <td>
      Hierarchical categories with inherited configuration
    </td>
  </tr>
  
  <tr>
    <td>
      Explicit context
    </td>
    
    <td>
      Fields on the operation logger
    </td>
    
    <td>
      <code>
        logger.with()
      </code>
      
       attaches reusable properties
    </td>
  </tr>
  
  <tr>
    <td>
      Implicit access
    </td>
    
    <td>
      <code>
        useLogger()
      </code>
      
       in integrations backed by AsyncLocalStorage
    </td>
    
    <td>
      <code>
        withContext()
      </code>
      
       when context-local storage is configured
    </td>
  </tr>
  
  <tr>
    <td>
      Levels
    </td>
    
    <td>
      <code>
        trace
      </code>
      
      , <code>
        debug
      </code>
      
      , <code>
        info
      </code>
      
      , <code>
        warn
      </code>
      
      , <code>
        error
      </code>
      
      , <code>
        fatal
      </code>
    </td>
    
    <td>
      <code>
        trace
      </code>
      
      , <code>
        debug
      </code>
      
      , <code>
        info
      </code>
      
      , <code>
        warning
      </code>
      
      , <code>
        error
      </code>
      
      , <code>
        fatal
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Testing
    </td>
    
    <td>
      Memory drain for capturing events and inspecting them
    </td>
    
    <td>
      Dedicated capture/assertion packages and lint rules
    </td>
  </tr>
</tbody>
</table>

LogTape's [context documentation](https://logtape.org/manual/contexts) 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:

```typescript [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`:

```typescript [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. evlog ships `trace` and `fatal`; note that `trace` is dropped by default unless you set `sampling.rates.trace`.

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](https://logtape.org/manual/testing).

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](https://logtape.org/comparison) 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](https://www.evlog.dev/reference/performance) 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](https://www.evlog.dev/learn/wide-events), structured errors, and framework integrations reduce code in your application. For a Node.js logger with serializers and worker transports, see [evlog vs Pino](https://www.evlog.dev/compare/pino). The [logger overview](https://www.evlog.dev/reference/vs-other-loggers) covers the other choices, and [Simple Logging](https://www.evlog.dev/learn/simple-logging) introduces evlog's per-message API.

---

- [evlog vs Pino](https://www.evlog.dev/compare/pino)
- [Performance Benchmarks](https://www.evlog.dev/reference/performance)
- [Wide Events](https://www.evlog.dev/learn/wide-events)


## Sitemap

See the full [sitemap](https://www.evlog.dev/sitemap.md) for all pages.
