# 如何在Node.js服務中寫日志
## 目錄
1. [日志的重要性](#日志的重要性)
2. [Node.js日志基礎](#nodejs日志基礎)
3. [控制臺日志的局限性](#控制臺日志的局限性)
4. [常用日志庫介紹](#常用日志庫介紹)
- [Winston](#winston)
- [Bunyan](#bunyan)
- [Pino](#pino)
- [Log4js](#log4js)
5. [日志級別詳解](#日志級別詳解)
6. [日志格式化](#日志格式化)
7. [日志存儲策略](#日志存儲策略)
- [文件存儲](#文件存儲)
- [數據庫存儲](#數據庫存儲)
- [云服務存儲](#云服務存儲)
8. [日志分割與輪轉](#日志分割與輪轉)
9. [結構化日志](#結構化日志)
10. [性能考慮](#性能考慮)
11. [安全最佳實踐](#安全最佳實踐)
12. [實戰示例](#實戰示例)
13. [監控與告警](#監控與告警)
14. [總結](#總結)
---
## 日志的重要性
在現代軟件開發中,日志系統是應用程序不可或缺的組成部分。良好的日志實踐能夠:
- 快速定位和診斷問題
- 監控應用程序運行狀態
- 分析用戶行為和數據趨勢
- 滿足合規性要求
- 為審計提供依據
研究表明,完善的日志系統可以減少高達40%的故障排查時間(來源:2022年DevOps狀態報告)。
---
## Node.js日志基礎
Node.js提供了基礎的`console`模塊:
```javascript
console.log('Info message');
console.error('Error message');
console.warn('Warning message');
但這些基礎方法在實際生產環境中往往不夠用,我們需要更專業的解決方案。
原生控制臺日志存在以下問題:
最流行的Node.js日志庫之一,特點包括:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// 添加控制臺輸出(非生產環境)
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
優勢: - 多傳輸支持(文件、控制臺、HTTP等) - 靈活的格式化系統 - 活躍的社區支持
以結構化日志著稱的庫:
const bunyan = require('bunyan');
const log = bunyan.createLogger({
name: 'myapp',
streams: [
{
level: 'info',
path: '/var/log/myapp.log'
}
]
});
log.info({ userId: 123 }, 'User login');
特點: - 默認JSON格式輸出 - 包括調用上下文信息 - 支持Dtrace集成
目前性能最高的Node.js日志庫:
const pino = require('pino');
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => { return { level: label }; }
}
});
logger.info('Server started');
性能對比(每秒日志條目):
| 庫名稱 | 性能 |
|---|---|
| Pino | 25,000 |
| Bunyan | 8,000 |
| Winston | 5,000 |
源自Java的log4j設計:
const log4js = require('log4js');
log4js.configure({
appenders: {
out: { type: 'stdout' },
file: { type: 'file', filename: 'logs/app.log' }
},
categories: {
default: { appenders: ['out', 'file'], level: 'debug' }
}
});
const logger = log4js.getLogger();
logger.level = 'debug';
特點: - 熟悉的配置方式(對Java開發者) - 強大的appender系統 - 內置集群支持
標準日志級別及其使用場景:
| 級別 | 數值 | 使用場景 |
|---|---|---|
| error | 0 | 系統錯誤,需要立即處理 |
| warn | 1 | 潛在問題警告 |
| info | 2 | 重要運行時信息 |
| debug | 3 | 調試信息 |
| trace | 4 | 詳細跟蹤信息 |
最佳實踐:
- 生產環境通常使用info級別
- 開發環境可使用debug級別
- 避免在循環中使用debug及以上級別
現代日志格式化趨勢:
{
"timestamp": "2023-07-20T08:42:51.000Z",
"level": "info",
"message": "User login",
"userId": 123,
"service": "auth-service",
"requestId": "a1b2c3d4"
}
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(({ timestamp, level, message }) => {
return `${timestamp} [${level}] ${message}`;
})
)
基礎配置示例:
new winston.transports.File({
filename: 'app.log',
maxsize: 5 * 1024 * 1024, // 5MB
maxFiles: 5
})
MongoDB存儲示例:
const MongoDB = require('winston-mongodb');
logger.add(new MongoDB({
level: 'error',
db: process.env.MONGO_URI,
collection: 'server_logs',
capped: true,
cappedSize: 10000000 // 10MB
}));
AWS CloudWatch配置:
const WinstonCloudWatch = require('winston-cloudwatch');
logger.add(new WinstonCloudWatch({
logGroupName: 'my-app',
logStreamName: 'web-server'
}));
日志輪轉的三種策略:
const { createLogger, transports } = require('winston');
const { DailyRotateFile } = require('winston-daily-rotate-file');
const transport = new DailyRotateFile({
filename: 'application-%DATE%.log',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d'
});
datePattern: 'YYYY-MM-DD-HH',
frequency: '24h'
結構化日志的優勢:
Pino結構化示例:
logger.info({
event: 'user_login',
userId: user.id,
ip: request.ip,
userAgent: request.headers['user-agent']
}, 'User login successful');
查詢示例(ELK Stack):
event:"user_login" AND responseTime:>500
優化日志性能的方法:
性能測試指標示例:
Pino (async): 28,000 ops/sec
Winston (file): 4,500 ops/sec
Console (sync): 800 ops/sec
日志安全注意事項:
避免記錄敏感信息:
實現數據脫敏:
function maskCreditCard(number) {
return number.replace(/\d{12}/, '****-****-****');
}
完整的Express應用日志配置:
const express = require('express');
const pino = require('pino-http');
const app = express();
const logger = pino({
serializers: {
req: (req) => ({
method: req.method,
url: req.url,
headers: {
'user-agent': req.headers['user-agent']
}
}),
res: (res) => ({
statusCode: res.statusCode
})
}
});
app.use(logger);
// 業務路由
app.get('/', (req, res) => {
req.log.info('Homepage accessed');
res.send('Hello World');
});
// 錯誤處理
app.use((err, req, res, next) => {
req.log.error({
error: err.message,
stack: err.stack
}, 'Unhandled error');
res.status(500).send('Server Error');
});
日志監控方案:
ELK Stack:
Grafana + Loki:
# Loki配置示例
scrape_configs:
- job_name: nodejs
static_configs:
- targets: ['localhost:3100']
labels:
job: 'nodejs-app'
env: 'production'
groups:
- name: nodejs-errors
rules:
- alert: HighErrorRate
expr: rate(log_errors_total[1m]) > 5
for: 5m
Node.js日志系統最佳實踐:
推薦技術棧組合: - 開發環境:Pino + Pretty打印 - 生產環境:Winston + ELK - Serverless:Pino + CloudWatch
通過完善的日志系統,您可以: ? 減少30-50%的故障排查時間 ? 提高系統可觀測性 ? 滿足合規性要求 ? 為業務分析提供數據支持 “`
注:本文實際約5200字(含代碼示例),如需擴展特定部分或添加更多實踐案例,可以進一步補充內容。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。