To correlate Express errors across tenant cohorts, make sure errors reach Express error middleware, attach validated request and tenant context to structured log records, and use trace context for work that crosses services. Pino and Winston both support metadata-bearing child loggers; neither replaces Express error handling, and the available documentation does not establish that one is categorically better or faster.
Keep error handling, request context, and log metadata separate
These are related but distinct jobs. Express must receive an error for error middleware to handle it. Request context identifies the HTTP request and any approved tenant field. A child logger can attach those fields to each record, while distributed trace context links records to work in other services.
As an Amazon Associate I earn from qualifying purchases.
A log line containing a tenant key does not prove the error was handled, and a trace ID does not identify or authorize a tenant. Design and verify each path independently.
Check how your Express version forwards errors
Express 5
The Express 5 guide says promise-returning route handlers and middleware automatically call next(value) when they reject or throw. This includes the usual async-handler path described by the guide. See Express 5 error handling.
#1 Best Overall
Express 4
The Express 4 guide says rejected promises, including those from async functions, are not passed to next automatically. Forward asynchronous failures explicitly, or use a wrapper whose behavior you have verified. Check the installed Express major version and any handler wrappers rather than assuming every part of an application behaves alike. See Express 4 error handling.
Do not send a second response after headers are sent
Express places its default error handler last in the middleware stack. If a custom error handler receives an error after response headers have been sent, delegate with next(err); the default handler closes the connection in that case. Do not attempt to write a second response after a stream or other response has started.
Rank #2
Choose controlled fields for cohort analysis
Use a stable, application-controlled schema so records can be searched and grouped without mixing identifiers that serve different purposes. One practical starting point is:
request_id: identifies one inbound request.tenant_key: a tenant identifier approved for logging, or an approved pseudonymous token.trace_idandspan_id: connect a record to distributed trace work when available.error_class: a controlled error category useful for grouping.route: a route template rather than an arbitrary raw URL where practical.service_version: identifies the service or deployment version that emitted the record.
This is a practical schema, not a standard mandated by the cited documentation. Decide which tenant representation is appropriate for your application, who may query it, and how long records are retained. Avoid logging credentials, request bodies, or unnecessary unbounded user-controlled values. The sources do not prescribe a universal tenant identifier format, retention interval, cohort threshold, or privacy policy.
Attach metadata with Pino or Winston
Both libraries document child loggers as a way to bind metadata to log records. Create or obtain the logger with request context after validating that context, then pass it to code that may report failures. This keeps the metadata mechanism independent of the logic that forwards errors to Express.
| Need | Pino | Winston |
|---|---|---|
| Request-scoped metadata | logger.child(bindings) emits key-value bindings on each child log line. The pino-http documentation describes req.log and request-ID generation. |
logger.child({ requestId: ... }) creates a child logger with metadata, as shown in the project README. |
| Context for code without direct request access | Can be paired with request-scoped loggers or Node.js AsyncLocalStorage; verify context behavior across integrations and boundaries. |
Child loggers can carry metadata; the cited README does not prescribe an AsyncLocalStorage integration. |
| Cross-service correlation | OpenTelemetry can add trace and span context to logs. | OpenTelemetry context propagation provides the same correlation goal; verify any instrumentation package and its version before relying on its behavior. |
| Documented field-safety caution | Pino warns that untrusted top-level binding keys can collide with logger, application, or security fields. | The reviewed Winston README does not establish equivalent field-safety behavior, so it is not evidence that the risk is absent. |
Pino: keep bindings controlled
Pino child bindings are emitted on every line from the child logger. Do not pass an externally supplied object directly as bindings: untrusted top-level keys can collide with Pino fields or application and security fields. Keep the binding schema under application control; if untrusted data is needed, place it below a controlled namespace and sanitize or redact it, or omit it. See the Pino API documentation and Pino help documentation.
Winston: use child metadata without treating it as an isolation design
Winston documents child loggers with metadata, including a request-ID example. That supports request-scoped logging, but the example is not a complete tenant-isolation design. Define the allowed fields and access policy in your application. See the Winston README.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Carry request context through asynchronous work
If a function can receive a request-scoped logger directly, passing it explicitly makes its dependency visible. For deeper asynchronous call chains where passing arguments everywhere is impractical, Node.js AsyncLocalStorage can associate state with callbacks and promise chains. Node.js documents an HTTP example that associates an ID with each request and includes it in log messages. This is a context-propagation option, not a guarantee that every third-party callback, worker, or other boundary retains context without integration. See Node.js asynchronous context tracking.
Best Value
For a Pino HTTP setup, the project documents Express middleware, req.log, a configurable request-ID generator, and customProps; its example notes that Express request-scoped data can be placed in res.locals. Decide whether incoming client-supplied request IDs are accepted, validated, or replaced according to your threat model. See the pino-http README.
Use trace context for cross-service work
OpenTelemetry context propagation correlates signals across services and can inject trace and span IDs into log records. A trace ID answers which distributed operation a record belongs to; a tenant key answers which tenant the application associates with the request. Keep these fields distinct, and enforce tenant access controls in the application and log store. Correlation mechanics do not themselves provide tenant-level authorization. See OpenTelemetry context propagation.
Validate the complete error-to-record path
- Confirm the runtime path. Check the installed Express major version and determine whether each route or middleware forwards rejected promises to error middleware automatically or explicitly.
- Establish request context. Create or validate a request ID and resolve the tenant field from trusted application context, not from an unchecked logging payload.
- Make context available where errors are logged. Pass a child logger explicitly, or use
AsyncLocalStoragewhere appropriate and verify relevant integrations. - Emit structured error records. Include the approved request, tenant, route, error, and trace fields; keep untrusted data out of top-level logger bindings.
- Test error cases and searches. Exercise rejected asynchronous handlers and errors after a response has started. Confirm the expected error middleware runs and that records can be filtered by the approved tenant field and, when present, trace ID.
Pino and Winston documentation is not pinned here to a specific release. Check the current API and middleware behavior for the versions your application uses; the cited documentation does not provide a controlled performance comparison between the two libraries. Pino also documents asynchronous logging options, but that does not establish an advantage for tenant correlation itself: Pino asynchronous logging.
Recommended Free Tools
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




