Stability: 1.0 - Early Development
The node:logger module provides high-performance structured logging
capabilities for Node.js applications. It uses diagnostics_channel internally
to dispatch log events to consumers, allowing multiple consumers to receive
logs independently.
import { Logger, JSONConsumer } from 'node:logger';
const logger = new Logger({ level: 'info' });
const consumer = new JSONConsumer({ level: 'info' });
consumer.attach();
logger.info('Hello world');
// Outputs: {"level":"info","time":1234567890,"msg":"Hello world"}const { Logger, JSONConsumer } = require('node:logger');
const logger = new Logger({ level: 'info' });
const consumer = new JSONConsumer({ level: 'info' });
consumer.attach();
logger.info('Hello world');
// Outputs: {"level":"info","time":1234567890,"msg":"Hello world"}The logger supports the following log levels, in order of severity:
| Level | Value | Description |
|---|---|---|
trace |
10 | Detailed debugging information |
debug |
20 | Debug information |
info |
30 | General information |
warn |
40 | Warning messages |
error |
50 | Error messages |
fatal |
60 | Critical errors |
Log levels follow RFC 5424 numerical ordering.
The Logger class is used to create log records and publish them to
diagnostics_channel channels.
options{Object}level{string} Minimum log level. Default:'info'.bindings{Object} Context fields added to all log records. Default:{}.serializers{Object} Custom serializer functions for specific fields. Default:{}.
Creates a new Logger instance.
import { Logger } from 'node:logger';
const logger = new Logger({
level: 'debug',
bindings: { service: 'my-app', version: '1.0.0' },
});const { Logger } = require('node:logger');
const logger = new Logger({
level: 'debug',
bindings: { service: 'my-app', version: '1.0.0' },
});msg{string} Log message.obj{Object} Object containingmsgproperty and additional fields.error{Error} Error object to log.fields{Object} Additional fields to include in the log record.
Logs a message at the trace level.
logger.trace('Detailed trace message');
logger.trace('User action', { userId: 123, action: 'click' });
logger.trace({ msg: 'Object format', requestId: 'abc123' });msg{string} Log message.obj{Object} Object containingmsgproperty and additional fields.error{Error} Error object to log.fields{Object} Additional fields to include in the log record.
Logs a message at the debug level.
logger.debug('Debug information');
logger.debug('Processing request', { requestId: 'abc123' });msg{string} Log message.obj{Object} Object containingmsgproperty and additional fields.error{Error} Error object to log.fields{Object} Additional fields to include in the log record.
Logs a message at the info level.
logger.info('Server started');
logger.info('Request received', { method: 'GET', path: '/api/users' });
logger.info({ msg: 'User logged in', userId: 123 });msg{string} Log message.obj{Object} Object containingmsgproperty and additional fields.error{Error} Error object to log.fields{Object} Additional fields to include in the log record.
Logs a message at the warn level.
logger.warn('Deprecated API used');
logger.warn('High memory usage', { memoryUsage: process.memoryUsage() });msg{string} Log message.obj{Object} Object containingmsgproperty and additional fields.error{Error} Error object to log.fields{Object} Additional fields to include in the log record.
Logs a message at the error level.
logger.error('Database connection failed');
logger.error(new Error('Something went wrong'));
logger.error(new Error('Request failed'), { requestId: 'abc123' });msg{string} Log message.obj{Object} Object containingmsgproperty and additional fields.error{Error} Error object to log.fields{Object} Additional fields to include in the log record.
Logs a message at the fatal level.
logger.fatal('Application crash');
logger.fatal(new Error('Unrecoverable error'));bindings{Object} Additional context fields for the child logger.options{Object}level{string} Log level for the child logger.serializers{Object} Additional serializers for the child logger.
- Returns: {Logger} A new child logger instance.
Creates a child logger with additional context bindings. Child loggers inherit the parent's configuration and add their own bindings to all log records.
Note for library authors: The
leveloption inchild()is intended for application code only. Library and module authors should NOT override the log level in child loggers. Instead, libraries should inherit the parent logger's level to respect the application developer's log level configuration. Application developers can use this feature to isolate specific components or adjust verbosity for particular subsystems they directly control.
import { Logger } from 'node:logger';
const logger = new Logger({ bindings: { service: 'my-app' } });
const requestLogger = logger.child({ requestId: 'abc123' });
requestLogger.info('Processing request');
// Log includes: service: 'my-app', requestId: 'abc123'const { Logger } = require('node:logger');
const logger = new Logger({ bindings: { service: 'my-app' } });
const requestLogger = logger.child({ requestId: 'abc123' });
requestLogger.info('Processing request');
// Log includes: service: 'my-app', requestId: 'abc123'- {boolean}
trueif the level is enabled,falseotherwise.
Each log method (trace, debug, info, warn, error, fatal) has an
enabled property that indicates whether that level is enabled for this logger.
Use this to check if a level is enabled before performing expensive computations:
if (logger.debug.enabled) {
// Perform expensive debug computation only if debug is enabled
logger.debug('Debug info', { expensiveData: computeDebugData() });
}
// Typos will throw a TypeError (safer than silent failure)
// logger.debg.enabled → TypeError: Cannot read properties of undefinedFor dynamic level checks, use property access:
const level = config.logLevel; // 'info', 'debug', etc.
if (logger[level]?.enabled) {
logger[level]('Dynamic log message');
}The LogConsumer class is the base class for log consumers. Consumers
subscribe to diagnostics_channel events and process log records.
options{Object}level{string} Minimum log level to consume. Default:'info'.
Creates a new LogConsumer instance.
Attaches the consumer to log channels. After calling this method, the consumer will receive log records from all loggers.
const consumer = new JSONConsumer({ level: 'info' });
consumer.attach();
// Consumer now receives all log records at 'info' level and above- {boolean}
trueif the level is enabled,falseotherwise.
Each log level (trace, debug, info, warn, error, fatal) has an
enabled property that indicates whether that level is enabled for this
consumer.
const { LogConsumer } = require('node:logger');
const consumer = new LogConsumer({ level: 'info' });
console.log(consumer.debug.enabled); // false (below threshold)
console.log(consumer.info.enabled); // true
console.log(consumer.error.enabled); // true
// Typos will throw a TypeError (safer than silent failure)
// consumer.debg.enabled → TypeError: Cannot read properties of undefinedrecord{Object} The log record to handle.level{string} Log level.msg{string} Log message.time{number} Timestamp in milliseconds.bindingsStr{string} Pre-serialized bindings JSON string.fields{Object} Additional log fields.
Handles a log record. Subclasses must implement this method.
import { LogConsumer } from 'node:logger';
class CustomConsumer extends LogConsumer {
handle(record) {
console.log(`[${record.level}] ${record.msg}`);
}
}const { LogConsumer } = require('node:logger');
class CustomConsumer extends LogConsumer {
handle(record) {
console.log(`[${record.level}] ${record.msg}`);
}
}- Extends: {LogConsumer}
The JSONConsumer class outputs log records as JSON to a stream.
options{Object}level{string} Minimum log level to consume. Default:'info'.stream{number|string|Object} Output destination. Can be a file descriptor (number), file path (string), or a writable stream object. Default:stdout(fd 1).fields{Object} Additional fields to include in every log record. Default:{}.
Creates a new JSONConsumer instance.
import { JSONConsumer } from 'node:logger';
// Output to stdout (default)
const consumer1 = new JSONConsumer({ level: 'info' });
// Output to a file
const consumer2 = new JSONConsumer({
level: 'debug',
stream: '/var/log/app.log',
});
// Output to stderr
const consumer3 = new JSONConsumer({
level: 'error',
stream: 2,
});
// Add fields to every log
const consumer4 = new JSONConsumer({
level: 'info',
fields: { hostname: 'server-1', env: 'production' },
});const { JSONConsumer } = require('node:logger');
// Output to stdout (default)
const consumer1 = new JSONConsumer({ level: 'info' });
// Output to a file
const consumer2 = new JSONConsumer({
level: 'debug',
stream: '/var/log/app.log',
});callback{Function} Called when flush completes.
Flushes pending writes to the underlying stream.
consumer.flush(() => {
console.log('All logs flushed');
});Flushes pending writes synchronously.
consumer.flushSync();Closes the consumer and its underlying stream.
consumer.end();- {Object}
An object containing standard serializer functions for common objects.
error{Error} Error object to serialize.- Returns: {Object} Serialized error object.
Serializes an Error object for logging. Includes type, message, stack,
and any additional properties. Recursively serializes cause if present.
import { Logger, stdSerializers } from 'node:logger';
const logger = new Logger({
serializers: {
err: stdSerializers.err,
},
});const { Logger, stdSerializers } = require('node:logger');
const logger = new Logger({
serializers: {
err: stdSerializers.err,
},
});request{http.IncomingMessage} HTTP request object.- Returns: {Object} Serialized request object with
method,url,headers,remoteAddress, andremotePort.
Serializes an HTTP request object for logging.
const http = require('node:http');
const { Logger, JSONConsumer, stdSerializers } = require('node:logger');
const logger = new Logger({
serializers: {
req: stdSerializers.req,
},
});
const consumer = new JSONConsumer();
consumer.attach();
http.createServer((req, res) => {
logger.info('Request received', { req });
res.end('OK');
}).listen(3000);response{http.ServerResponse} HTTP response object.- Returns: {Object} Serialized response object with
statusCodeandheaders.
Serializes an HTTP response object for logging.
logger.info('Response sent', { res });- {Object}
An object mapping log level names to their numeric values.
const { LEVELS } = require('node:logger');
console.log(LEVELS);
// { trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60 }- {Object}
An object containing diagnostics_channel instances for each log level.
Advanced users can subscribe directly to these channels.
const { channels } = require('node:logger');
channels.info.subscribe((record) => {
// Custom handling of info-level logs
});- {symbol}
A symbol that objects can implement to define custom serialization behavior
for logging. Similar to util.inspect.custom.
When an object with a [serialize]() method is logged, the logger will call
that method instead of serializing the object directly. This allows objects
to control which properties are included in logs, filtering out sensitive
data like passwords or tokens.
import { Logger, JSONConsumer, serialize } from 'node:logger';
class User {
constructor(id, name, password) {
this.id = id;
this.name = name;
this.password = password; // Sensitive!
}
// Define custom serialization
[serialize]() {
return {
id: this.id,
name: this.name,
// password is excluded
};
}
}
const consumer = new JSONConsumer();
consumer.attach();
const logger = new Logger();
const user = new User(1, 'Alice', 'secret123');
logger.info({ msg: 'User logged in', user });
// Output: {"level":"info","time":...,"msg":"User logged in","user":{"id":1,"name":"Alice"}}
// Note: password is not included in the outputconst { Logger, JSONConsumer, serialize } = require('node:logger');
class DatabaseConnection {
constructor(host, user, password) {
this.host = host;
this.user = user;
this.password = password;
}
[serialize]() {
return {
host: this.host,
user: this.user,
connected: this.isConnected,
// password is excluded
};
}
}The serialize symbol takes precedence over field-specific serializers.
If an object has both a [serialize]() method and a matching serializer
in the logger's serializers option, the [serialize]() method will be used.
import { Logger, JSONConsumer } from 'node:logger';
// Create a logger
const logger = new Logger({ level: 'info' });
// Create and attach a consumer
const consumer = new JSONConsumer({ level: 'info' });
consumer.attach();
// Log messages
logger.info('Application started');
logger.info('User logged in', { userId: 123 });
logger.error(new Error('Something went wrong'));import { Logger, JSONConsumer } from 'node:logger';
import { randomUUID } from 'node:crypto';
const logger = new Logger({
bindings: { service: 'api-server' },
});
const consumer = new JSONConsumer();
consumer.attach();
function handleRequest(req, res) {
const requestLogger = logger.child({
requestId: randomUUID(),
method: req.method,
path: req.url,
});
requestLogger.info('Request started');
// ... handle request ...
requestLogger.info('Request completed', { statusCode: res.statusCode });
}import { Logger, JSONConsumer } from 'node:logger';
const logger = new Logger({ level: 'trace' });
// Console output for development (info and above)
const consoleConsumer = new JSONConsumer({ level: 'info' });
consoleConsumer.attach();
// File output for debugging (all levels)
const fileConsumer = new JSONConsumer({
level: 'trace',
stream: '/var/log/app-debug.log',
});
fileConsumer.attach();
// Error file (errors only)
const errorConsumer = new JSONConsumer({
level: 'error',
stream: '/var/log/app-error.log',
});
errorConsumer.attach();import { Logger, JSONConsumer, stdSerializers } from 'node:logger';
const logger = new Logger({
serializers: {
err: stdSerializers.err,
req: stdSerializers.req,
res: stdSerializers.res,
user: (user) => ({ id: user.id, email: user.email }), // Custom serializer
},
});
const consumer = new JSONConsumer();
consumer.attach();
logger.info('User action', {
user: { id: 1, email: 'user@example.com', password: 'secret' },
});
// Output will not include password due to custom serializerimport { LogConsumer } from 'node:logger';
import { styleText } from 'node:util';
const levelStyles = {
trace: 'gray',
debug: 'cyan',
info: 'green',
warn: 'yellow',
error: 'red',
fatal: 'magenta',
};
class ConsoleColorConsumer extends LogConsumer {
handle(record) {
const style = levelStyles[record.level] ?? 'white';
const label = styleText(style, `[${record.level.toUpperCase()}]`);
console.log(`${label} ${record.msg}`);
}
}
const consumer = new ConsoleColorConsumer({ level: 'debug' });
consumer.attach();