简介:这是一份开源数据库日志分析工具 pgBadger 11.5 的完整源码包,面向 PostgreSQL 运维与研发人员,用于快速解析 syslog、stderr、csvlog 等格式的日志并生成可视化图表报告。工具采用纯 Perl 编写,内部集成 jQuery 与 jqPlot 等前端库,支持直接处理 gzip 压缩日志,适合在高并发、大日志量场景下排查慢查询与错误信息。压缩包共含 54 个文件,约 2.2MB,其中包含 pgbadger 主脚本、Perl 模块、自动化测试用例(.t)、前端图表资源(.js/.css)以及 README、ChangeLog、Pod 文档等,同时附带多个压缩日志样本和测试夹具,便于直接运行验证。已有 440 人学习下载。通过本包可快速部署 pgBadger,掌握其日志格式自动识别与报表生成逻辑,还可参考测试用例和文档进行二次开发或集成到监控系统,适合作为 PostgreSQL 日志分析的实用工具包。
1. 先看懂pgBadger:一份日志报告替代半小时的SQL排查
pgBadger是PostgreSQL日志分析器,它把数据库原本零散的运行日志解析成一份按时间线组织的HTML报告。PostgreSQL日志里其实埋着大量诊断信息:慢查询、临时文件、checkpoint频率、锁等待、连接数变化……这些内容散落在每天几百MB的文本里,人工翻日志找问题通常要花不少时间。pgBadger的价值,就是把“翻日志”变成“打开报告”:先看概览,再定位异常板块,最后下钻到具体SQL。
它适合两类人:一类是DBA和运维工程师,每天要巡检数据库;另一类是业务侧开发,遇到“数据库慢”的反馈不知道从哪里下手,用pgBadger先看Top查询和临时文件,能快速把问题边界划清楚。需要注意,pgBadger只做日志统计,不进数据库内部采集指标,所以它对现有业务的侵入几乎为零。对大多数PostgreSQL使用者来说,这是成本最低的日志分析起步方案。
2. 日志侧准备:让PostgreSQL先吐出可解析的日志
pgBadger不自己访问数据库,也不依赖任何扩展,它的输入只有一个:PostgreSQL的日志文件。所以整个方案的第一道门槛,不是安装pgBadger,而是确认数据库的日志格式满足解析要求。我在实际运维里见过不少案例,日志参数没配全,报告里要么缺失板块,要么干脆解析不出来。这一章先把日志侧要做的事讲清楚。
2.1 先确认日志输出格式:stderr还是CSV
PostgreSQL的日志输出主要由log_destination决定。常见做法是保持默认的stderr,把日志交给日志收集器按文件保存;另一种是csvlog,字段结构化,pgBadger解析起来更稳。两种格式都能做分析,但踩坑方式不一样。
如果走stderr,配置大概是这样的:
log_destination = 'stderr' logging_collector = on log_directory = 'log' log_filename = 'postgresql-%Y-%m-%d.log' log_rotation_age = 1d log_rotation_size = 0这段配置里,log_destination指定输出目标为stderr;logging_collector开启后数据库才会把日志写入文件;log_directory和log_filename决定日志文件的存放位置和命名方式;log_rotation_age=1d让日志按天轮换,后续报告归档时容易对账。log_rotation_size=0表示不按大小轮换,避免一天内出现多个零散文件。
CSV格式对应这样配置:
log_destination = 'csvlog' logging_collector = on log_filename = 'postgresql-%Y-%m-%d.csv'CSV格式的解析速度快,字段天然分开,时间戳、用户、数据库、查询文本分别占一列,pgBadger不需要靠正则猜字段,出错的概率低很多。代价是文件体积比stderr略大。如果日常日志动不动就上GB,建议直接上CSV,后面第5章会专门讲大文件带来的坑。
2.2 该打开的日志开关:每个参数都对应报告里的一个板块
pgBadger报告里有会话、慢查询、临时文件、Checkpoint、锁等待、自动vacuum这些板块。它们不是凭空生成的,背后都对应postgresql.conf里的一组开关。下面这组配置是我在实际业务库上常用的底线配置:
log_line_prefix = '%m [%p] %q%u@%d %h ' log_checkpoints = on log_connections = on log_disconnections = on log_lock_waits = on log_temp_files = 0 log_autovacuum_min_duration = 0 log_min_duration_statement = 500参数逐个说:log_line_prefix决定每行日志的前缀格式,%m是带毫秒的时间戳,pgBadger靠它做时间线排序;%p是进程号;%u是用户名;%d是数据库名;%h是客户端地址。这四个字段分别对应报告里按进程、按用户、按数据库的统计维度。如果前缀里没有%m,pgBadger对很多行会无法识别,报告数字会明显偏少。
log_connections和log_disconnections记录会话建立与断开,报告里才有连接数变化曲线,也能定位连接风暴发生在哪个时间段。log_lock_waits记录锁等待事件,搭配锁等待板块定位锁冲突。log_checkpoints记录checkpoint开始与完成,报告里会有checkpoint分布与耗时,能发现刷盘是否过于频繁。log_temp_files记录临时文件,设0表示所有临时文件都记录,这个值不是越小越好,后面避坑章会展开。log_autovacuum_min_duration记录超过指定时长的autovacuum操作,设0表示全记录,日志量会增加。log_min_duration_statement是慢查询阈值,超过500毫秒的SQL会被记录。
| 参数 | 报告对应板块 | 建议值 |
|---|---|---|
| log_min_duration_statement | Top查询、慢查询分析 | 500~1000,按业务调整 |
| log_checkpoints | Checkpoint与BGWriter | 打开 |
| log_temp_files | 临时文件统计 | 0或1024 |
| log_lock_waits | 锁等待 | 打开 |
| log_autovacuum_min_duration | VACUUM与autovacuum | 0或1000 |
| log_connections | 会话连接趋势 | 打开 |
这组配置里不建议开log_statement=all。log_statement会记录所有SQL文本,日志量放大几十倍,而pgBadger的分类依赖的是执行耗时和临时文件这些属性,不是SQL文本本身。开了log_statement=all,报告反而会被大量无关语句刷屏,慢查询的Top列表失去参考意义。
2.3 改完参数后怎么验证:别把确认交给运气
配置改完要确认真的生效。用psql登进去执行下面三条命令:
psql -c "SHOW log_line_prefix;" psql -c "SHOW log_min_duration_statement;" psql -c "SELECT pg_reload_conf();"pg_reload_conf()会让大部分参数热加载,不需要重启实例。但log_filename这类与日志收集相关的参数改了,通常需要重启。如果只是改了log_min_duration_statement、log_lock_waits这种,热加载就够。
热加载之后,再去看一眼原始日志长什么样:
tail -n 50 /var/lib/postgresql/log/postgresql-$(date +%Y-%m-%d).log正常情况下,日志里会出现类似这样的行:
2025-03-14 10:00:00.123 CST [12345] user@db 10.0.0.8 LOG: duration: 512.123 ms statement: select ...这说明%m时间戳、用户信息、耗时都写进日志了,pgBadger能拿到完整字段。如果日志里只有SQL没有duration,或者时间戳格式对不上,先回看2.1节的配置再往下走。我习惯在业务低谷时段做这个验证,避免改完参数后短时间看不到慢查询日志,误以为没生效。
3. 安装与跑出第一份报告:从命令到报告板块解读
pgBadger本身是perl脚本,安装门槛很低。这一章的目标是快速跑出第一份HTML报告,并知道先看哪里。
3.1 安装pgBadger:包管理器、源码、容器
最常见做法是直接用发行版仓库安装。Debian系:
apt-get install pgbadgerRHEL系:
yum install pgbadger装完先验证版本:
pgbadger --versionpgBadger对perl版本有要求,系统自带的老perl可能会在运行时报错。如果发行版仓库里的pgBadger版本太旧,或者没有这个包,用源码安装:
perl Makefile.PL make sudo make install源码安装主要解决仓库版本过旧的问题,编译依赖很少,perl环境正常就能过。容器场景下,我不建议在数据库容器里塞多余的包,更习惯用一个专门跑分析任务的临时容器,把日志目录挂载进去再执行pgBadger,分析完容器直接销毁,不污染数据库实例。
3.2 最小命令:一条命令生成HTML报告
运行pgBadger只需要指定日志文件和输出路径:
pgbadger /var/lib/postgresql/log/postgresql-2025-03-14.log -o report_0314.html如果不指定-f参数,pgBadger会按stderr格式解析,大多数默认配置都能直接对上。如果你的日志配置是csvlog,要显式声明:
pgbadger -f csv /var/lib/postgresql/log/postgresql-2025-03-14.csv -o report_0314.html这里-o指定输出HTML文件路径,-f指定日志格式,可选值包括stderr、syslog、csv、json。显式指定格式能避免解析器猜错。跑完在浏览器打开report_0314.html,首页是概览,能看到日志覆盖时间范围、总会话数、慢查询总数、临时文件统计。先看这几个总量,再点进对应板块,不要一上来就钻进Top SQL细节里。
3.3 报告里先看哪几块:慢查询、临时文件、Checkpoint与锁等待
pgBadger的报告板块很多,初次拿到报告容易迷失。我一般按这个顺序看:
- Top查询:默认按总耗时排名,能看出哪些SQL累计占掉了数据库大部分时间。点进去能看到每条SQL的执行次数、平均耗时、最大耗时。
- 临时文件:如果某天磁盘IO异常,先看这一块。临时文件写得多,说明排序或哈希操作的内存不够,被下放到临时文件,往往和work_mem设置或SQL写法相关。
- Checkpoint:观察checkpoint分布是否集中在一两个时间段,如果集中说明日志落盘压力集中,可能要调整checkpoint相关参数。
- 锁等待:应用反馈“卡住”的时候,锁等待板块会把等待最严重的库和锁类型列出来,方便顺着时间点反查应用日志。
- VACUUM:表和索引膨胀问题被反馈过的话,这一块能看出autovacuum是否长时间没跑完,或者是否频繁触发。
经验阈值上没有绝对标准,但有个习惯值得参考:某个库的临时文件总量超过几百MB,我就会顺着报告里的会话信息往下查,多数情况能定位到一条排序或hash join语句。慢查询板块也一样,与其只盯总耗时第一,不如同时看平均耗时高的语句。这个细节到第5章还会专门讲。
报告默认生成HTML,图表和表格都打进一个文件里,方便直接转发给同事看。如果需要归档,把HTML文件命名为带日期和实例角色的形式,比如report_orderdb_20250314.html,后续检索会省很多事。
4. 多日志合并、增量处理与时间范围过滤:pgBadger的日常用法升级
单日单文件跑通后,实际使用中很快就会遇到几个问题:日志跨了多天怎么合并、日志文件还在增长怎么写日报、只想看某个时间段怎么办。这一章讲的都是实战里用得上的功能。
4.1 多日日志文件合并:目录输入是最省事的姿势
如果日志已经按日切割,直接把多天文件一次性传给pgBadger:
pgbadger /var/lib/postgresql/log/postgresql-2025-03-14.log \ /var/lib/postgresql/log/postgresql-2025-03-15.log \ -o report_two_days.html文件多的时候用目录输入:
pgbadger -d /var/lib/postgresql/log -o report_week.html-d参数让pgBadger遍历目录下的日志文件,自动按时间合并生成一份报告。注意,这个方式会把目录里所有日志文件都拿进来,如果目录里既有stderr日志又有CSV日志,或者混着logrotate产生的.gz压缩包,解析器会刷出一片WARNING。更干净的做法是给pgBadger准备一个单独目录,里面只放它要处理的日志。
这里有一个常见误区:logrotate切割后会生成postgresql-2025-03-14.log.1、postgresql-2025-03-14.log.1.gz这类文件,如果它们和原始日志放在同一目录,pgBadger会当成不同文件统计两次。同步的SQL执行次数会翻倍,这个问题第5章会再提。
4.2 增量处理:一个正在增长的日志也能每天出报告
很多系统不想为pgBadger额外做日志切割,当天日志持续写入,这就用到--incremental:
pgbadger --incremental \ -O /var/lib/pgbadger/reports \ /var/lib/postgresql/log/current.log--incremental让pgBadger记住上次已解析的字节偏移,下次从上次的结尾继续解析,不会从头扫一遍。配合-O输出目录,增量模式会生成固定命名的报告文件,方便每天覆盖写入。
增量模式依赖状态文件,默认放在输出目录附近,由pgBadger自动维护。如果状态文件被清理工具误删,它会把整个文件重新解析一遍,报告里的数字就会翻倍。这个坑我踩过一次,后来写清理脚本时都会把状态文件目录加白名单。
4.3 时间范围过滤与排除项:只分析问题区间
遇到“早上10点到11点数据库特别慢”这种诉求,不需要把整天日志都跑一遍。用-b和-e指定时间窗口:
pgbadger -b "2025-03-14 10:00:00" -e "2025-03-14 12:00:00" \ /var/lib/postgresql/log/postgresql-2025-03-14.log \ -o report_peak.html-b是开始时间,-e是结束时间,格式是YYYY-MM-DD HH:MM:SS,按服务器本地时区解析。输出报告里只会保留这个时间窗口内的事件,慢查询排行也只基于这段时间。注意跨时区部署时,传入的时间字符串要对齐服务器时区。报告时间比你预期差8小时,基本都是时区没有对齐。
如果某类日志噪音太大,比如autovacuum刷屏、checkpoint频繁,用-x排除对应类型:
pgbadger -x autovacuum,checkpoint \ /var/lib/postgresql/log/postgresql-2025-03-14.log \ -o report_no_noise.html-x后面接逗号分隔的类别,常见可选值有query、connection、checkpoint、lock、tempfile、autovacuum。排除类别只影响报告统计,不会改动原始日志。
按库维度过滤也很常用:
pgbadger --dbname=orderdb \ /var/lib/postgresql/log/postgresql-2025-03-14.log \ -o report_orderdb.html多业务共库的环境里,这个参数很实用,只针对某个业务库生成报告,排查时不会被其他库的噪声干扰。需要注意,dbname过滤依赖日志里%u和%d字段,如果log_line_prefix配置不完整,过滤结果会偏少。
5. 避坑:日志解析失败与报告失真,现象、原因与解决
pgBadger整体很稳定,但它对输入格式的要求是刚性的。这一章列了我在实际使用里遇到最多的五个问题,按“现象→原因→解决”讲清楚。
5.1 报告数字接近为空:先查log_line_prefix有没有时间戳
现象:报告能生成,但概览页面的会话数、查询数都接近0,日志里大量行没有被解析。
原因:最常见的是log_line_prefix里没有%m,pgBadger解析stderr时依靠前缀里的时间戳识别一行日志的起点,结构对不上,整条日志就会被当成无法识别的行跳过。另一个原因是log_destination实际是csvlog,但命令里没加-f csv,解析器按stderr去解析CSV,字段必然错位。
解决:先执行psql -c "SHOW log_line_prefix;",确认包含%m;再打开原始日志看每行开头长什么样;如果日志是csvlog,命令里显式加上-f csv。最快的验证方式是只拿一小段日志试跑,报告数字能对上,再用全量文件跑。
5.2 同步日志出现重复统计:logrotate后的文件被重复传入
现象:报告里的SQL执行次数明显比实际多,Top查询列表里出现同一条SQL的邻近排名。
原因:目录输入时,目录里既有原始日志,也有logrotate生成的postgresql-2025-03-14.log.1或.gz,pgBadger把它们当成不同文件,同一批数据被统计了两遍。
解决:输入文件时明确指定原始日志;如果非要用目录,就把logrotate的后缀文件移走,或者单独维护一个原始日志目录,切割后再把旧文件归档到别处。日志量大的环境,我更建议用logrotate的copytruncate,或者直接按日命名日志,让pgBadger输入的文件名保持稳定。
5.3 Top查询排序失真:总耗时第一不等于最该优化
现象:Top查询里排名第一的SQL执行上千次,每次平均几十毫秒,累计耗时排第一;真正单次执行好几秒的SQL排在十名开外。
原因:pgBadger默认按总耗时排序,总耗时是平均耗时乘执行次数。高次数低耗时的语句累计数字大,但优化空间有限;单次高耗时才是响应时间恶化的主要来源。
解决:看Top查询时,把平均耗时和最大耗时两列一起看,重点关注次数低但均值高的记录。我通常会用平均耗时倒序扫一遍,先处理单次几百毫秒以上的语句;如果业务指标反映的是接口偶发变慢,重点看最大耗时列。
5.4 临时文件板块噪声大:log_temp_files=0记录了一切
现象:临时文件报告统计出大量几KB到几十KB的小文件,但磁盘IO指标并没有异常。
原因:log_temp_files=0会把每一次临时文件创建都记录下来,排序、哈希、物化都会产生小临时文件,许多写入量只有几KB。报告统计的是数量和总量,小文件一多,板块被噪声刷满,真正的大临时文件反而不容易被看到。
解决:把log_temp_files设成1024,也就是只记录超过1MB的临时文件,这是生产环境常用的起点值。参数改完热加载即可,旧日志文件里已有的记录不受影响,需要观察新产生的日志。
5.5 pgBadger自身跑不动:大日志文件耗时与内存双高
现象:日志文件几个GB,跑一次pgBadger要好几分钟,期间内存占用飙到几个GB,有时还提示内存不足。
原因:stderr格式的日志靠正则逐行匹配,解析成本与文件行数成正比;如果日志里又开了log_statement=all,文件体积早就失控了。这是输入侧没控制好,不是pgBadger的设计问题。
解决:优先把日志格式切成csvlog,解析效率比stderr正则高一个量级;再配合按日logrotate,保证单日日志不要超过1GB;排查问题时先用-b和-e把时间窗口缩小再跑。我最早处理过一个8GB日志,硬啃到机器接近翻车,后来养成的习惯是先看文件大小,大文件先按时间分段。
6. 把pgBadger挂在日常巡检里:一条命令生成日报并归档
日志轮转加定时脚本,是pgBadger最省心的落地方式。前面把配置、命令、参数和坑都讲完了,最后给出一套可以直接放进crontab里的日报方案。假设日志按日命名为postgresql-YYYY-MM-DD.log:
#!/bin/bash LOG_DIR=/var/lib/postgresql/log OUT_DIR=/var/lib/pgbadger/daily YESTERDAY=$(date -d "yesterday" +%Y-%m-%d) LOGFILE="$LOG_DIR/postgresql-$YESTERDAY.log" if [ -f "$LOGFILE" ]; then pgbadger "$LOGFILE" -o "$OUT_DIR/pgbadger-$YESTERDAY.html" \ -T "$(hostname) PostgreSQL日报" fi find "$OUT_DIR" -name "pgbadger-*.html" -mtime +30 -delete脚本逻辑很直白:先确定昨天日期,文件名对得上就生成报告;-T参数给HTML加上标题,报告归档后不用打开就能认出是哪台机器、哪一天的;最后清理三十天前的旧报告,避免巡检目录无限膨胀。定时任务放在早上业务低峰之后:
0 7 * * * /opt/scripts/pgbadger_daily.sh >/dev/null 2>&1这样每天早上能看到昨天的报告,先对比概览里的慢查询数量和临时文件总量,跟前一天差异大就点进对应板块看细节。
这也是我自己坚持了很久的做法。最初我只开了log_min_duration_statement,报告里缺checkpoint和锁等待,遇到问题还得回头翻原始日志;后来把日志参数配全,pgBadger报告才真正成为每天巡检的存档。版本升级后格式有变化时,先拿一周历史日志试跑,对照报告确认数据正常后再交给定时任务,能省去很多返工。希望帮到你。
本文还有配套的精品资源,点击获取