Node.js 应用一旦上线,性能问题往往不会直接暴露在代码里,而是先出现在请求耗时、内存波动和错误分布这些日志信号中。对运行在 Ubuntu 上的服务来说,真正有用的不是“日志越多越好”,而是先把日志写成可分析的数据,再根据场景选择命令行、集中化平台或专业监控工具逐层定位问题。看完这篇,你可以判断什么时候只靠日志就够用,什么时候该引入 ELK、PM2 或 APM,把排查成本降下来。
先把日志写成可分析的数据
性能分析的前提,不是先上大平台,而是先让日志本身可读、可筛选、可聚合。相比零散的纯文本,JSON 这类结构化日志更适合后续被脚本、命令行工具和日志平台解析。
在 Node.js 里,Winston 和 Pino 都是常见选择。它们通常支持日志级别、格式化输出以及多目标传输,比如同时写入控制台和文件。下面这个 Winston 配置,核心就是把日志统一转成 JSON,并把错误日志和综合日志分开保存:
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' })
]
});
logger.info('Request received', { method: 'GET', path: '/api', latency: 120 }); // 包含性能指标
这种写法的价值在于,每条日志都可以携带固定字段,例如 method、path、latency。后续无论你是用 grep 临时排查,还是把日志送进 Elasticsearch,都能直接按字段检索,而不是再去拆自然语言文本。
哪些性能指标值得直接写进日志
结构化只是第一步,真正决定日志有没有分析价值的,是你是否在关键路径上主动埋点。对于大多数 Node.js 服务,至少应该覆盖请求耗时、内存使用和 CPU 使用这几类指标。

请求耗时通常最值得优先记录,因为它直接反映接口体验。示例里使用 process.hrtime 计算高精度耗时,再把结果以毫秒形式写入日志:
// 记录请求响应时间(毫秒)
const start = process.hrtime();
app.get('/api', (req, res) => {
// 业务逻辑...
const diff = process.hrtime(start);
const latency = (diff[0] * 1e9 + diff[1]) / 1e6;
logger.info('Request completed', { method: req.method, path: req.path, latency });
});
// 定期记录内存和CPU使用率
setInterval(() => {
const memory = process.memoryUsage();
const cpu = process.cpuUsage();
logger.info('System metrics', {
memoryUsage: `${memory.heapUsed / 1024 / 1024}MB`,
cpuUsage: `${(cpu.user / 1e6).toFixed(2)}ms`
});
}, 5000); // 每5秒记录一次
这里有两个实用点。第一,接口耗时日志最好和请求路径、HTTP 方法一起记录,否则后面只能看到“慢了”,却不知道是哪类请求在慢。第二,系统级指标不必每次请求都打,像示例这样每 5000 毫秒记录一次,更适合观察服务负载变化趋势。
如果你的目标是先搭一套够用的分析基础,建议先确保以下信息能稳定进入日志:
- 请求维度:请求方法、路径、响应耗时。
- 进程维度:内存使用量、CPU 使用时间。
- 异常维度:错误级别、错误消息、发生时间。
这几类字段已经足够支撑大多数日常排障,例如判断是某个接口突然变慢,还是整体进程内存持续攀升。
Ubuntu 上先用命令行完成快速排查
如果问题还处在单机、单服务阶段,Ubuntu 自带的命令行工具往往是最快的入口。它们不需要额外部署,适合先做一次低成本筛查,确认异常是不是已经能从现有日志中看出来。
常见用法包括三类:
- grep:适合先过滤错误日志或指定请求,例如查找所有错误日志:
grep 'ERROR' /path/to/combined.log - awk:适合从结构化内容里提取字段并做简单统计,例如计算平均响应时间:
awk -F'latency":' '{if($2) {sum+=$2; count++}} END {print "A verage latency: " sum/count "ms"}' combined.log - sort/uniq:适合统计高频错误或重复问题,例如统计错误类型分布:
grep 'ERROR' combined.log | awk -F'"message":' '{print $2}' | sort | uniq -c | sort -nr
这类命令的优势不在于“全面”,而在于你可以很快判断故障大概落在哪个层面。比如平均延迟突然升高,多半是请求路径或下游依赖出了问题;如果某类错误频次异常集中,就可以继续顺着错误消息定位代码或外部服务。
对于中小型项目,很多问题到这一步其实已经能定性。真正需要引入更重工具的情况,通常是日志量变大、服务实例增多,或者你开始需要趋势图和跨时间窗口对比。
什么时候该上 ELK 或 PM2
当服务进入多实例部署、日志分散在不同机器上,或者你已经不满足于人工跑命令看结果时,就该考虑集中化方案。这里有两条典型路径:一条是 ELK Stack 做统一收集和可视化,另一条是 PM2 先解决进程管理与基础监控。

ELK Stack 适合做集中存储和可视化分析
ELK Stack 由 Elasticsearch、Logstash 和 Kibana 组成,适合把 Node.js 日志集中起来做搜索、聚合和图表展示。原始步骤可以按下面来走:

- 安装 Elasticsearch 和 Logstash:
sudo apt install elasticsearch logstash - 配置 Logstash 解析 Node.js 日志,创建
/etc/logstash/conf.d/nodejs.conf
input {
file {
path => "/path/to/combined.log"
start_position => "beginning"
codec => "json"
}
}
filter {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:loglevel} %{GREEDYDATA:msg}" }
}
date {
match => ["timestamp", "ISO8601"]
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "nodejs-%{+YYYY.MM.dd}"
}
}
- 启动 Logstash:
sudo systemctl start logstash - 打开 Kibana 的
http://localhost:5601,创建索引模式,查看响应时间趋势、错误率等指标
ELK 的价值在于,它把原本分散在文件里的日志变成了可筛选、可对比、可画图的数据。只要字段设计得合理,你可以很快看出某个时间段错误率是否上升、某类请求是否持续偏慢。
PM2 更适合作为运行期的轻量监控入口
如果你还没到要部署整套 ELK 的阶段,但又希望对 Node.js 进程有更好的可观测性,PM2 是成本更低的一步。它本身就是常见的进程管理工具,同时带有日志查看和资源监控能力。
npm install -g pm2
pm2 start app.js --name "my-app"
pm2 logs my-app # 实时查看日志
pm2 monit # 监控CPU、内存使用
其中 pm2 logs 适合直接看运行期输出,pm2 monit 则能快速观察 CPU 和内存变化。对于单机或少量实例服务,PM2 往往已经能覆盖“先跑稳、先看清”的需求。
别忽略日志轮转和更深入的分析手段
性能分析不只是看日志内容,还包括控制日志文件本身的生命周期。日志不断增长会带来两个直接问题:一是磁盘空间被吃掉,二是分析成本变高。Ubuntu 上通常会用 Logrotate 处理这一层。
可以在 /etc/logrotate.d/nodejs 中定义规则:
/path/to/your/nodejs/*.log {
daily # 每天轮转
missingok # 文件不存在不报错
rotate 7 # 保留7天
compress # 压缩旧日志
notifempty # 空文件不轮转
create 0640 root adm # 新日志权限
}
这个配置的重点很明确:每天轮转、保留 7 天、压缩旧文件,并且为空日志跳过轮转。这样做的好处不是“更规范”而已,而是能避免排查进行到一半时,系统先因为日志膨胀把磁盘打满。
当你已经完成基础日志、命令行分析和集中化管理后,如果仍然需要更细的链路性能信息,就可以再考虑更高级的工具:
- APM 工具:例如 New Relic、Datadog、Elastic APM。这类工具能自动捕捉数据库查询、外部 API 调用等耗时,适合大型项目或复杂依赖场景。
- Chrome DevTools:适合深入分析单次请求或本地复现问题。可以通过
node --inspect app.js启动服务,再在 Chrome 中打开chrome://inspect,点击“Open dedicated DevTools for Node”查看 CPU 和内存表现。
可以把它们理解成两种不同方向的补充:APM 更适合长期在线观测,DevTools 更适合针对一次具体异常做深入剖析。
一条更实用的落地顺序
如果你正在给 Ubuntu 上的 Node.js 服务补一套性能分析能力,没必要一开始就把所有工具全上。更稳妥的顺序通常是:先把日志结构化,再补齐耗时与资源指标,然后用命令行验证这些字段是否足够解释日常问题。
当单机排查开始吃力,再引入 ELK 做集中分析;如果你更关心进程托管和基础监控,可以先落 PM2。与此同时,用 Logrotate 控住日志体积,避免基础设施问题反过来影响排障效率。等到系统复杂度进一步上升,再把 APM 或 --inspect 调试纳入工具箱,这样投入和收益会更平衡。







