---
title: "evlog vs Winston: formats and request events"
description: "Compare Winston formats, transports, and custom levels with evlog event accumulation before migrating an existing application."
canonical_url: "https://www.evlog.dev/compare/winston"
---
# evlog vs Winston: formats and request events

> Compare Winston formats, transports, and custom levels with evlog event accumulation before migrating an existing application.

Your Winston configuration may encode years of operational decisions: which fields reach a backend, which errors trigger alerts, and which transports receive each severity. Start a migration by preserving those decisions.

## At a glance

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      Winston
    </th>
    
    <th>
      evlog
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Core model
    </td>
    
    <td>
      Each call writes an <code>
        info
      </code>
      
       object through a format chain to one or more transports
    </td>
    
    <td>
      Individual structured logs, plus an operation logger that accumulates fields and emits one event
    </td>
  </tr>
  
  <tr>
    <td>
      Output shaping
    </td>
    
    <td>
      Composable formats from <code>
        logform
      </code>
      
       (<code>
        combine
      </code>
      
      , <code>
        json
      </code>
      
      , <code>
        printf
      </code>
      
      , custom formats)
    </td>
    
    <td>
      Event fields set by the application, plugins and enrichers, then drain serialization
    </td>
  </tr>
  
  <tr>
    <td>
      Destinations
    </td>
    
    <td>
      Core Console, File and Http transports, plus community transports
    </td>
    
    <td>
      Drain adapters such as Axiom, OTLP, Sentry, PostHog, Datadog and Better Stack
    </td>
  </tr>
  
  <tr>
    <td>
      Levels
    </td>
    
    <td>
      npm levels by default (<code>
        error
      </code>
      
       to <code>
        silly
      </code>
      
      ), or custom levels
    </td>
    
    <td>
      Fixed severities, with sampling for wide events
    </td>
  </tr>
  
  <tr>
    <td>
      Process failures
    </td>
    
    <td>
      <code>
        exceptionHandlers
      </code>
      
       and <code>
        rejectionHandlers
      </code>
    </td>
    
    <td>
      Request errors become structured error events; process handlers stay your choice
    </td>
  </tr>
  
  <tr>
    <td>
      Extras
    </td>
    
    <td>
      Profiling, querying and streaming logs from some transports
    </td>
    
    <td>
      Structured errors with <code>
        why
      </code>
      
      , <code>
        fix
      </code>
      
       and <code>
        link
      </code>
    </td>
  </tr>
</tbody>
</table>

Sources: the [Winston README](https://github.com/winstonjs/winston#readme) sections on formats, logging levels, transports, exceptions, rejections and profiling.

## Identify what the configuration already does

Winston composes formats through `logform`, supports custom levels, and configures multiple transports with their own levels. It also offers child loggers, profiling, and exception/rejection handlers. JSON output is available through `format.json()`. These are reasons to retain it when the pipeline already works. See the [Winston documentation](https://github.com/winstonjs/winston).

<table>
<thead>
  <tr>
    <th>
      Existing requirement
    </th>
    
    <th>
      What to check in evlog
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Custom levels
    </td>
    
    <td>
      Map them to four severities and update queries or alerts that depend on the original values.
    </td>
  </tr>
  
  <tr>
    <td>
      Format chain
    </td>
    
    <td>
      Decide which transformations become context fields, plugin hooks, or drain serialization.
    </td>
  </tr>
  
  <tr>
    <td>
      Custom transport
    </td>
    
    <td>
      Find a matching drain or port the protocol and delivery behavior.
    </td>
  </tr>
  
  <tr>
    <td>
      Per-transport level
    </td>
    
    <td>
      Preserve destination-specific filtering explicitly in the delivery configuration.
    </td>
  </tr>
  
  <tr>
    <td>
      Exception/rejection handler
    </td>
    
    <td>
      Keep process-level failure handling separate from an individual request's error event.
    </td>
  </tr>
</tbody>
</table>

## Decide who assembles the final event

Winston can log a completed object once. The application decides how to collect it. evlog's `createLogger()` provides that accumulator, `set()` adds context, and `emit()` produces the final event. Framework integrations manage that operation lifecycle.

Use [Wide Events](https://www.evlog.dev/learn/wide-events) to decide whether this model removes code from your handlers. It does not automatically preserve a Winston output schema, and changing several records into one may require new dashboard queries.

## Migrate with observable checkpoints

Choose one operation and document the output fields consumers require. Configure an evlog [drain adapter](https://www.evlog.dev/integrate/adapters/overview), then exercise success and failure against a test destination. Confirm that redaction, severity mapping, retries, and process shutdown meet the same requirements as the existing path.

Keep Winston when format composition or custom transports already solve the problem. Consider evlog when accumulated context, structured errors, and its integration lifecycle reduce application code. Neither a library's age nor unequal-output benchmarks establish which is better for your workload. The [Performance reference](https://www.evlog.dev/reference/performance) explains the measurements to compare.

## FAQ

**Is evlog a drop-in replacement for Winston?**
No. Call signatures, levels and the output envelope differ. Plan the migration around the fields that dashboards and alerts depend on.

**Can I keep Winston and add evlog?**
Yes. A common split is to keep Winston for existing transports and adopt evlog request events on new routes, as long as both don't forward the same records to the same backend.

**Which one is faster?**
This page makes no performance claim. Compare both on your own workload with equivalent output, as described in the [Performance reference](https://www.evlog.dev/reference/performance).

Use the [logger overview](https://www.evlog.dev/reference/vs-other-loggers) to compare the other options before expanding a migration.

---

- [Logger overview](https://www.evlog.dev/reference/vs-other-loggers)


## Sitemap

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