日志策略 - Nginx, NestJS, Pino
在 Nginx 中动态查看 IP 访问情况;
在 Pino 中动态查看用户访问情况。
Nginx
Nginx 的日志管理是运维和开发中非常重要的一环。通常分为访问日志 (access_log) 和 错误日志 (error_log)。
以下是关于 Nginx 日志记录方式及常用查看工具的详细说明:
一、 Nginx 日志记录方式
1. 记录到本地文件(最常用)
这是默认方式,Nginx 将每一条请求记录在服务器的磁盘文件中。
- 配置指令:
access_log和log_format。 - 自定义格式:可以自定义日志包含的字段(如 IP、时间、请求路径、状态码、响应时间等)。
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main; - 现代推荐:现在很多场景推荐使用 JSON 格式 记录日志,方便程序直接解析(如 ELK、GoAccess)。
2. 记录到 Syslog
Nginx 可以将日志直接发送到系统日志服务(Syslog),便于集中管理。
- 配置示例:
access_log syslog:server=127.0.0.1:514,facility=local7,tag=nginx,severity=info;
3. 记录到标准输出 (stdout/stderr)
这在 Docker 或 Kubernetes 环境中非常常见。Nginx 将日志打到屏幕,由容器引擎(如 Docker Engine)捕获并存储。
- 实现方法:通常将日志文件软链接到
/dev/stdout。
二、 常用的查看与搜索工具
根据需求从简单到复杂,可以将工具分为以下几类:
1. 基础命令行工具(快、准、稳)
适用于快速排查问题,无需安装额外软件。
tail -f: 实时查看最新日志。tail -f /var/log/nginx/access.log
grep: 搜索特定关键词(如某个 IP 或 404 状态码)。grep "404" /var/log/nginx/access.log
awk: 统计与过滤。比如统计访问量前 10 的 IP。awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -n 10
zgrep/zcat: 直接搜索压缩过的旧日志(.gz 文件)。
2. 实时交互式工具(推荐)
适用于需要可视化、但不想搭建复杂系统的场景。
- GoAccess (强烈推荐):
- 特点:极快,支持在 Linux 终端显示,也可以生成实时的 HTML 报告。
- 场景:想直观看到哪个 URL 访问最多、哪些 IP 异常、响应时间分布等。
- Angie / Nginx VTS:
- 通过安装模块,在网页上实时查看 Nginx 的流量状态。
3. 集中式日志管理系统(企业级)
适用于多台服务器、需要长期存储和复杂检索的情况。
- ELK Stack (Elasticsearch, Logstash, Kibana):
- 行业标准。Logstash/Filebeat 收集日志,Elasticsearch 存储,Kibana 展示。
- 优点:功能极其强大,支持复杂的报表和搜索。
- Loki (Grafana Labs):
- 被称为“日志界的 Prometheus”,比 ELK 更轻量,适合 Kubernetes 环境。
- Graylog:
- 比 ELK 部署更简单,自带告警功能,非常适合管理 Syslog。
4. 云原生与分析工具
- 阿里日志服务 SLS / AWS CloudWatch: 如果在云上,直接使用云厂商提供的服务,省去运维成本。
- SQL 分析: 如果将日志导入 ClickHouse 或 StarRocks,可以用 SQL 进行亿级数据的秒级分析。
总结:我该选哪个?
- 随便看看 / 查个报错:直接用
tail -f和grep。 - 看今日流量排行 / 统计分析:安装 GoAccess(只需一个命令即可看到精美报表)。
- 多台服务器 / 长期排查分析:搭建 ELK 或 Loki。
- 容器环境:直接
kubectl logs或配套 Loki。
小建议:无论用什么工具,将 Nginx 日志格式配置为 JSON 会让你后续的所有搜索和分析工作轻松 10 倍。
NestJS
在 NestJS 开发中,日志记录通常分为三个层次:内置日志、生产级日志库、以及接入外部日志平台。
以下是 NestJS 常用的日志记录方式以及配套的查看/搜索工具:
一、 NestJS 常用的日志记录方式
1. 内置 Logger (适合开发环境)
NestJS 自带了一个 Logger 类,可以打印带有颜色和类名的格式化日志。
- 优点:零配置,开箱即用。
- 缺点:功能简单,不支持日志分级写入文件,不方便在生产环境下检索。
2. Pino (推荐,性能之选)
目前 NestJS 社区最推崇的是 Pino。它以极高的性能著称,且默认输出 JSON 格式。
- 配合库:
nestjs-pino - 特点:
- 性能极高:比 Winston 快 5-10 倍。
- 结构化日志:默认输出 JSON,非常方便被 Logstash 或 Fluentd 解析。
- 自动记录请求:可以自动记录 HTTP 请求的详细信息(URL、状态码、响应时间)。
3. Winston (老牌且全能)
Winston 是 Node.js 领域最成熟的日志库。
- 配合库:
nest-winston - 特点:
- 配置丰富:支持多种 Transport(控制台、文件、HTTP、MongoDB 等)。
- 生态强大:几乎可以对接任何存储媒介。
4. 访问日志 (Access Logs) 的实现
对于 HTTP 访问日志,通常有两种实现方式:
- 中间件 (Middleware):使用
morgan(Node.js 经典访问日志中间件)或pino-http。 - 拦截器 (Interceptor):通过 NestJS 拦截器手动记录请求参数和执行时间。
二、 常见的查看与搜索工具
日志记录下来后,如何查看和搜索是关键。通常根据项目规模分为以下几类方案:
1. 轻量级方案 (中小型项目)
如果你直接将日志写到了服务器的文件中:
jq:命令行工具。如果你的日志是 JSON 格式,tail -f access.log | jq可以美化输出并进行过滤。- PM2 Log Management:如果你用 PM2 部署,可以使用
pm2 logs查看。 - Dozzle:如果你使用 Docker,Dozzle 是一个非常轻量级的 Web 界面,可以实时查看所有容器日志。
2. 自建日志中心 (主流企业级方案)
- ELK Stack (Elasticsearch, Logstash, Kibana):
- Logstash/Filebeat:负责收集日志。
- Elasticsearch:负责存储和索引。
- Kibana:前端可视化界面,搜索功能极其强大,是行业标准。
- Grafana Loki:
- 被称为“日志界的 Prometheus”。
- 比 ELK 更轻量,因为它不索引全文,只索引标签。非常适合在 Kubernetes 环境中使用,配合 Grafana 界面展示。
3. 云原生与 SaaS 方案 (省心之选)
- AWS CloudWatch Logs / AliCloud SLS:云厂商自带的日志服务,配置简单,支持全文搜索。
- Datadog / New Relic:全家桶式的监控方案,日志、链路追踪 (APM) 和指标集成在一起,但价格较贵。
- Sentry:虽然 Sentry 主要是做错误追踪,但它现在也支持记录面包屑 (Breadcrumbs) 和简单的日志流,是捕获异常的首选。
三、 总结与建议方案
| 场景 | 推荐方案 |
|---|---|
| 本地开发 | NestJS 内置 Logger |
| 生产环境日志库 | nestjs-pino (输出 JSON 格式) |
| 访问日志收集 | pino-http 或 morgan |
| 查看与搜索 (中小型) | Grafana Loki + Grafana (轻量、现代) |
| 查看与搜索 (大型/复杂) | ELK Stack (Kibana 是搜索体验天花板) |
| 查看与搜索 (云原生) | 直接使用 AWS CloudWatch 或 阿里云 SLS |
最佳实践建议:
在 NestJS 中,始终建议将日志以 JSON 格式 输出到 Stdout (标准输出)。这样符合“云原生 12 因素应用”理念,无论是 Docker、K8s 还是日志收集插件,都能轻松捕获并处理这些日志。
为啥用 Pino 替代 console.log?
在 NestJS 中使用 pino(通常通过 nestjs-pino 库)比原生的 console.log 效率要高出 非常多,在极端情况下,性能差距可以达到 5 倍到 20 倍。
以下是深度对比和分析:
1. 性能基准 (Benchmarks)
根据 Pino 官方提供的基准测试(在 Node.js 环境下),每秒处理的日志条数对比大致如下:
console.log: 约 10,000 - 20,000 条/秒 (受限于同步阻塞和字符串格式化)Winston: 约 20,000 - 40,000 条/秒Pino: 约 200,000 - 300,000 条/秒Pino (Extreme mode): 甚至可以更高。
2. 为什么 Pino 效率更高?
A. 非阻塞 I/O (Asynchronous Logging)
console.log: 在 Node.js 中,如果输出目标是 TTY(终端),它是同步阻塞的。这意味着当你的代码调用console.log时,事件循环(Event Loop)会暂停,直到日志写完。在高并发的 NestJS 应用中,这会导致严重的延迟。- Pino: 默认支持异步写入。它通过内部缓存和批量写入技术(使用
pino.destination()),尽量减少对主线程的占用。
B. 序列化开销
console.log: 每次调用都会调用 Node.js 内部的util.format,这个过程涉及复杂的对象检查和字符串拼接,非常耗 CPU。- Pino: 专门优化了 JSON 的序列化(使用
fast-json-stringify思想)。由于现代日志系统(如 ELK, Loki)通常需要 JSON 格式,Pino 直接生成 JSON 字符串,避免了二次处理。
C. 延迟计算
- Pino 尽量推迟字符串化的过程。如果日志级别不匹配(例如:当前是
info级别,但你写了debug日志),Pino 会以极低的开销直接跳过,而console.log往往已经在参数传递阶段完成了一些字符串处理。
3. 在 NestJS 中的具体表现
NestJS 默认的 Logger 类是对 console 的一层封装。
- 内存占用:在大流量下,
console.log因为阻塞会导致请求堆积,从而引起内存上升。Pino 的内存占用非常稳定。 - 吞吐量:使用
nestjs-pino后,你的 API 响应时间(Response Time)在压力测试下通常会下降 10%-30%,因为记录请求日志的时间被大幅压缩了。
4. 什么时候该选 Pino?
- 高并发应用:如果你的 NestJS 应用每秒处理超过几百个请求。
- 生产环境:生产环境需要结构化日志(JSON),以便于日志分析。
- 对延迟敏感:不希望因为打印一行日志而增加几毫秒的接口延迟。
5. 如何在 NestJS 中获得最高性能?
如果你决定使用 Pino,建议这样配置以榨干性能:
// app.module.ts
LoggerModule.forRoot({
pinoHttp: {
// 1. 生产环境关闭美化打印 (prettyPrint),因为美化非常耗性能
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty' }
: undefined,
// 2. 使用异步 destination (Extreme mode)
// 注意:极端情况下进程崩溃可能丢失最后几行日志
dest: pino.destination({ sync: false })
},
})
总结
pino 相比 console.log 不是微小的改进,而是量级的飞跃。
- 如果只是个人小项目或本地调试,
console.log的性能损失可以忽略不计。 - 对于任何严肃的后端项目,切换到 Pino 能显著降低 CPU 负载并提升系统的最大并发能力。
Pino links
A Complete Guide to Pino Logging in Node.js
Pino 日志文件
Pino 是 Node.js 中一个高性能的 JSON 日志库,默认将日志输出到 stdout(标准输出)。要将日志写入文件,有几种常见方式,推荐使用最新版本(v7+)的 transports 机制,尤其是内置的 pino/file transport。