The message formatting system provides flexible control over how log messages are displayed. It supports both template-based formatting with token substitution and custom function-based formatting.
Configure custom message formatting.
/**
* Custom message format
* - false: use default format (just the message)
* - string: template with {tokens} and conditional blocks
* - function: custom formatter function
* @default false
*/
messageFormat?: false | string | MessageFormatFunc;
/**
* Custom message format function
* @param log - The complete log object
* @param messageKey - The key containing the message
* @param levelLabel - The level label string (e.g., "INFO", "ERROR")
* @param extras - Additional context including colors
* @returns Formatted message string
*/
type MessageFormatFunc = (
log: Record<string, unknown>,
messageKey: string,
levelLabel: string,
extras: PrettifierExtras
) => string;
interface PrettifierExtras {
colors: Colorette;
}Use {property} tokens to reference log properties.
/**
* Template tokens:
* - {property} - Access top-level properties
* - {nested.property} - Access nested properties with dot notation
* - {levelLabel} - Special token for level label
* - {msg} - Message property (configurable via messageKey)
*/Usage Examples:
const pinoPretty = require('pino-pretty');
// Basic template
const stream1 = pinoPretty({
messageFormat: '{levelLabel} - {msg}'
});
// Input: { level: 30, msg: 'hello' }
// Output: INFO - hello
// Include PID
const stream2 = pinoPretty({
messageFormat: '{levelLabel} - {pid} - {msg}'
});
// Input: { level: 30, pid: 1234, msg: 'hello' }
// Output: INFO - 1234 - hello
// Nested properties
const stream3 = pinoPretty({
messageFormat: '{levelLabel} - url:{req.url} - {msg}'
});
// Input: { level: 30, req: { url: '/api/users' }, msg: 'Request' }
// Output: INFO - url:/api/users - Request
// Custom label token
const stream4 = pinoPretty({
levelLabel: 'severity',
messageFormat: '{severity}: {msg}'
});Use {if property}...{end} blocks for conditional output.
/**
* Conditional syntax:
* {if property}content{end} - Show content if property exists
* Nested conditions are not supported
* Else statements are not supported
*/Usage Examples:
// Conditional PID
pinoPretty({
messageFormat: '{levelLabel} - {if pid}{pid} - {end}{msg}'
});
// With pid: INFO - 1234 - hello
// Without pid: INFO - hello
// Multiple conditionals
pinoPretty({
messageFormat: '{levelLabel}{if pid} [{pid}]{end}{if hostname} @{hostname}{end} - {msg}'
});
// Full: INFO [1234] @server1 - hello
// Partial: INFO [1234] - hello
// Minimal: INFO - hello
// Conditional nested property
pinoPretty({
messageFormat: '{if req.id}[{req.id}] {end}{msg}'
});
// With req.id: [abc123] hello
// Without: hello
// Conditional with text
pinoPretty({
messageFormat: '{levelLabel}{if userId} (user:{userId}){end} - {msg}'
});
// With userId: INFO (user:42) - hello
// Without: INFO - helloReal-world message format templates.
// HTTP request logging
messageFormat: '{levelLabel} {method} {url}{if statusCode} -> {statusCode}{end}{if duration} ({duration}ms){end}'
// Output: INFO GET /api/users -> 200 (45ms)
// Service logging
messageFormat: '[{levelLabel}]{if service} {service}{end}{if requestId} [{requestId}]{end} {msg}'
// Output: [INFO] auth-service [abc-123] User logged in
// Database logging
messageFormat: '{levelLabel} - {operation}{if table} on {table}{end}{if duration} - {duration}ms{end}: {msg}'
// Output: INFO - SELECT on users - 12ms: Fetched user records
// Error logging
messageFormat: '{levelLabel}{if errorCode} ({errorCode}){end}{if userId} [user:{userId}]{end} - {msg}'
// Output: ERROR (AUTH_FAILED) [user:123] - Invalid credentialsImplement custom message formatting logic.
/**
* Custom message format function
* @param log - Complete log object
* @param messageKey - Key for message (usually 'msg')
* @param levelLabel - Level label string
* @param extras - Colors and other context
* @returns Formatted message string
*/
type MessageFormatFunc = (
log: Record<string, unknown>,
messageKey: string,
levelLabel: string,
extras: { colors: Colorette }
) => string;Usage Examples:
const pinoPretty = require('pino-pretty');
// Simple function
const stream1 = pinoPretty({
messageFormat: (log, messageKey) => {
const msg = log[messageKey];
return `>> ${msg}`;
}
});
// With level
const stream2 = pinoPretty({
messageFormat: (log, messageKey, levelLabel) => {
return `[${levelLabel}] ${log[messageKey]}`;
}
});
// With colors
const stream3 = pinoPretty({
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
return colors.bold(`${levelLabel}: `) + msg;
}
});
// Conditional logic
const stream4 = pinoPretty({
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
if (log.requestId) {
return colors.cyan(`[${log.requestId}] `) + msg;
}
return msg;
}
});
// Complex formatting
const stream5 = pinoPretty({
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
const parts = [levelLabel];
if (log.service) {
parts.push(colors.blue(log.service));
}
if (log.requestId) {
parts.push(colors.gray(`[${log.requestId}]`));
}
return parts.join(' ') + ': ' + msg;
}
});
// Output: INFO auth-service [abc-123]: User logged inFormat HTTP request logs.
const stream = pinoPretty({
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
// HTTP request format
if (log.req && log.res) {
const method = log.req.method || 'GET';
const url = log.req.url || '/';
const status = log.res.statusCode || '???';
const duration = log.responseTime || log.duration;
// Color status code
let statusColor;
if (status >= 500) statusColor = colors.red;
else if (status >= 400) statusColor = colors.yellow;
else if (status >= 300) statusColor = colors.cyan;
else statusColor = colors.green;
const parts = [
colors.bold(method),
url,
'->',
statusColor(status)
];
if (duration) {
parts.push(colors.gray(`(${duration}ms)`));
}
return parts.join(' ') + (msg ? ` - ${msg}` : '');
}
return msg;
}
});
// Output: GET /api/users -> 200 (45ms) - Request successfulAdd contextual information to error messages.
const stream = pinoPretty({
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
// Error format with context
if (log.err || log.error) {
const error = log.err || log.error;
const errorMsg = error.message || msg;
const errorType = error.type || error.name || 'Error';
const parts = [colors.red(colors.bold(errorType))];
if (log.errorCode) {
parts.push(colors.yellow(`[${log.errorCode}]`));
}
if (log.userId) {
parts.push(colors.gray(`(user:${log.userId})`));
}
return parts.join(' ') + ': ' + errorMsg;
}
return msg;
}
});
// Output: AuthenticationError [AUTH_001] (user:123): Invalid credentialsFormat logs with structured metadata.
const stream = pinoPretty({
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
const parts = [];
// Add level with color
const levelColors = {
TRACE: colors.gray,
DEBUG: colors.blue,
INFO: colors.green,
WARN: colors.yellow,
ERROR: colors.red,
FATAL: colors.bgRed
};
const colorize = levelColors[levelLabel] || colors.white;
parts.push(colorize(levelLabel));
// Add service
if (log.service) {
parts.push(colors.blue(`[${log.service}]`));
}
// Add trace ID
if (log.traceId) {
parts.push(colors.gray(`trace:${log.traceId.slice(0, 8)}`));
}
// Add user context
if (log.userId) {
parts.push(colors.magenta(`user:${log.userId}`));
}
return parts.join(' ') + ' - ' + msg;
}
});
// Output: INFO [auth-service] trace:a1b2c3d4 user:42 - User logged inWhen messageFormat is false (default).
/**
* Default format:
* [time] LEVEL (metadata): message
* object properties...
*
* Structure:
* - Time (if translated)
* - Level (colored if colorize enabled)
* - Metadata (pid, hostname, name, etc. unless ignored)
* - Message
* - Additional object properties
*/Default Output Examples:
// Basic log
{ level: 30, time: 1522431328992, msg: 'hello' }
// Output: [10:30:45] INFO: hello
// With metadata
{ level: 30, time: 1522431328992, msg: 'hello', pid: 1234, hostname: 'server1' }
// Output: [10:30:45] INFO (1234 on server1): hello
// With objects
{ level: 30, msg: 'User login', userId: 42, action: 'login' }
// Output: [10:30:45] INFO: User login
// userId: 42
// action: "login"
// Error with stack
{ level: 50, msg: 'Error occurred', err: { type: 'Error', message: 'Failed', stack: '...' } }
// Output: [10:30:45] ERROR: Error occurred
// Error: Failed
// <stack trace>Keep message formatting efficient.
/**
* Best practices:
* - Keep template strings simple
* - Avoid complex computations in functions
* - Cache color functions if used repeatedly
* - Return strings directly, avoid string building
*/Examples:
// Good: Simple and fast
messageFormat: '{levelLabel} - {msg}'
// Good: Efficient function
messageFormat: (log, messageKey) => {
return `[${log.service || 'app'}] ${log[messageKey]}`;
}
// Bad: Expensive computation
messageFormat: (log, messageKey) => {
// Don't do heavy processing here
const processed = expensiveOperation(log);
return processed + log[messageKey];
}Handle missing properties gracefully.
/**
* Handle missing or invalid properties:
* - Check property existence before use
* - Provide fallback values
* - Avoid throwing errors
*/Examples:
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey] || '(no message)';
// Safe property access
const userId = log.userId || log.user?.id || 'unknown';
const requestId = log.requestId || log.req?.id;
const parts = [levelLabel];
if (requestId) {
parts.push(colors.cyan(`[${requestId}]`));
}
return parts.join(' ') + ` (user:${userId}) - ${msg}`;
}Use colors effectively in custom formats.
/**
* Color usage tips:
* - Use colors.bold for emphasis
* - Use colors.dim/gray for metadata
* - Use semantic colors (red for errors, green for success)
* - Don't overuse colors - keep it readable
*/Examples:
messageFormat: (log, messageKey, levelLabel, { colors }) => {
const msg = log[messageKey];
// Semantic coloring
const levelColor =
levelLabel === 'ERROR' ? colors.red :
levelLabel === 'WARN' ? colors.yellow :
levelLabel === 'INFO' ? colors.green :
colors.white;
// Emphasis on important parts
const level = colors.bold(levelColor(levelLabel));
// Dim/gray for metadata
const metadata = log.requestId
? colors.gray(` [${log.requestId}]`)
: '';
return `${level}${metadata} - ${msg}`;
}