backend-pino
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePino (Structured Logging)
Pino(结构化日志工具)
Overview
概述
Pino is the fastest JSON logger for Node.js. It outputs structured logs that integrate with observability platforms (Datadog, ELK, Splunk, CloudWatch).
Version: v9.x (2024-2025)
Performance: ~5x faster than Winston, ~10x faster than Bunyan
Performance: ~5x faster than Winston, ~10x faster than Bunyan
Key Benefit: Structured JSON logs → easy parsing, filtering, and alerting in production.
Pino是Node.js中最快的JSON日志工具。它输出的结构化日志可与可观测性平台(Datadog、ELK、Splunk、CloudWatch)集成。
版本: v9.x(2024-2025)
性能: 比Winston快约5倍,比Bunyan快约10倍
性能: 比Winston快约5倍,比Bunyan快约10倍
核心优势: 结构化JSON日志 → 便于生产环境中的解析、过滤和告警。
When to Use This Skill
适用场景
✅ Use Pino when:
- Building production APIs
- Need structured JSON logs
- Integrating with observability platforms
- Require request tracing (correlation IDs)
- Must redact sensitive data (passwords, tokens)
❌ Skip Pino when:
- Simple scripts or CLI tools
- Early prototyping (console.log is fine)
- Client-side JavaScript
✅ 推荐使用Pino的场景:
- 构建生产级API
- 需要结构化JSON日志
- 与可观测性平台集成
- 要求请求追踪(关联ID)
- 必须脱敏敏感数据(密码、令牌)
❌ 不推荐使用Pino的场景:
- 简单脚本或CLI工具
- 早期原型开发(使用console.log即可)
- 客户端JavaScript
Quick Start
快速开始
Installation
安装
bash
npm install pino pino-pretty
npm install -D @types/pinobash
npm install pino pino-pretty
npm install -D @types/pinoBasic Configuration
基础配置
typescript
// src/lib/logger.ts
import pino from 'pino';
const isDev = process.env.NODE_ENV === 'development';
export const logger = pino({
level: process.env.LOG_LEVEL || (isDev ? 'debug' : 'info'),
// Pretty print in development
transport: isDev ? {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:standard',
ignore: 'pid,hostname',
},
} : undefined,
// Redact sensitive fields
redact: {
paths: [
'password',
'token',
'authorization',
'cookie',
'*.password',
'*.token',
'req.headers.authorization',
],
censor: '[REDACTED]',
},
// Add base context to all logs
base: {
service: process.env.SERVICE_NAME || 'api',
env: process.env.NODE_ENV,
version: process.env.APP_VERSION,
},
});
export default logger;typescript
// src/lib/logger.ts
import pino from 'pino';
const isDev = process.env.NODE_ENV === 'development';
export const logger = pino({
level: process.env.LOG_LEVEL || (isDev ? 'debug' : 'info'),
// 开发环境下启用美观打印
transport: isDev ? {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:standard',
ignore: 'pid,hostname',
},
} : undefined,
// 脱敏敏感字段
redact: {
paths: [
'password',
'token',
'authorization',
'cookie',
'*.password',
'*.token',
'req.headers.authorization',
],
censor: '[REDACTED]',
},
// 为所有日志添加基础上下文
base: {
service: process.env.SERVICE_NAME || 'api',
env: process.env.NODE_ENV,
version: process.env.APP_VERSION,
},
});
export default logger;Log Levels
日志级别
typescript
// In order of severity (lowest to highest)
logger.trace('Detailed debugging'); // level 10
logger.debug('Debug information'); // level 20
logger.info('Normal operation'); // level 30
logger.warn('Warning condition'); // level 40
logger.error('Error occurred'); // level 50
logger.fatal('App is crashing'); // level 60typescript
// 按严重程度从低到高排序
logger.trace('详细调试信息'); // 级别10
logger.debug('调试信息'); // 级别20
logger.info('正常运行记录'); // 级别30
logger.warn('警告情况'); // 级别40
logger.error('发生错误'); // 级别50
logger.fatal('应用崩溃'); // 级别60Level Configuration
级别配置
typescript
// Set via environment
LOG_LEVEL=debug npm start
// Or in code
const logger = pino({ level: 'debug' });typescript
// 通过环境变量设置
LOG_LEVEL=debug npm start
// 或在代码中设置
const logger = pino({ level: 'debug' });Structured Logging
结构化日志
Log Objects, Not Strings
记录对象而非字符串
typescript
// ❌ Avoid string interpolation
logger.info(`User ${userId} logged in from ${ip}`);
// ✅ Use structured objects
logger.info({ userId, ip, action: 'login' }, 'User logged in');typescript
// ❌ 避免字符串插值
logger.info(`User ${userId} logged in from ${ip}`);
// ✅ 使用结构化对象
logger.info({ userId, ip, action: 'login' }, '用户登录');Output (JSON)
输出(JSON格式)
json
{
"level": 30,
"time": 1702300800000,
"service": "api",
"userId": "user_123",
"ip": "192.168.1.1",
"action": "login",
"msg": "User logged in"
}json
{
"level": 30,
"time": 1702300800000,
"service": "api",
"userId": "user_123",
"ip": "192.168.1.1",
"action": "login",
"msg": "用户登录"
}Child Loggers (Context)
子日志器(上下文传递)
Request Context
请求上下文
typescript
// Create child logger with request context
const requestLogger = logger.child({
requestId: 'req_abc123',
userId: 'user_456',
});
// All logs include context automatically
requestLogger.info('Processing request');
requestLogger.info({ orderId: 'order_789' }, 'Order created');typescript
// 创建包含请求上下文的子日志器
const requestLogger = logger.child({
requestId: 'req_abc123',
userId: 'user_456',
});
// 所有日志将自动包含上下文
requestLogger.info('处理请求中');
requestLogger.info({ orderId: 'order_789' }, '订单已创建');Output
输出
json
{
"requestId": "req_abc123",
"userId": "user_456",
"msg": "Processing request"
}
{
"requestId": "req_abc123",
"userId": "user_456",
"orderId": "order_789",
"msg": "Order created"
}json
{
"requestId": "req_abc123",
"userId": "user_456",
"msg": "处理请求中"
}
{
"requestId": "req_abc123",
"userId": "user_456",
"orderId": "order_789",
"msg": "订单已创建"
}Express Request Logging
Express请求日志中间件
Middleware
中间件实现
typescript
// src/middleware/request-logger.ts
import { randomUUID } from 'crypto';
import { Request, Response, NextFunction } from 'express';
import { logger } from '../lib/logger';
// Extend Express Request type
declare global {
namespace Express {
interface Request {
log: typeof logger;
requestId: string;
}
}
}
export function requestLogger(req: Request, res: Response, next: NextFunction) {
const requestId = (req.headers['x-request-id'] as string) || randomUUID();
const start = Date.now();
// Create child logger with request context
const childLogger = logger.child({
requestId,
method: req.method,
path: req.path,
userAgent: req.headers['user-agent'],
});
// Attach to request for use in handlers
req.log = childLogger;
req.requestId = requestId;
// Set response header for tracing
res.setHeader('x-request-id', requestId);
// Log request start
childLogger.info('Request started');
// Log request completion
res.on('finish', () => {
childLogger.info({
statusCode: res.statusCode,
duration: Date.now() - start,
}, 'Request completed');
});
next();
}typescript
// src/middleware/request-logger.ts
import { randomUUID } from 'crypto';
import { Request, Response, NextFunction } from 'express';
import { logger } from '../lib/logger';
// 扩展Express Request类型
declare global {
namespace Express {
interface Request {
log: typeof logger;
requestId: string;
}
}
}
export function requestLogger(req: Request, res: Response, next: NextFunction) {
const requestId = (req.headers['x-request-id'] as string) || randomUUID();
const start = Date.now();
// 创建包含请求上下文的子日志器
const childLogger = logger.child({
requestId,
method: req.method,
path: req.path,
userAgent: req.headers['user-agent'],
});
// 将日志器附加到请求对象,供后续处理器使用
req.log = childLogger;
req.requestId = requestId;
// 设置响应头用于追踪
res.setHeader('x-request-id', requestId);
// 记录请求开始
childLogger.info('请求已启动');
// 记录请求完成
res.on('finish', () => {
childLogger.info({
statusCode: res.statusCode,
duration: Date.now() - start,
}, '请求已完成');
});
next();
}Usage in Express
在Express中使用
typescript
// src/app.ts
import express from 'express';
import { requestLogger } from './middleware/request-logger';
const app = express();
app.use(requestLogger);
app.get('/users/:id', async (req, res) => {
req.log.info({ userId: req.params.id }, 'Fetching user');
try {
const user = await getUser(req.params.id);
req.log.debug({ user }, 'User found');
res.json(user);
} catch (error) {
req.log.error({ error }, 'Failed to fetch user');
res.status(500).json({ error: 'Internal error' });
}
});typescript
// src/app.ts
import express from 'express';
import { requestLogger } from './middleware/request-logger';
const app = express();
app.use(requestLogger);
app.get('/users/:id', async (req, res) => {
req.log.info({ userId: req.params.id }, '获取用户信息');
try {
const user = await getUser(req.params.id);
req.log.debug({ user }, '用户信息已找到');
res.json(user);
} catch (error) {
req.log.error({ error }, '获取用户信息失败');
res.status(500).json({ error: '内部错误' });
}
});tRPC Logging Middleware
tRPC日志中间件
typescript
// src/server/middleware/logging.ts
import { middleware } from '../trpc';
import { logger } from '@/lib/logger';
export const loggerMiddleware = middleware(async ({ path, type, next, ctx }) => {
const start = Date.now();
const log = ctx.log || logger;
log.debug({ path, type }, 'tRPC procedure started');
try {
const result = await next();
log.info({
path,
type,
duration: Date.now() - start,
}, 'tRPC procedure completed');
return result;
} catch (error) {
log.error({
path,
type,
duration: Date.now() - start,
error,
}, 'tRPC procedure failed');
throw error;
}
});
// Apply to all procedures
export const loggedProcedure = publicProcedure.use(loggerMiddleware);typescript
// src/server/middleware/logging.ts
import { middleware } from '../trpc';
import { logger } from '@/lib/logger';
export const loggerMiddleware = middleware(async ({ path, type, next, ctx }) => {
const start = Date.now();
const log = ctx.log || logger;
log.debug({ path, type }, 'tRPC流程已启动');
try {
const result = await next();
log.info({
path,
type,
duration: Date.now() - start,
}, 'tRPC流程已完成');
return result;
} catch (error) {
log.error({
path,
type,
duration: Date.now() - start,
error,
}, 'tRPC流程执行失败');
throw error;
}
});
// 应用到所有流程
export const loggedProcedure = publicProcedure.use(loggerMiddleware);Error Logging
错误日志
With Stack Traces
包含堆栈跟踪
typescript
try {
await riskyOperation();
} catch (error) {
// Pino serializes Error objects automatically
logger.error({ err: error }, 'Operation failed');
}typescript
try {
await riskyOperation();
} catch (error) {
// Pino会自动序列化Error对象
logger.error({ err: error }, '操作执行失败');
}Custom Error Serializer
自定义错误序列化器
typescript
const logger = pino({
serializers: {
err: pino.stdSerializers.err, // Default error serializer
error: (error) => ({
type: error.constructor.name,
message: error.message,
stack: error.stack,
code: error.code,
// Add custom fields
...(error.details && { details: error.details }),
}),
},
});typescript
const logger = pino({
serializers: {
err: pino.stdSerializers.err, // 默认错误序列化器
error: (error) => ({
type: error.constructor.name,
message: error.message,
stack: error.stack,
code: error.code,
// 添加自定义字段
...(error.details && { details: error.details }),
}),
},
});Sensitive Data Redaction
敏感数据脱敏
Configuration
配置
typescript
const logger = pino({
redact: {
paths: [
'password',
'secret',
'token',
'apiKey',
'authorization',
'cookie',
'creditCard',
'*.password', // Nested fields
'*.secret',
'req.headers.cookie',
'req.headers.authorization',
'user.email', // PII
],
censor: '[REDACTED]',
remove: false, // Keep key, redact value
},
});typescript
const logger = pino({
redact: {
paths: [
'password',
'secret',
'token',
'apiKey',
'authorization',
'cookie',
'creditCard',
'*.password', // 嵌套字段
'*.secret',
'req.headers.cookie',
'req.headers.authorization',
'user.email', // 个人可识别信息
],
censor: '[REDACTED]',
remove: false, // 保留键名,仅脱敏值
},
});Output
输出
json
{
"user": {
"id": "123",
"email": "[REDACTED]",
"password": "[REDACTED]"
}
}json
{
"user": {
"id": "123",
"email": "[REDACTED]",
"password": "[REDACTED]"
}
}Production Configuration
生产环境配置
typescript
// src/lib/logger.ts
import pino from 'pino';
const isProduction = process.env.NODE_ENV === 'production';
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
// No transport in production (JSON to stdout)
transport: isProduction ? undefined : {
target: 'pino-pretty',
},
// Faster serialization in production
formatters: {
level: (label) => ({ level: label }),
},
// ISO timestamp
timestamp: pino.stdTimeFunctions.isoTime,
// Redaction
redact: ['password', 'token', '*.password'],
// Base context
base: {
service: process.env.SERVICE_NAME,
version: process.env.APP_VERSION,
env: process.env.NODE_ENV,
},
});typescript
// src/lib/logger.ts
import pino from 'pino';
const isProduction = process.env.NODE_ENV === 'production';
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
// 生产环境不使用传输器(直接输出JSON到标准输出)
transport: isProduction ? undefined : {
target: 'pino-pretty',
},
// 生产环境下更快的序列化
formatters: {
level: (label) => ({ level: label }),
},
// ISO格式时间戳
timestamp: pino.stdTimeFunctions.isoTime,
// 脱敏配置
redact: ['password', 'token', '*.password'],
// 基础上下文
base: {
service: process.env.SERVICE_NAME,
version: process.env.APP_VERSION,
env: process.env.NODE_ENV,
},
});Rules
最佳实践
Do ✅
推荐做法 ✅
- Use structured objects, not string interpolation
- Create child loggers for request context
- Redact sensitive data (passwords, tokens, PII)
- Include correlation IDs for tracing
- Use appropriate log levels
- Log errors with
{ err: error }
- 使用结构化对象,而非字符串插值
- 为请求上下文创建子日志器
- 脱敏敏感数据(密码、令牌、个人可识别信息)
- 包含关联ID用于追踪
- 使用合适的日志级别
- 使用记录错误
{ err: error }
Avoid ❌
避免做法 ❌
- in production code
console.log() - Logging sensitive data (passwords, tokens)
- String interpolation for structured data
- Excessive debug logging in production
- Blocking I/O in log transports
- 在生产代码中使用
console.log() - 记录敏感数据(密码、令牌)
- 对结构化数据使用字符串插值
- 生产环境中过度打印调试日志
- 在日志传输中使用阻塞式I/O
Log Level Guidelines
日志级别指南
| Level | Use Case | Production |
|---|---|---|
| Very detailed debugging | Off |
| Development debugging | Off |
| Normal operations | On |
| Potential issues | On |
| Errors (handled) | On |
| App crashing | On |
| 级别 | 使用场景 | 生产环境是否启用 |
|---|---|---|
| 极详细的调试信息 | 关闭 |
| 开发调试使用 | 关闭 |
| 正常运行记录 | 开启 |
| 潜在问题预警 | 开启 |
| 已处理的错误 | 开启 |
| 应用崩溃级错误 | 开启 |
Troubleshooting
故障排查
yaml
"Logs not appearing":
→ Check LOG_LEVEL environment variable
→ Verify level: logger.level returns current level
→ Debug level is often disabled in production
"pino-pretty not working":
→ Only use in development
→ Check transport configuration
→ npm install pino-pretty
"Sensitive data in logs":
→ Add paths to redact array
→ Use wildcards: '*.password'
→ Verify with test logs
"Performance issues":
→ Remove pino-pretty in production
→ Reduce log level
→ Check for sync logging (avoid)yaml
"日志不显示":
→ 检查LOG_LEVEL环境变量
→ 验证当前级别:logger.level返回的当前级别
→ 生产环境通常禁用debug级别
"pino-pretty不生效":
→ 仅在开发环境使用
→ 检查传输器配置
→ 确认已安装pino-pretty
"日志中出现敏感数据":
→ 将对应路径添加到redact数组
→ 使用通配符:'*.password'
→ 通过测试日志验证
"性能问题":
→ 生产环境移除pino-pretty
→ 降低日志级别
→ 检查是否存在同步日志操作(避免使用)Integration with Observability
与可观测性平台集成
Datadog
Datadog
typescript
// Datadog expects JSON logs to stdout
const logger = pino({
formatters: {
level: (label) => ({ level: label }),
},
// Datadog trace correlation
mixin: () => ({
dd: {
trace_id: getCurrentTraceId(),
span_id: getCurrentSpanId(),
},
}),
});typescript
// Datadog期望JSON日志输出到标准输出
const logger = pino({
formatters: {
level: (label) => ({ level: label }),
},
// Datadog追踪关联
mixin: () => ({
dd: {
trace_id: getCurrentTraceId(),
span_id: getCurrentSpanId(),
},
}),
});ELK Stack
ELK栈
typescript
// Elasticsearch-friendly format
const logger = pino({
timestamp: pino.stdTimeFunctions.isoTime,
formatters: {
level: (label) => ({ level: label }),
},
});typescript
// 适配Elasticsearch的格式
const logger = pino({
timestamp: pino.stdTimeFunctions.isoTime,
formatters: {
level: (label) => ({ level: label }),
},
});File Structure
文件结构
src/
├── lib/
│ └── logger.ts # Logger configuration
├── middleware/
│ └── request-logger.ts # Express middleware
└── server/
└── middleware/
└── logging.ts # tRPC middlewaresrc/
├── lib/
│ └── logger.ts # 日志工具配置
├── middleware/
│ └── request-logger.ts # Express中间件
└── server/
└── middleware/
└── logging.ts # tRPC中间件References
参考链接
- https://getpino.io — Official documentation
- https://github.com/pinojs/pino — GitHub
- https://github.com/pinojs/pino-pretty — Pretty printing
- https://getpino.io — 官方文档
- https://github.com/pinojs/pino — GitHub仓库
- https://github.com/pinojs/pino-pretty — 美观打印工具