Create specialized logger instances with additional context and configure custom serialization for complex objects like HTTP requests, responses, and errors.
Create child loggers that inherit parent configuration while adding specific contextual fields.
class Logger {
/**
* Create a child logger with additional fields and optional configuration
* @param options - Additional fields and configuration options
* @param simple - Use simple child creation (performance optimization)
* @returns New child logger instance
*/
child(options: ChildOptions, simple?: boolean): Logger;
}
interface ChildOptions {
[key: string]: any; // Additional fields for all log records
level?: string | number; // Override parent level
serializers?: Serializers; // Additional or override serializers
}Add and configure custom serializers for converting complex objects to JSON-safe representations.
class Logger {
/**
* Add custom serializers to the logger
* @param serializers - Object mapping field names to serializer functions
*/
addSerializers(serializers: Serializers): void;
}
interface Serializers {
[fieldName: string]: SerializerFunction;
}
type SerializerFunction = (obj: any) => any;Built-in serializers for common objects like HTTP requests, responses, and errors.
// Access to standard serializers
const stdSerializers: {
req: SerializerFunction; // HTTP request serializer
res: SerializerFunction; // HTTP response serializer
err: SerializerFunction; // Error object serializer
};
/**
* HTTP request serializer - extracts safe request information
* @param req - HTTP request object
* @returns Serialized request data
*/
function reqSerializer(req: any): {
method: string;
url: string;
headers: object;
remoteAddress: string;
remotePort: number;
} | any;
/**
* HTTP response serializer - extracts safe response information
* @param res - HTTP response object
* @returns Serialized response data
*/
function resSerializer(res: any): {
statusCode: number;
header: string;
} | any;
/**
* Error serializer - safely serializes Error objects with stack traces
* @param err - Error object
* @returns Serialized error data
*/
function errSerializer(err: Error): {
message: string;
name: string;
stack: string;
code?: string;
signal?: string;
} | any;Utilities for handling circular references and non-serializable objects.
/**
* Safe JSON stringification that handles circular references
* @returns Replacer function for JSON.stringify
*/
function safeCycles(): (key: string, value: any) => any;Usage Examples:
const bunyan = require('bunyan');
// Create parent logger
const log = bunyan.createLogger({
name: 'parent-app',
serializers: bunyan.stdSerializers
});
// Create child logger with request context
const reqLog = log.child({
req_id: '12345-67890',
user_id: 'alice',
component: 'auth'
});
// Child inherits parent configuration but adds context
reqLog.info('Processing authentication');
// Output: {"name":"parent-app","req_id":"12345-67890","user_id":"alice","component":"auth","level":30,"msg":"Processing authentication",...}
// Create child with level override
const debugChild = log.child({
component: 'database'
}, true); // simple=true for performance
debugChild.level('debug');
debugChild.debug('Database query executed');
// Child with additional serializers
const apiLog = log.child({
component: 'api',
serializers: {
body: function(body) {
// Custom serializer for request body
if (body && body.password) {
const safe = Object.assign({}, body);
safe.password = '[REDACTED]';
return safe;
}
return body;
}
}
});
// Using standard serializers
const express = require('express');
const app = express();
app.use((req, res, next) => {
req.log = log.child({req_id: generateId()});
req.log.info({req: req}, 'Request started');
next();
});
app.get('/users/:id', (req, res) => {
req.log.info('Fetching user');
try {
const user = getUserById(req.params.id);
req.log.info({res: res}, 'Request completed successfully');
res.json(user);
} catch (err) {
req.log.error({err: err}, 'Request failed');
res.status(500).json({error: 'Internal server error'});
}
});
// Custom serializers for domain objects
log.addSerializers({
user: function(user) {
return {
id: user.id,
username: user.username,
// Don't log sensitive fields like passwords
role: user.role
};
},
query: function(query) {
return {
sql: query.sql,
duration: query.duration,
// Redact potentially sensitive query parameters
params: query.params ? '[REDACTED]' : undefined
};
}
});
// Use custom serializers
log.info({user: currentUser, query: dbQuery}, 'User operation completed');Child loggers inherit from their parents:
const parentLog = bunyan.createLogger({
name: 'parent',
level: 'info',
serializers: {custom: customSerializer}
});
const childLog = parentLog.child({
component: 'auth',
serializers: {
user: userSerializer // Adds to parent serializers
}
});
// Child has access to both 'custom' and 'user' serializers
childLog.info({custom: obj1, user: obj2}, 'Message');Always sanitize sensitive data in custom serializers:
const sensitiveSerializer = {
user: function(user) {
return {
id: user.id,
username: user.username,
// Never log passwords, tokens, or other secrets
email: user.email ? user.email.replace(/(.{2})[^@]*(@.*)/, '$1***$2') : undefined
};
},
req: function(req) {
const safe = bunyan.stdSerializers.req(req);
// Remove sensitive headers
if (safe.headers) {
delete safe.headers.authorization;
delete safe.headers.cookie;
}
return safe;
}
};simple=true for child loggers when possible for better performance// Performance-optimized child creation
const fastChild = log.child({req_id: id}, true);Serializers should handle errors gracefully to avoid breaking logging:
const robustSerializer = {
complexObject: function(obj) {
try {
return {
id: obj.id,
status: obj.getStatus(),
data: obj.safeData()
};
} catch (err) {
// Return safe fallback if serialization fails
return {
id: obj.id || 'unknown',
error: 'Serialization failed'
};
}
}
};