@nextrush/logger
Request logging middleware with correlation IDs and structured output.
HTTP requests leave no trace by default. When a production issue hits, you need structured logs with correlation IDs to track requests across services and find the failure point.
The logger middleware solves this: structured request/response logging, automatic correlation ID tracking, and a request-scoped logger attached to every context as ctx.log.
Default Behavior
With default options, logger():
- Logs request completion with method, path, status code, and duration
- Generates a UUID correlation ID per request (via
crypto.randomUUID()) - Sets the correlation ID in the
x-request-idresponse header - Attaches a scoped logger to
ctx.logwith the correlation ID - Logs request start in development only (based on
isProductionBuild()) - Uses
infolevel for 2xx/3xx,warnfor 4xx,errorfor 5xx responses
Installation
$ pnpm add @nextrush/logger
This package wraps @nextrush/log and re-exports all its functionality.
Minimal Usage
Register the logger middleware before route handlers. It must run first to attach ctx.log and
track request timing.
import { createApp } from '@nextrush/core';
import { logger } from '@nextrush/logger';
const app = createApp();
app.use(logger());
app.use(async (ctx) => {
ctx.log.info('Processing request');
ctx.json({ ok: true });
});The middleware attaches ctx.log — a request-scoped logger that includes the correlation ID in every log entry.
Configuration Options
LoggerMiddlewareOptions controls the middleware behavior. It extends LoggerOptions from @nextrush/log.
Middleware Options
| Property | Type | Description |
|---|---|---|
skip? | (ctx: Context) => boolean | Skip logging for matching requests |
formatMessage? | (ctx: Context, duration: number) => string= `${method} ${path}` | Custom log message formatter |
successLevel? | LogLevel= "info" | Log level for 2xx/3xx responses |
clientErrorLevel? | LogLevel= "warn" | Log level for 4xx responses |
serverErrorLevel? | LogLevel= "error" | Log level for 5xx responses |
logRequestStart? | boolean= true in dev, false in production | Log when request starts |
correlationIdHeader? | string= "x-request-id" | Header name for correlation ID |
generateCorrelationId? | boolean= true | Generate ID if not present in headers |
context? | string= "nextrush" | Logger namespace prefix |
Inherited from LoggerOptions
| Property | Type | Description |
|---|---|---|
minLevel? | LogLevel | Minimum log level to output |
silent? | boolean= false | Suppress console output (transports still fire) |
pretty? | boolean | Force pretty printing regardless of environment |
colors? | boolean | Enable/disable terminal colors |
redact? | boolean= true in production | Redact sensitive keys in log output |
sensitiveKeys? | string[] | Additional keys to redact (merged with defaults) |
transports? | LogTransport[] | Custom log transports |
timestamps? | boolean | Include timestamps in output |
metadata? | LogContext | Additional metadata for all log entries |
samplingRate? | number | Sampling rate for debug logs in production (0–1) |
env? | 'development' | 'test' | 'production' | Environment preset |
app.use(
logger({
minLevel: 'info',
skip: (ctx) => ctx.path === '/health',
logRequestStart: false,
correlationIdHeader: 'x-request-id',
context: 'api',
})
);Integration Example
Correlation IDs Across Services
Track requests through microservices using the correlation ID:
import { logger } from '@nextrush/logger';
import type { LoggerContext } from '@nextrush/logger';
app.use(logger());
app.use(async (ctx) => {
const correlationId = (ctx as LoggerContext).log.getCorrelationId();
const response = await fetch('https://api.internal/data', {
headers: { 'x-request-id': correlationId ?? '' },
});
ctx.json(await response.json());
});Do not use ctx.get('x-request-id') to retrieve a generated correlation ID. The middleware sets
it on the response via ctx.set(). Use ctx.log.getCorrelationId() instead.
Logger Without Request Logging
Use attachLogger() when you need ctx.log but not automatic request/response logging:
import { attachLogger } from '@nextrush/logger';
app.use(attachLogger({ context: 'api' }));
app.use(async (ctx) => {
ctx.log.info('Handler called');
ctx.json({ ok: true });
});Type Guards
import { hasLogger, getLogger } from '@nextrush/logger';
app.use(async (ctx) => {
if (hasLogger(ctx)) {
ctx.log.info('Logger available');
}
// Get logger with automatic fallback
const log = getLogger(ctx, 'fallback-context');
log.info('Works with or without middleware');
});Standalone Logger
Create loggers outside of middleware context:
import { createLogger } from '@nextrush/logger';
const log = createLogger('MyService');
log.info('Server starting');
log.error('Connection failed', new Error('timeout'));Common Mistakes
Placing logger middleware after route handlers. The middleware must run before handlers to attach ctx.log. Register it early in the middleware chain.
Skipping health checks without the skip option. Health check endpoints generate high-volume logs with no diagnostic value. Use skip: (ctx) => ctx.path === '/health'.
Using redactKeys instead of sensitiveKeys. The option inherited from @nextrush/log is sensitiveKeys, not redactKeys.
Troubleshooting
ctx.log is undefined — The logger middleware did not run before your handler. Ensure app.use(logger()) is registered before route handlers. If using skip, verify your route is not being skipped.
Correlation ID not appearing in logs — Set generateCorrelationId: true (the default). If using a custom header name, ensure correlationIdHeader matches the header your upstream service sends.
Too many logs in production — Set minLevel: 'info' and logRequestStart: false to reduce volume. Use skip to exclude health checks and static asset paths.
Related
- Plugins Overview — All plugin packages
- @nextrush/request-id — Request ID middleware
- @nextrush/timer — Response timing