跳转到内容

日志管理

日志是排查问题、审计安全事件、分析系统行为的核心数据来源。本文介绍如何在 EL 系发行版上有效地管理和分析日志。

在 CentOS/AlmaLinux/Rocky Linux 中,日志系统由两个核心组件构成:

  • journald(systemd-journald):systemd 的日志守护进程,收集所有 systemd 单元、内核和启动日志,存储为二进制格式
  • rsyslog:传统的 syslog 守护进程,接收 journald 转发的日志,写入文本文件,支持远程传输

两者协同工作:journald 负责收集,rsyslog 负责持久化存储和转发。

应用/服务 → journald(二进制日志)→ rsyslog → /var/log/下的文本日志
远程日志服务器
Terminal window
# 查看所有日志
journalctl
# 查看最新的日志(类似 tail -f)
journalctl -f
# 只看最近 100 行
journalctl -n 100
# 查看某个服务的日志
journalctl -u nginx
journalctl -u sshd -f
# 查看内核日志
journalctl -k
# 按时间范围查看
journalctl --since "2026-03-20 00:00:00" --until "2026-03-20 23:59:59"
journalctl --since "1 hour ago"
journalctl --since today
# 按优先级过滤(0=emerg, 3=err, 4=warning, 6=info)
journalctl -p err # 只看错误及以上
journalctl -p warning -u nginx # nginx 的警告级别日志
# 输出为 JSON 格式
journalctl -u nginx -o json-pretty -n 5
# 查看磁盘使用量
journalctl --disk-usage

默认情况下,journald 日志可能仅存储在内存中(/run/log/journal/),重启后丢失。开启持久化存储:

Terminal window
# 创建持久化目录
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
# 编辑配置
sudo tee /etc/systemd/journald.conf.d/persistent.conf > /dev/null <<'EOF'
[Journal]
Storage=persistent
SystemMaxUse=2G
SystemMaxFileSize=256M
MaxRetentionSec=3month
Compress=yes
EOF
# 重启 journald
sudo systemctl restart systemd-journald

关键参数说明:

参数说明
Storage=persistent持久化存储到磁盘
SystemMaxUse=2G日志最多占用 2G 磁盘空间
SystemMaxFileSize=256M单个日志文件最大 256M
MaxRetentionSec=3month最多保留 3 个月
Compress=yes启用压缩
Terminal window
# 查看 rsyslog 默认规则
cat /etc/rsyslog.conf
# EL 系默认的日志分布:
# /var/log/messages - 大多数系统日志
# /var/log/secure - 认证与授权日志
# /var/log/maillog - 邮件日志
# /var/log/cron - cron 任务日志
# /var/log/boot.log - 启动日志
Terminal window
# 为特定程序创建独立的日志文件
sudo tee /etc/rsyslog.d/myapp.conf > /dev/null <<'EOF'
# 将 myapp 标签的日志写入独立文件
if $programname == 'myapp' then /var/log/myapp.log
& stop
# 将所有 local6 设施的日志写入指定文件
local6.* /var/log/custom-app.log
EOF
sudo systemctl restart rsyslog

rsyslog 支持自定义日志格式:

Terminal window
sudo tee /etc/rsyslog.d/custom-template.conf > /dev/null <<'EOF'
# 自定义日志格式模板
template(name="CustomFormat" type="string"
string="%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg%\n"
)
# 对特定日志应用自定义格式
local6.* /var/log/custom-app.log;CustomFormat
EOF

在日志收集服务器上配置接收端:

Terminal window
sudo tee /etc/rsyslog.d/remote.conf > /dev/null <<'EOF'
# 启用 TCP 接收(端口 514)
module(load="imtcp")
input(type="imtcp" port="514")
# 按主机名分目录存储远程日志
template(name="RemoteHost" type="string"
string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log"
)
# 远程日志使用自定义存储路径
if $fromhost-ip != '127.0.0.1' then {
action(type="omfile" dynaFile="RemoteHost")
stop
}
EOF
sudo systemctl restart rsyslog
# 防火墙放行
sudo firewall-cmd --permanent --add-port=514/tcp
sudo firewall-cmd --reload

在客户端上配置日志转发:

Terminal window
sudo tee /etc/rsyslog.d/forward.conf > /dev/null <<'EOF'
# 将所有日志通过 TCP 转发到远程服务器
*.* @@log-server.example.com:514
# @ 表示 UDP
# @@ 表示 TCP(推荐,可靠性更高)
# 如果远程不可用,启用磁盘缓冲队列
action(
type="omfwd"
target="log-server.example.com"
port="514"
protocol="tcp"
queue.type="LinkedList"
queue.filename="forward_queue"
queue.maxdiskspace="1g"
queue.saveonshutdown="on"
action.resumeRetryCount="-1"
)
EOF
sudo systemctl restart rsyslog

生产环境中,日志传输应当加密:

Terminal window
# 安装 TLS 模块
sudo dnf install rsyslog-gnutls -y
# 日志服务器端 TLS 配置
sudo tee /etc/rsyslog.d/tls-server.conf > /dev/null <<'EOF'
global(
defaultNetstreamDriver="gtls"
defaultNetstreamDriverCAFile="/etc/pki/rsyslog/ca.pem"
defaultNetstreamDriverCertFile="/etc/pki/rsyslog/server-cert.pem"
defaultNetstreamDriverKeyFile="/etc/pki/rsyslog/server-key.pem"
)
module(load="imtcp"
StreamDriver.Name="gtls"
StreamDriver.Mode="1"
StreamDriver.AuthMode="x509/name"
)
input(type="imtcp" port="6514")
EOF
# 客户端 TLS 配置
sudo tee /etc/rsyslog.d/tls-client.conf > /dev/null <<'EOF'
global(
defaultNetstreamDriver="gtls"
defaultNetstreamDriverCAFile="/etc/pki/rsyslog/ca.pem"
defaultNetstreamDriverCertFile="/etc/pki/rsyslog/client-cert.pem"
defaultNetstreamDriverKeyFile="/etc/pki/rsyslog/client-key.pem"
)
action(
type="omfwd"
target="log-server.example.com"
port="6514"
protocol="tcp"
StreamDriver="gtls"
StreamDriverMode="1"
StreamDriverAuthMode="x509/name"
)
EOF

logrotate 防止日志文件无限增长,自动压缩和清理旧日志。

Terminal window
cat /etc/logrotate.conf
# 默认设置:
# - 每周轮转
# - 保留 4 周
# - 创建新文件
# - 包含 /etc/logrotate.d/ 目录下的配置
Terminal window
sudo tee /etc/logrotate.d/myapp > /dev/null <<'EOF'
/var/log/myapp.log
/var/log/myapp-error.log
{
daily # 每天轮转
missingok # 文件不存在不报错
rotate 30 # 保留 30 份
compress # gzip 压缩
delaycompress # 延迟一次再压缩(便于程序继续写入)
notifempty # 空文件不轮转
create 0640 myapp myapp # 创建新文件的权限和属主
dateext # 使用日期作为后缀(而非数字)
dateformat -%Y%m%d # 日期格式
sharedscripts # 多个文件只执行一次脚本
postrotate
# 轮转后通知应用重新打开日志文件
/bin/kill -USR1 $(cat /var/run/myapp.pid 2>/dev/null) 2>/dev/null || true
endscript
}
EOF
Terminal window
sudo tee /etc/logrotate.d/nginx > /dev/null <<'EOF'
/var/log/nginx/*.log {
daily
missingok
rotate 60
compress
delaycompress
notifempty
create 0640 nginx adm
dateext
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
EOF
Terminal window
# 调试模式(不实际执行,只显示会做什么)
sudo logrotate -d /etc/logrotate.d/myapp
# 强制执行轮转
sudo logrotate -f /etc/logrotate.d/myapp
# 查看 logrotate 状态文件
cat /var/lib/logrotate/logrotate.status
Terminal window
# 搜索错误信息
grep -i "error" /var/log/messages
grep -i "failed" /var/log/secure
# 搜索特定时间段
grep "Mar 20 1[0-5]:" /var/log/messages # 3月20日 10:00-15:59 的日志
# 排除干扰信息
grep -i "error" /var/log/messages | grep -v "No error"
# 递归搜索目录
grep -r "out of memory" /var/log/
# 显示匹配行前后的上下文
grep -B 3 -A 5 "kernel panic" /var/log/messages
# 统计出现次数
grep -c "Failed password" /var/log/secure
Terminal window
# 统计各 IP 的 SSH 失败登录次数
awk '/Failed password/ {for(i=1;i<=NF;i++) if($i=="from") print $(i+1)}' /var/log/secure \
| sort | uniq -c | sort -rn | head -20
# 按小时统计 Nginx 请求数
awk '{print $4}' /var/log/nginx/access.log \
| cut -d: -f1-2 | sort | uniq -c
# 统计 HTTP 状态码分布
awk '{print $9}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn
# 找出请求最慢的 URL(假设响应时间在最后一列)
awk '{print $NF, $7}' /var/log/nginx/access.log \
| sort -rn | head -20
# 统计每分钟请求量(找出流量高峰)
awk '{print $4}' /var/log/nginx/access.log \
| cut -d: -f1-3 | sort | uniq -c | sort -rn | head -10
Terminal window
# 提取特定时间段的日志(journalctl 格式)
sed -n '/^Mar 20 14:00/,/^Mar 20 15:00/p' /var/log/messages
# 实时监控并高亮关键字
tail -f /var/log/messages | grep --color=auto -i "error\|warning\|fail"
# 使用 last 查看登录记录
last -20 # 最近 20 条登录记录
lastb -20 # 最近 20 条失败登录
last reboot # 重启记录
# 使用 ausearch 搜索审计日志(需要 auditd)
ausearch -m avc # SELinux 拒绝事件
ausearch -k login_events # 按审计规则搜索
/usr/local/bin/log_summary.sh
#!/bin/bash
# 每日日志摘要报告
DATE=$(date -d yesterday '+%b %d')
REPORT="/tmp/log_summary_$(date +%Y%m%d).txt"
{
echo "=========================================="
echo " 日志摘要报告 - ${DATE}"
echo "=========================================="
echo ""
echo "--- SSH 失败登录 TOP 10 ---"
grep "${DATE}" /var/log/secure 2>/dev/null \
| grep "Failed password" \
| awk '{for(i=1;i<=NF;i++) if($i=="from") print $(i+1)}' \
| sort | uniq -c | sort -rn | head -10
echo ""
echo "--- 错误日志统计 ---"
echo "messages 中的 error: $(grep -ci "error" /var/log/messages 2>/dev/null || echo 0)"
echo "secure 中的 failure: $(grep -ci "fail" /var/log/secure 2>/dev/null || echo 0)"
echo ""
echo "--- 磁盘空间告警 ---"
df -h | awk 'NR>1 {gsub(/%/,"",$5); if($5+0>80) print $0}'
echo ""
echo "--- 最近重启记录 ---"
last reboot | head -5
echo ""
echo "--- OOM Killer 事件 ---"
grep -c "Out of memory" /var/log/messages 2>/dev/null || echo "0 次"
} > "$REPORT"
cat "$REPORT"
# 可选:邮件发送
# mail -s "日志摘要 ${DATE}" admin@example.com < "$REPORT"

当服务器数量增多时,需要专门的日志平台进行统一收集、搜索和分析。

ELK Stack(Elasticsearch + Logstash + Kibana)

Section titled “ELK Stack(Elasticsearch + Logstash + Kibana)”

ELK 是最流行的日志分析平台:

  • Elasticsearch:日志存储和搜索引擎
  • Logstash:日志收集和处理管道
  • Kibana:Web 可视化界面

使用 Filebeat 作为轻量级日志采集器:

Terminal window
# 安装 Filebeat
sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
sudo tee /etc/yum.repos.d/elastic.repo > /dev/null <<'EOF'
[elastic-8.x]
name=Elastic repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF
sudo dnf install filebeat -y
# 配置 Filebeat 采集系统日志
sudo tee /etc/filebeat/filebeat.yml > /dev/null <<'EOF'
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/messages
- /var/log/secure
fields:
type: syslog
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
fields:
type: nginx-access
output.elasticsearch:
hosts: ["https://elasticsearch:9200"]
username: "elastic"
password: "your_password"
EOF
sudo systemctl enable --now filebeat

Loki 是 Grafana 推出的轻量级日志聚合系统,对标 ELK 但资源消耗更低:

Terminal window
# 安装 Promtail(Loki 的日志采集器)
cd /tmp
curl -LO https://github.com/grafana/loki/releases/download/v3.0.0/promtail-linux-amd64.zip
unzip promtail-linux-amd64.zip
sudo cp promtail-linux-amd64 /usr/local/bin/promtail
sudo chmod +x /usr/local/bin/promtail
# 基础配置
sudo mkdir -p /etc/promtail
sudo tee /etc/promtail/config.yml > /dev/null <<'EOF'
server:
http_listen_port: 9080
positions:
filename: /var/lib/promtail/positions.yaml
clients:
- url: http://loki-server:3100/loki/api/v1/push
scrape_configs:
- job_name: syslog
static_configs:
- targets:
- localhost
labels:
job: syslog
host: __HOSTNAME__
__path__: /var/log/messages
- job_name: nginx
static_configs:
- targets:
- localhost
labels:
job: nginx
host: __HOSTNAME__
__path__: /var/log/nginx/*.log
EOF
# 替换主机名
sudo sed -i "s/__HOSTNAME__/$(hostname)/" /etc/promtail/config.yml

Loki 的优势在于它不索引日志全文,而是通过标签(labels)组织日志流,资源消耗远低于 Elasticsearch,非常适合中小规模部署。

任务工具
查看服务日志journalctl -u 服务名
查看系统日志/var/log/messagesjournalctl
日志持久化journald Storage=persistent
日志轮转logrotate
远程集中收集rsyslog TCP/TLS 转发
日志搜索分析grep / awk / journalctl 过滤
大规模日志平台ELK Stack 或 Grafana Loki

养成定期检查日志的习惯,并通过自动化脚本或日志平台及时发现异常,是保障服务器稳定运行的关键实践。