Skip to content

日志分析与审计 ​

核心问题:系统视图和指标监控告诉我们"现在怎么样",但无法回答"过去发生了什么"。日志是事后分析、安全审计和慢查询回溯的关键数据源。如何配置日志级别、选择合适的日志格式、利用工具自动分析,同时不让日志 IO 成为新的性能瓶颈?

1. 日志配置详解 ​

postgresql.conf 核心日志参数 ​

ini
# === 日志输出方式 ===
logging_collector = on          # 启用日志收集器(生产环境必须开启)
log_destination = 'csvlog'      # 推荐 csvlog,便于工具解析;可选 stderr, jsonlog(PG15+)
log_directory = 'log'           # 日志目录(相对于 data_directory)
log_filename = 'postgresql-%Y-%m-%d.log'  # 日志文件名模式
log_rotation_age = 1d           # 按天轮转
log_rotation_size = 100MB       # 按大小轮转(与 age 取 OR)
log_truncate_on_rotation = on   # 轮转时覆盖同名文件

# === 记录哪些语句 ===
log_statement = 'ddl'           # none / ddl / mod / all
                                # none: 不记录语句
                                # ddl: 只记录 DDL(CREATE/ALTER/DROP)
                                # mod: 记录 DDL + DML(INSERT/UPDATE/DELETE)
                                # all: 记录所有语句(不建议生产环境)

# === 慢查询记录 ===
log_min_duration_statement = 1000  # 单位 ms,执行超过此时间的语句记录到日志
                                   # 0 = 记录所有语句的执行时间
                                   # -1 = 关闭

# === 其他重要参数 ===
log_checkpoints = on            # 记录检查点信息(强烈建议开启)
log_connections = on            # 记录连接建立
log_disconnections = on         # 记录连接断开(含会话持续时间)
log_lock_waits = on             # 记录锁等待超时
log_temp_files = 0              # 记录所有临时文件使用(0 = 全部记录)
log_autovacuum_min_duration = 0 # 记录所有 autovacuum 操作
log_line_prefix = '%m [%p] %u@%d '  # 日志行前缀

log_line_prefix 常用占位符 ​

占位符含义推荐
%t时间戳(无毫秒)-
%m时间戳(含毫秒)✅ 推荐
%p进程 ID✅ 推荐
%u用户名✅ 推荐
%d数据库名✅ 推荐
%r客户端地址和端口✅ 推荐
%h客户端主机名-
%a应用名称✅ 推荐
%l日志行号-
%s会话开始时间-
ini
# 推荐的 log_line_prefix 配置
log_line_prefix = '%m [%p] %u@%d/%a %r '
# 示例输出: 2024-01-15 10:30:45.123 CST [12345] app_user@mydb/app 192.168.1.100:5432

Java 应用连接配置 ​

yaml
# application.yml - 让日志能追踪到 Java 应用
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb?ApplicationName=my-spring-app
    hikari:
      data-source-properties:
        ApplicationName: my-spring-app

2. csvlog 格式解析 ​

csvlog 是 PostgreSQL 推荐的日志格式,每行一条记录,字段用逗号分隔,方便导入数据库分析。

csvlog 字段(PG 15 为例,共 23 列) ​

列号字段说明
1log_time日志时间
2user_name用户名
3database_name数据库名
4process_id进程 ID
5connection_from客户端地址
6session_id会话 ID
7session_line_num会话内行号
8command_tag命令标签
9session_start_time会话开始时间
10virtual_transaction_id虚拟事务 ID
11transaction_id事务 ID
12error_severity错误级别
13sql_stateSQLSTATE 错误码
14message日志消息
15detail详细信息
16hint提示
17internal_query内部查询
18internal_query_pos内部查询位置
19context上下文
20query用户查询
21query_pos查询位置
22location代码位置
23application_name应用名称

导入 csvlog 到数据库分析 ​

sql
-- 创建日志表
CREATE TABLE IF NOT EXISTS pg_log (
    log_time timestamptz,
    user_name text,
    database_name text,
    process_id integer,
    connection_from text,
    session_id text,
    session_line_num bigint,
    command_tag text,
    session_start_time timestamptz,
    virtual_transaction_id text,
    transaction_id bigint,
    error_severity text,
    sql_state text,
    message text,
    detail text,
    hint text,
    internal_query text,
    internal_query_pos integer,
    context text,
    query text,
    query_pos integer,
    location text,
    application_name text
);

-- 导入日志文件
COPY pg_log FROM '/var/lib/postgresql/data/log/postgresql-2024-01-15.csv' WITH (FORMAT csv);

-- 慢查询 Top 10
SELECT log_time, user_name, database_name, 
       LEFT(message, 200) AS message,
       LEFT(query, 200) AS query
FROM pg_log
WHERE message LIKE 'duration:%'
ORDER BY CAST(REPLACE(SPLIT_PART(message, ' ', 2), 'ms', '') AS numeric) DESC
LIMIT 10;

-- 错误统计
SELECT error_severity, sql_state, COUNT(*), 
       MIN(log_time) AS first_seen, MAX(log_time) AS last_seen
FROM pg_log
WHERE error_severity IN ('ERROR', 'FATAL', 'PANIC')
GROUP BY error_severity, sql_state
ORDER BY COUNT(*) DESC;

3. pgBadger 日志分析工具 ​

pgBadger 是 PostgreSQL 日志分析的瑞士军刀,能从 csvlog/stderr 中生成丰富的 HTML 报告。

安装 ​

bash
# Debian/Ubuntu
sudo apt-get install pgbadger

# 或从源码安装
wget https://github.com/darold/pgbadger/releases/download/v12.4/pgbadger-12.4.tar.gz
tar xzf pgbadger-*.tar.gz
cd pgbadger-*/
perl Makefile.PL
make && sudo make install

使用 ​

bash
# 分析单个日志文件
pgbadger /var/lib/postgresql/data/log/postgresql-2024-01-15.csv -o report.html

# 分析多个日志文件
pgbadger /var/lib/postgresql/data/log/postgresql-2024-01-*.csv -o report.html

# 增量分析(每天生成报告,支持历史对比)
pgbadger --last-parsed /tmp/pgbadger_last \
         /var/lib/postgresql/data/log/postgresql-*.csv \
         -o /var/www/pgbadger/report_$(date +%Y%m%d).html

# 并行处理(大日志文件加速)
pgbadger -j 4 /var/lib/postgresql/data/log/postgresql-*.csv -o report.html

# JSON 输出(便于程序化处理)
pgbadger -f json /var/lib/postgresql/data/log/postgresql-*.csv -o report.json

自动化脚本 ​

bash
#!/bin/bash
# /etc/cron.daily/pgbadger-analysis.sh

LOG_DIR="/var/lib/postgresql/data/log"
REPORT_DIR="/var/www/pgbadger"
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
LAST_PARSED="/tmp/pgbadger_last"

pgbadger \
  --last-parsed "$LAST_PARSED" \
  --prefix='%m [%p] %u@%d/%a %r ' \
  "${LOG_DIR}/postgresql-${YESTERDAY}.csv" \
  -o "${REPORT_DIR}/report_${YESTERDAY}.html"

# 清理 30 天前的报告
find "$REPORT_DIR" -name "report_*.html" -mtime +30 -delete

pgBadger 报告关键内容 ​

模块关注点
Overall Statistics总查询数、TPS、连接数
Slowest Queries最慢的 SQL Top N
Time Consuming Queries总耗时最高的 SQL
Most Frequent Queries高频 SQL
Queries by User按用户统计
Errors错误和致命错误
Checkpoints检查点频率和时长
Connections连接/断开趋势

4. 审计日志配置(pgAudit 扩展) ​

pgAudit 提供细粒度的审计能力,记录谁在什么时间做了什么操作,满足合规要求。

安装和配置 ​

ini
# postgresql.conf
shared_preload_libraries = 'pgaudit'

# pgaudit 审核级别
pgaudit.log = 'ddl, role, write'
# ddl: CREATE/ALTER/DROP 等
# role: GRANT/REVOKE
# write: INSERT/UPDATE/DELETE/TRUNCATE
# read: SELECT(通常不开,日志量太大)
# function: 函数调用
# misc: DISCARD, FETCH, CHECKPOINT 等
# all: 以上全部

pgaudit.log_catalog = on          # 是否记录系统表访问
pgaudit.log_parameter = on        # 是否记录 SQL 参数
pgaudit.log_statement_once = on   # 每条语句只记录一次(减少重复)
pgaudit.log_level = 'log'         # 日志级别:debug5..log..warning
sql
-- 启用扩展
CREATE EXTENSION IF NOT EXISTS pgaudit;

-- 针对特定角色启用审计(更精细的控制)
-- 不设置 pgaudit.log 参数,改为针对角色设置
ALTER ROLE auditor SET pgaudit.log = 'all';

-- 针对特定表审计
-- 先设置 pgaudit.role,然后将该角色的权限授予需要审计的对象
CREATE ROLE auditor_role;
ALTER DATABASE mydb SET pgaudit.role = 'auditor_role';
GRANT SELECT, INSERT, UPDATE, DELETE ON sensitive_table TO auditor_role;

pgAudit 日志示例 ​

txt
2024-01-15 10:30:45.123 CST [12345] app_user@mydb LOG:  AUDIT: SESSION,1,1,WRITE,INSERT,,,
"INSERT INTO orders (user_id, amount) VALUES (123, 99.99);",<none>

5. 日志与监控的配合策略 ​

分层监控体系 ​

txt
┌─────────────────────────────────────────────────────┐
│                    告警层 (PagerDuty/企微/钉钉)       │
├─────────────────────────────────────────────────────┤
│  指标监控 (Prometheus + Grafana)                      │
│  - 实时采集系统视图                                    │
│  - 秒级/分钟级指标                                    │
│  - 阈值告警                                          │
├─────────────────────────────────────────────────────┤
│  日志分析 (pgBadger + ELK)                            │
│  - 慢查询详细分析                                     │
│  - 错误和异常追踪                                     │
│  - 审计合规                                          │
├─────────────────────────────────────────────────────┤
│  SQL 分析 (pg_stat_statements)                        │
│  - Top N 资源消耗 SQL                                 │
│  - 执行计划变化检测                                    │
└─────────────────────────────────────────────────────┘

配合方式 ​

场景指标监控日志分析SQL 分析
突然变慢发现异常 →查看当时日志 →定位具体 SQL
报错率上升发现回滚增加 →查看 ERROR 日志 →分析报错 SQL
连接耗尽告警连接数 →查看连接建立日志 →分析连接来源
审计需求-pgAudit 日志 →-

6. 日志对 IO 的影响及优化 ​

日志 IO 影响评估 ​

log_statement日志量IO 影响推荐
none最小无开发环境
ddl小极低生产环境默认
mod中低需要审计 DML
all极大高仅调试用
log_min_duration_statement日志量IO 影响推荐
-1 (关闭)无无不推荐
0 (全部)极大高仅调试
1000 (1秒)中低生产推荐
5000 (5秒)小极低保守配置

优化建议 ​

ini
# 1. 日志存放在独立磁盘/volume
log_directory = '/pg_log'   # 与 data_directory 不同的磁盘

# 2. 使用 SSD 存放日志
# 将日志目录挂载到 SSD

# 3. 合理的轮转策略
log_rotation_age = 1d
log_rotation_size = 100MB
log_truncate_on_rotation = on

# 4. 关闭不必要的日志(生产环境)
log_statement = 'ddl'
log_connections = off       # 如果不需要审计连接,关闭以减少日志量
log_disconnections = off    # 同上

# 5. 使用 syslog 代替文件日志(高 IO 场景)
# log_destination = 'syslog'
# syslog_facility = 'local0'
# syslog_ident = 'postgres'

日志存储规划 ​

bash
# 估算日志量
# 假设:TPS 1000,log_min_duration_statement=1000,约 1% 慢查询
# 每条日志约 500 bytes
# 每天日志量 ≈ 1000 * 86400 * 1% * 500 ≈ 430 MB/天

# 磁盘规划
# 保留 30 天日志 → 约 13 GB
# 加上 csvlog 冗余 → 约 26 GB
# 建议预留 50 GB

# 自动清理脚本
find /pg_log -name "postgresql-*.csv" -mtime +30 -delete
find /pg_log -name "postgresql-*.log" -mtime +30 -delete

小结 ​

日志是监控体系中不可或缺的一环。指标监控发现异常,日志分析定位根因。配置时要在"信息充分"和"IO 开销"之间找到平衡——生产环境推荐 log_statement = 'ddl' + log_min_duration_statement = 1000ms 的组合,既不会遗漏关键信息,也不会给 IO 带来过大压力。审计场景使用 pgAudit 扩展,日志分析使用 pgBadger,三者配合构成完整的日志监控方案。

Released under the MIT License.