从日志入手定位 Tomcat 性能问题,关键是把访问日志、应用日志、JVM/GC 日志与线程转储串联起来,围绕吞吐、时延、错误、线程与内存等核心指标做趋势与异常分析。
一 日志采集与关键指标
- 日志位置与类型
- 常见路径:TOMCAT_HOME/logs/ 或 /var/log/tomcatX/(X 为实例号)
- 关键文件与作用
- catalina.out / localhost.log*:标准输出、错误与本地主机日志,定位异常堆栈与运行期错误
- localhost_access_log.YYYY-MM-DD.txt:访问日志,统计吞吐、时延、状态码
- gc.log:JVM GC 日志,评估停顿与内存压力(需开启)
- 建议开启与采集的关键指标
- 吞吐(RPS)、响应时间分布(p50/p95/p99)、错误率(4xx/5xx)
- 线程池使用与饱和(是否排队、拒绝)、JVM 内存与 GC 频率/停顿
- 数据库连接池等待/超时、慢外部依赖调用耗时
- 采集与集中
- 使用 Logstash 读取日志并写入 Elasticsearch,在 Kibana/Grafana 做可视化与告警
- 示例 Logstash 片段(按实际 pattern 调整):
- input: file 路径指向 Tomcat 日志目录
- filter: grok 解析时间戳、线程、类、消息
- output: elasticsearch 索引按日期滚动
二 命令行快速分析范式
- 访问日志字段约定
- 常见格式包含:客户端IP、时间、请求方法、URI、协议、状态码、响应时间(ms)
- 若未记录响应时间,可在应用日志或 AccessLogValve 中开启
- 吞吐与时段热点
- 统计每分钟请求数(示例按空格分隔的第4列是时间字段,需按实际日志格式调整)
- grep “GET” /var/log/tomcatX/localhost_access_log.*.txt | awk ‘{print $4}’ | cut -d: -f2 | sort | uniq -c | sort -nr
- 错误率与状态码分布
- 统计 5xx 比例:grep ‘HTTP/1.[01]" 5[0-9][0-9]’ access.log | wc -l
- 统计 4xx 比例:grep ‘HTTP/1.[01]" 4[0-9][0-9]’ access.log | wc -l
- 响应时间分位与长尾
- 以响应时间为第 N 列为例(假设为 $NF):
- 计算 p95:sort -kNF | awk ‘{a[NR]=$NF} END {print a[int(NR*0.95)]}’
- Top N 慢请求:sort -kNF -nr | head -n 20
- 关联异常
- 将高耗时 URI 与 catalina.out 异常堆栈时间对齐,定位触发源(例如某接口在特定时段抛异常导致线程堆积)
三 可视化与长期观测
- 集中化与可视化
- ELK:Logstash 采集 → Elasticsearch 存储 → Kibana 仪表盘(吞吐、p95/p99、错误率、Top URI、线程池/队列、GC 次数与停顿)
- Grafana + Prometheus:若已暴露 JMX/Prometheus 指标,可与日志联动做阈值告警与趋势对比
- 关键面板建议
- RPS 与响应时间分位趋势、HTTP 错误率、线程池活跃/队列/拒绝、GC 次数与停顿时长、Top 慢接口、数据库/外部依赖 P95 时延
四 常见瓶颈与日志特征对照
| 瓶颈类型 |
日志与指标特征 |
排查与优化要点 |
| 连接数/文件描述符 |
访问日志显示并发激增;系统或应用出现 “Too many open files” |
提升 ulimit -n;优化 keepAlive;检查连接泄漏 |
| 线程池饱和 |
响应时间拉长、排队;应用日志提示线程耗尽或拒绝;线程转储中大量线程 BLOCKED/WAITING |
在 server.xml Executor 调整 maxThreads/minSpareThreads;减少阻塞操作;优化慢请求 |
| 内存/GC 压力 |
catalina.out 出现 OutOfMemoryError;gc.log 显示频繁 Full GC 或长停顿 |
调整 -Xms/-Xmx;分析对象生命周期;优化缓存与集合使用 |
| 磁盘 I/O |
访问日志写入延迟、磁盘 util 高 |
使用 iostat 确认;迁移至 SSD;减少同步落盘 |
| 数据库连接池 |
应用/连接池日志出现获取连接超时;接口时延抖动 |
调整连接池 maxActive/maxIdle;定位慢 SQL 与缺失索引 |
| 网络带宽/延迟 |
吞吐受限;跨机房调用时延高 |
使用 iftop/nload 与 ping/traceroute 排查;考虑就近接入/带宽升级 |
| 代码效率/锁竞争 |
特定 URI 持续高耗时;线程转储显示锁竞争 |
使用 JProfiler/YourKit/Async Profiler 定位热点;减少同步与长事务 |
五 落地流程与配置建议
- 启用必要日志
- 在 conf/server.xml 的 AccessLogValve 中开启并记录 响应时间(便于 p95/p99 统计)
- 在 bin/catalina.sh / catalina.bat 的 JAVA_OPTS 中开启 GC 日志:
- -Xloggc:/var/log/tomcatX/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
- 建立基线
- 以正常业务时段为基准,记录 RPS、p95/p99、错误率、GC 停顿 的常态区间,用于后续阈值告警
- 告警与处置
- 设置阈值:如 p95 > 基线+50%、5xx > 1%、Full GC 次数/小时异常、线程池队列持续增长
- 处置闭环:定位根因(SQL/外部依赖/代码/配置)→ 调整(线程池、JVM、连接池、缓存、SQL)→ 回归验证(复测 RPS 与 p95)
- 工具组合
- 日志链路:Logstash → Elasticsearch → Kibana/Grafana
- 诊断工具:jstack(线程转储)、VisualVM/JProfiler(CPU/内存/锁)、Async Profiler(低开销采样)
- 变更与回滚
- 任何配置或代码调整先在测试环境验证,保留回滚方案,变更后持续观测至少 1–2 个业务周期 以确认稳定性