一键导入
pino-logging-setup
Official Pino logging best practices - structured JSON logging with child loggers, serializers, transports, and request-scoped logging for production applications.
菜单
Official Pino logging best practices - structured JSON logging with child loggers, serializers, transports, and request-scoped logging for production applications.
| name | pino-logging-setup |
| description | Official Pino logging best practices - structured JSON logging with child loggers, serializers, transports, and request-scoped logging for production applications. |
Pino is a super-fast, all-natural JSON logger for Node.js applications. This skill provides official guidance from the Pino team for production-grade structured logging.
pino-pretty only in development (performance overhead)req.log pattern in HTTP frameworks for request-scoped logging// ✅ Correct - object first, message second
logger.info({ user: userId, action: 'login' }, 'User logged in')
// ❌ Wrong - message first (Winston style)
logger.info('User logged in', { user: userId })
const pino = require('pino')
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
// Redact sensitive fields
redact: {
paths: [
'password',
'token',
'apiKey',
'authorization',
'creditCard',
'*.password',
'*.token'
],
censor: '[REDACTED]'
},
// Custom serializers
serializers: {
user: (user) => ({
id: user.id,
username: user.username,
role: user.role
// Omit sensitive fields like password, email
}),
body: (body) => {
const str = JSON.stringify(body)
return str.length > 1000
? str.substring(0, 1000) + '...[truncated]'
: body
}
},
// Format timestamp as ISO 8601
timestamp: pino.stdTimeFunctions.isoTime,
// Base fields
base: {
pid: process.pid,
hostname: require('os').hostname()
}
})
module.exports = logger
const pino = require('pino')
const transport = pino.transport({
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:standard',
ignore: 'pid,hostname'
}
})
const logger = pino({
level: 'debug',
transport: process.env.NODE_ENV === 'development' ? transport : undefined
})
module.exports = logger
const express = require('express')
const logger = require('./logger')
const app = express()
// Middleware: Attach child logger to req
app.use((req, res, next) => {
req.log = logger.child({
requestId: req.headers['x-request-id'] || crypto.randomUUID(),
method: req.method,
url: req.url
})
req.log.info('Request started')
res.on('finish', () => {
req.log.info({ statusCode: res.statusCode }, 'Request completed')
})
next()
})
// Route handlers use req.log
app.get('/users/:id', async (req, res) => {
req.log.info({ userId: req.params.id }, 'Fetching user')
try {
const user = await fetchUser(req.params.id)
req.log.info({ user }, 'User fetched successfully')
res.json(user)
} catch (error) {
req.log.error({ error, userId: req.params.id }, 'Failed to fetch user')
res.status(500).json({ error: 'Internal server error' })
}
})
// services/payment.js
const parentLogger = require('./logger')
const log = parentLogger.child({ module: 'payment' })
async function processPayment(orderId, amount) {
log.info({ orderId, amount }, 'Processing payment')
try {
const result = await stripe.charge({ amount })
log.info({ orderId, transactionId: result.id }, 'Payment successful')
return result
} catch (error) {
log.error({ error, orderId }, 'Payment failed')
throw error
}
}
const logger = require('./logger')
// Request logger
const requestLogger = logger.child({
requestId: 'abc-123',
userId: 42
})
// Payment context within request
const paymentLogger = requestLogger.child({
paymentProvider: 'stripe',
orderId: 'order-456'
})
paymentLogger.info('Processing payment')
// Output includes: requestId, userId, paymentProvider, orderId
const pino = require('pino')
const transport = pino.transport({
targets: [
// Console for info+
{
target: 'pino-pretty',
level: 'info',
options: { destination: 1 } // stdout
},
// File for errors
{
target: 'pino/file',
level: 'error',
options: {
destination: './logs/errors.log',
mkdir: true
}
},
// File for all debug logs
{
target: 'pino/file',
level: 'debug',
options: {
destination: './logs/debug.log',
mkdir: true
}
}
]
})
const logger = pino({ level: 'debug' }, transport)
const pino = require('pino')
const logger = pino(pino.destination({
dest: './logs/app.log',
minLength: 4096, // Buffer before writing
sync: false // Asynchronous logging
}))
// Handle graceful shutdown
process.on('SIGTERM', () => {
logger.flush()
process.exit(0)
})
Standard levels (lowest to highest priority):
trace (10) - Very detailed debuggingdebug (20) - Debug informationinfo (30) - General information (default)warn (40) - Warning messageserror (50) - Error messagesfatal (60) - Fatal errors (process exit)logger.trace('Entering function')
logger.debug({ query: sql }, 'Executing query')
logger.info({ userId: 123 }, 'User logged in')
logger.warn({ retries: 3 }, 'Retry limit approaching')
logger.error({ error }, 'Database connection failed')
logger.fatal({ error }, 'Unrecoverable error, shutting down')
const pino = require('pino')
const logger = pino({
serializers: {
error: pino.stdSerializers.err
}
})
try {
throw new Error('Something broke')
} catch (error) {
logger.error({ error }, 'Operation failed')
// Outputs: message, stack, type, code
}
const logger = pino({
serializers: {
req: (req) => ({
id: req.id,
method: req.method,
url: req.url,
headers: {
'user-agent': req.headers['user-agent'],
'content-type': req.headers['content-type']
},
remoteAddress: req.ip
}),
res: (res) => ({
statusCode: res.statusCode,
headers: {
'content-type': res.getHeader('content-type')
}
})
}
})
// ❌ Wrong
console.log('User logged in:', userId)
console.error('Error:', error)
// ✅ Correct
logger.info({ userId }, 'User logged in')
logger.error({ error }, 'Operation failed')
// ❌ Wrong (Winston style)
logger.info('User logged in', { userId, action })
// ✅ Correct (Pino style)
logger.info({ userId, action }, 'User logged in')
// ❌ Wrong - loses request context
app.get('/users', (req, res) => {
logger.info('Fetching users') // No requestId!
})
// ✅ Correct - preserves request context
app.get('/users', (req, res) => {
req.log.info('Fetching users') // Includes requestId
})
// ❌ Wrong - exposes PII
logger.info({ user }, 'User logged in') // Contains password!
// ✅ Correct - use serializers or explicit fields
logger.info({ userId: user.id, username: user.username }, 'User logged in')
// ❌ Wrong - performance overhead
logger.info('User ' + userId + ' logged in')
// ✅ Correct - structured data
logger.info({ userId }, 'User logged in')
// eslint.config.js
module.exports = {
rules: {
'no-console': ['error', { allow: ['warn', 'error'] }], // Block console in source
'no-restricted-imports': ['error', {
patterns: [{
group: ['winston', 'bunyan', 'morgan'],
message: 'Use Pino logger instead'
}]
}]
}
}
const pino = require('pino')
const { test } = require('node:test')
test('logger captures error context', async () => {
const stream = pino.destination({ sync: true })
const logger = pino(stream)
const logs = []
stream.on('data', (chunk) => {
logs.push(JSON.parse(chunk.toString()))
})
logger.error({ userId: 123, action: 'delete' }, 'Failed to delete')
assert.equal(logs[0].level, 50) // error level
assert.equal(logs[0].userId, 123)
assert.equal(logs[0].action, 'delete')
})
Type-safe SQL ORM for TypeScript with zero runtime overhead
TypeScript-first schema validation library with static type inference for form validation, API validation, and runtime type checking with compile-time types.
Official ESLint + typescript-eslint + boundaries best practices for enterprise monorepos (flat config, typed linting, architectural boundaries, CI quality gates).
Official ESLint and typescript-eslint performance tuning for large monorepos (cache strategy, concurrency, typed-linting scope, profiling).
Changesets for versioning and changelog management in monorepos. Use when managing package versions, generating changelogs, or publishing packages. Use for changesets, versioning, changelog, semver, monorepo-versioning, release, publish, bump.
Data modeling with Entity-Relationship Diagrams (ERDs), data dictionaries, and conceptual/logical/physical models. Documents data structures, relationships, and attributes.