Linux 日志管理
本文合并自:1-8-Linux系统服务与日志管理.md(日志管理部分)、03-Rsyslog日志服务.md、08-日志与系统信息.md、09-日志管理.md、05-日志轮转练习.md
适用环境:RHEL/CentOS 5(syslog)、RHEL/CentOS 6(rsyslog)、RHEL/CentOS 7(rsyslog + systemd-journald 并存)。
内容覆盖:日志的作用与分类、
/var/log下常见日志文件、二进制日志与专用查看命令、rsyslog 架构与配置语法、facility/level 全表、远程日志集中收集实操、logger测试、systemd-journald 与journalctl、logrotate日志轮转(含完整练习与参考答案)、日志分析实战、auditd 审计、常见故障排错清单。系统启动流程、运行级别与服务管理请见
08-01;ELK 日志分析平台请见08-04-ELK日志分析.md;Zabbix 监控告警请见08-05-Zabbix监控.md。
一、日志的作用与分类
1.1 什么是日志
日志用于记录系统或应用在运行过程中产生的信息。通过日志,运维人员可以:
- 了解系统或应用的运行状态;
- 排查故障、定位错误原因;
- 审计安全事件(谁在什么时间做了什么);
- 分析业务访问量、性能瓶颈与趋势。
系统日志记录系统上的各种活动:用户登入登出、系统开关机、Web/FTP 访问、端口扫描、服务启停、内核硬件检测等,供管理员排查故障、发现潜在问题。
1.2 日志系统的工作方式
Linux 的日志系统本身是一个系统服务。其他进程把日志信息发给日志服务,日志服务根据数据特征(服务类型 facility、级别 level)结合自身配置,将信息写入相应文件或做其他处理(转发、广播、丢弃等)。
应用进程 / 内核 → 日志服务(rsyslog / journald) → 按规则分流
├── 写入 /var/log/*.log
├── 输出到终端 / 广播用户
├── 转发到远程日志服务器
└── 丢弃
1.3 日志的分类
| 分类 | 说明 | 典型位置 / 查看方式 |
|---|---|---|
| 内核日志 | 内核启动与运行期间产生的信息,硬件检测、驱动加载、OOM、磁盘错误等 | 内核环形缓冲区,dmesg、/var/log/dmesg、journalctl -k |
| 系统日志 | 系统与各类守护进程的通用运行信息 | /var/log/messages、journalctl |
| 应用日志 | 各应用自行管理的日志(Apache、Nginx、MySQL、Tomcat 等) | /var/log/httpd/、/var/log/nginx/、/var/log/mysqld.log 等 |
| 审计日志 | 内核审计子系统 auditd 记录的系统调用、文件访问、权限变更 | /var/log/audit/audit.log,ausearch/aureport |
| 用户登录日志 | 用户登录成功/失败、当前在线、最后登录时间 | wtmp/btmp/utmp/lastlog(二进制),last/lastb/who/w/lastlog |
| 安全认证日志 | SSH、FTP、su、sudo、PAM 等认证与授权事件 | /var/log/secure |
1.4 各版本日志服务对照
| 系统版本 | 服务名称 | 主配置文件 | 说明 |
|---|---|---|---|
| RHEL/CentOS 5 | syslog |
/etc/syslog.conf |
传统 syslogd,功能较少 |
| RHEL/CentOS 6 | rsyslog |
/etc/rsyslog.conf |
syslog 增强版:模块化、TCP/UDP 远程传输、模板、过滤、写数据库 |
| RHEL/CentOS 7 | rsyslog + systemd-journald |
/etc/rsyslog.conf、/etc/systemd/journald.conf |
journald 统一采集,可转发 rsyslog 落盘为文本,两者并存 |
rsyslog 是 syslog 的增强版本,支持模块化、TCP/UDP 远程传输、模板、过滤等更多特性。
查看 syslog 相关的设施与级别定义,可借助 xinetd 手册:
man xinetd.conf
查找 log_type:
log_type determines where the service log output is sent. There are two formats:
SYSLOG syslog_facility [syslog_level]
The log output is sent to syslog at the specified facility. Possible facility names
include: daemon, auth, authpriv, user, mail, lpr, news, uucp, ftp local0-7. Possible
level names include: emerg, alert, crit, err, warning, notice, info, debug. If a level
is not present, the messages will be recorded at the info level.
二、/var/log 下常见日志文件对照表
2.1 总览表
| 日志文件 | 用途 | 查看方法 |
|---|---|---|
/var/log/messages |
最重要的系统整体日志,大多数服务的通用信息,几乎所有系统重要错误都会记录在此 | tail -f /var/log/messages、grep |
/var/log/secure |
登录认证与安全相关:SSH、FTP、Telnet、POP3、PAM、su、sudo |
tail -f /var/log/secure、grep Failed |
/var/log/cron |
crond 定时任务执行日志 | tail -f /var/log/cron |
/var/log/boot.log |
系统启动过程日志,也包含某些服务的启动日志 | cat /var/log/boot.log |
/var/log/dmesg |
开机时内核检测过程中产生的硬件信息 | cat /var/log/dmesg、dmesg |
/var/log/maillog |
邮件服务(postfix/sendmail)相关日志 | tail -f /var/log/maillog |
/var/log/spooler |
新闻组、UUCP 等 spool 类日志(现代系统基本为空) | cat /var/log/spooler |
/var/log/yum.log |
yum 安装/卸载/更新软件包的操作日志 |
cat /var/log/yum.log |
/var/log/acpid |
高级电源管理(ACPI)日志 | cat /var/log/acpid |
/var/log/wtmp |
所有用户登录、注销记录(二进制) | last |
/var/log/btmp |
失败的登录记录(二进制) | lastb |
/var/run/utmp |
当前登录用户信息(二进制) | who、w |
/var/log/lastlog |
每个用户最后一次登录信息(二进制) | lastlog |
/var/log/audit/audit.log |
auditd 审计子系统日志 | ausearch、aureport、tail |
/var/log/httpd/access_log、error_log |
Apache 访问日志与错误日志(目录 /var/log/httpd/) |
tail -f、awk 统计 |
/var/log/nginx/access.log、error.log |
Nginx 访问日志与错误日志 | tail -f、awk 统计 |
/var/log/mysqld.log(或 /var/log/mariadb/mariadb.log) |
MySQL/MariaDB 服务运行日志、启动失败原因、慢查询提示 | tail -f /var/log/mysqld.log |
/var/log/journal/ 或 /run/log/journal/ |
systemd-journald 的二进制日志库 | journalctl |
注意:
wtmp、btmp、utmp、lastlog等文件不是纯文本,必须使用last、lastb、who、lastlog等工具查看,直接cat会输出乱码。
2.2 常用查看命令
# 实时跟踪日志尾部(排障最常用)
tail -f /var/log/messages
tail -f /var/log/secure
tailf /var/log/messages # 旧系统上的等价命令
# 查看最后 N 行
tail -n 100 /var/log/messages
tail -100 /var/log/messages
# 从头分页阅读
less /var/log/messages
more /var/log/messages
# 关键字检索
grep -i error /var/log/messages
grep -n 'Failed password' /var/log/secure
# 查看日志文件大小与目录占用
ls -lh /var/log/
du -sh /var/log/
du -sh /var/log/* | sort -rh | head -10
三、二进制日志与专用命令
3.1 wtmp 与 last
/var/log/wtmp 记录所有用户的登录与注销历史,是二进制文件,用 last 读取。
last # 所有用户登入登出记录
last -F # 打印完整时间(含年份与秒)
last -n 10 # 只看最近 10 条
last root # 只看 root 的登录记录
last -x # 显示系统关机、运行级别变更等记录
last -x | grep -E '^(shutdown|reboot)' # 查看开机/关机时间
last -f /var/log/wtmp.1 # 指定读取轮转后的旧文件
说明:
last读取/var/log/wtmp(二进制);- 删除用户后
last仍能看到其历史记录,而lastlog则看不到。
3.2 btmp 与 lastb
/var/log/btmp 记录失败的登录尝试,是排查暴力破解的关键。
lastb # 登录失败记录(默认仅 root 可查看)
lastb -n 20 # 最近 20 条失败记录
lastb | head -50
ls -l /var/log/btmp
# -rw-------. 1 root utmp 6528 May 9 19:26 /var/log/btmp
说明:lastb 读取 /var/log/btmp(二进制),权限通常为 -rw-------,属主 root、属组 utmp,因此普通用户无法查看。
3.3 utmp 与 who / w
/var/run/utmp 记录当前登录用户,用 who、w 查看。
who # 查看当前在线用户
who -b # 最后一次系统启动时间
who -r # 查看当前运行级别
who -aH # 列出当前用户、系统引导、运行级别等全部信息(带表头)
w # 在线用户及其正在执行的命令、负载
whoami # 当前有效用户名
who -aH 输出示例:
系统引导 2018-10-25 17:56
运行级别 5 2018-10-25 09:56
soul ? :0 2018-10-25 09:56 ? 2115 (:0)
soul + pts/0 2018-10-25 09:57 旧的 3201 (:0)
配套的消息发送命令(用于通知在线用户即将维护):
write soul /dev/pts/5 # 给指定用户/终端发消息,Ctrl+D 结束
wall # 给所有在线用户发消息,Ctrl+D 结束
mesg n # 关闭接收消息(普通用户无法再给该用户发消息)
mesg y # 打开接收消息
echo "Message content" > /dev/pts/5 # 即使用户 mesg n,root 仍可向其终端写入
3.4 lastlog
/var/log/lastlog 记录每个用户最后一次登录的时间与来源。
lastlog # 所有用户最近一次登录记录(不显示图形界面登录)
lastlog -u root # 指定用户
lastlog -t 7 # 只显示最近 7 天内登录过的用户
3.5 dmesg 与内核环形缓冲区
内核把启动与运行期间的消息写入一块固定大小的环形缓冲区(ring buffer),新消息会覆盖最旧的消息。dmesg 用于读取该缓冲区。
dmesg # 查看内核环形缓冲区全部内容
dmesg | less # 分页阅读
dmesg -T # 把时间戳转换为人类可读的日期时间(重要!)
dmesg -T | tail -30 # 最近 30 条内核消息
dmesg -l err,crit # 只看错误及以上级别
dmesg -k # 只看内核消息
dmesg -w # 实时跟踪新产生的内核消息(等价于 tail -f)
dmesg -c # 显示后清空缓冲区
dmesg -H # 人性化输出(分页 + 彩色 + 相对时间)
dmesg | grep -i -E 'error|fail|sda|eth' # 排查磁盘/网卡/硬件问题
默认
dmesg的时间戳是"系统启动后经过的秒数",很难直接对应真实时间,排障时务必加-T。CentOS 7 及以上也可用
journalctl -k(等价于dmesg)、journalctl -k -b -1(上次启动的内核日志)。
四、rsyslog 日志服务
4.1 rsyslog 架构与三大模块
rsyslog 采用模块化设计,日志处理流程可归纳为三个阶段:
input(输入/采集) → filter(过滤/规则匹配) → output(输出/动作)
| 阶段 | 作用 | 对应模块示例 |
|---|---|---|
| input | 采集日志:本地 socket、内核、远程网络、文件 | imuxsock、imklog、imudp、imtcp、imfile、imjournal |
| filter | 按 facility.level 选择器、属性、表达式过滤日志 | 选择器语法、if ... then、property filter |
| output | 决定日志去向:写文件、写终端、转发、入库、执行程序、丢弃 | omfile、omfwd、ommysql、omprog |
常用模块说明:
| 模块 | 类型 | 作用 |
|---|---|---|
imuxsock |
input | 接收本地 Unix socket(/dev/log)的日志,即 logger、应用 syslog() 调用的来源 |
imklog |
input | 读取内核日志(内核环形缓冲区) |
imjournal |
input | 从 systemd journal 读取日志(CentOS 7 默认方式之一) |
imudp |
input | 以 UDP 方式接收远程日志(默认 514) |
imtcp |
input | 以 TCP 方式接收远程日志(默认 514),可靠但开销略高 |
imfile |
input | 把普通文本文件(如 Nginx access.log)纳入 rsyslog 采集 |
omfile |
output | 写入本地文件(内置,默认动作) |
omfwd |
output | 转发到远程日志服务器(@ UDP / @@ TCP) |
ommysql |
output | 写入 MySQL 数据库 |
omprog |
output | 把日志交给外部程序处理 |
4.2 /etc/rsyslog.conf 结构
主配置文件 /etc/rsyslog.conf 通常分为三大段:
vim /etc/rsyslog.conf
#### MODULES #### 模块加载区
$ModLoad imuxsock # 提供本地系统日志支持(logger 命令)
$ModLoad imklog # 提供内核日志支持
#$ModLoad immark # 提供 --MARK-- 周期标记消息
#$ModLoad imudp # 开启 UDP 接收
#$UDPServerRun 514
#$ModLoad imtcp # 开启 TCP 接收
#$InputTCPServerRun 514
#### GLOBAL DIRECTIVES #### 全局指令区(模板、工作目录、文件权限等)
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$IncludeConfig /etc/rsyslog.d/*.conf # 引入子配置目录
$WorkDirectory /var/lib/rsyslog
#### RULES #### 规则区(选择器 + 动作)
*.info;mail.none;authpriv.none;cron.none /var/log/messages
authpriv.* /var/log/secure
mail.* -/var/log/maillog
cron.* /var/log/cron
*.emerg *
uucp,news.crit /var/log/spooler
local7.* /var/log/boot.log
| 路径 | 作用 |
|---|---|
/etc/rsyslog.conf |
主配置文件 |
/etc/rsyslog.d/*.conf |
子配置目录,推荐把自定义规则单独放在这里,避免升级时被覆盖 |
4.3 facility(日志设施/服务类型)全表
facility 用来标记一种类型日志的来源,相当于"日志设备"。
| facility | 说明 |
|---|---|
auth |
认证相关,PAM 产生的日志 |
authpriv |
ssh、ftp 等登录信息的验证消息(私有认证信息,权限更严格) |
cron |
定时任务(crond、atd) |
daemon |
守护进程相关的消息(内部服务器/系统服务) |
kern |
内核信息(用户进程无法使用该设施) |
lpr |
打印服务相关的消息 |
mail |
内部邮件服务器相关的消息 |
mark |
时间戳标记,周期性写入 --MARK-- 用于判断日志服务是否存活 |
news |
新闻交换网络,基于 uucp |
syslog |
日志服务自身产生的消息 |
user |
用户程序产生的相关消息(默认设施) |
uucp |
Unix-to-Unix Copy,Email 的前身 |
ftp |
ftp 服务器相关的消息 |
local0 ~ local7 |
用户自定义消息;local7 通常被 Cisco 和 Windows 服务器使用,CentOS 上 local7 默认写 boot.log |
4.4 severity(日志级别)全表
level(严重级别,也叫 priority)指定记录日志信息的详细程度。
| 级别 | 数值 | 英文 | 含义与说明 |
|---|---|---|---|
emerg / panic |
0 | Emergency | 紧急,系统不可用、接近崩溃,一般为硬件问题 |
alert |
1 | Alerts | 报警,需要马上处理;应用已出现很多重大错误,比 crit 更严重 |
crit |
2 | Critical | 关键,非常严重的状况,比 err 更重要的错误 |
err / error |
3 | Errors | 错误,重大错误信息 |
warning / warn |
4 | Warnings | 警示,提示信息,可能有问题出现 |
notice |
5 | Notification | 通知,普通但很重要的信息,除 info 之外还需注意的信息 |
info |
6 | Information | 消息,最常见的级别,一些基本说明 |
debug |
7 | Debugging | 调试日志,最详细,通常难以阅读,一般不启用 |
none |
— | None | 不算级别,表示该设施的信息什么都不记录 |
数值越大,级别越低、消息越详细;数值越小,级别越严重。默认写法(如
cron.info)表示"大于或等于该级别",即 info、notice、warning、err、crit、alert、emerg 都会被记录。
4.5 选择器语法与符号
基本格式:
日志设施.[连接符号]日志级别 日志处理方式(action)
示例:
mail.info /var/log/maillog
表示:mail 设施产生的、级别大于等于 info 的日志,写入 /var/log/maillog。
符号完整含义:
| 符号 | 作用 |
|---|---|
. |
分隔服务(facility)与级别(level);无其他修饰时表示"大于或等于此级别"(最常用) |
* |
通配:*.级别 表示任何服务;设施.* 表示任何级别;*.* 表示所有服务所有级别 |
= |
只等于此级别,如 cron.=info |
! |
排除操作。前面有相同服务的表达式时才有意义,表示从前面包含的内容中排除某些级别。cron.!err 表示排除 err 及以上;cron.!=err 表示仅排除等于 err |
none |
该设施什么都不记录,常用于"排除某几类日志",如 *.info;mail.none |
; |
分隔不同的"服务.级别"组合 |
, |
分隔多个服务(共用同一级别),如 mail,cron.info |
-(动作侧) |
写在目标文件路径前,表示不立即刷盘(先写内存缓冲,攒够再落盘) |
关于 . / .= / .! 的对照(旧文档常用写法):
| 符号 | 含义 |
|---|---|
. |
大于或等于此级别(最常用) |
.= |
只等于此级别 |
.! |
不等于(排除)此级别 |
4.6 选择器配置示例
cron.* # cron 服务的所有级别
*.info # 所有服务 info 及以上级别
cron.info # cron 服务 info 及以上级别
cron.info;cron.!err # >=info 且 <err(即 info 到 warning)
cron.=info # 等于 info 级别
cron.info;cron.!=err # >=info 但不等于 err
*.warning;mail.info # 所有服务 >=warning,加上 mail 服务 >=info
mail,cron.info # mail 与 cron 服务均 >=info
cron.err /var/log/cron.log # 文件名前无减号:异步写入(立即刷盘)
mail.err -/var/log/mail.log # 文件名前有减号:同步写入,rsyslog 不会马上把数据写入文件
典型需求示例:
示例 1:将 mail 应用的日志记录到 /var/log/maillog
mail.info /var/log/maillog
示例 2:将 news 和 mail 的所有日志写入 /var/log/mylog,并把两者的警告信息额外写入 /var/log/my.log
news.*;mail.* /var/log/mylog
news.=warn;mail.=warn /var/log/my.log
多个应用/设施之间用
;隔开;指定单一级别用=接级别。
示例 3:将除 cron 和 mail 外的所有日志记录到 /var/log/mylog
*.*;mail;news.none /var/log/mylog
或(推荐、更严谨的写法):
*.*;mail.none;news.none /var/log/mylog
*.*;cron.none;mail.none /var/log/mylog
也可简写为:
*.*;cron,mail.none /var/log/mylog
4.7 动作(action):日志的六种去向
| 动作类型 | 写法 | 说明 |
|---|---|---|
| 写文件 | cron.* /var/log/cron |
最常见;前置 - 表示不立即刷盘 |
| 写终端/设备 | mail.=info /dev/tty12 |
输出到第 12 个终端,便于值守观察 |
| 广播给用户 | *.emerg * 或 *.alert root,czl |
* 广播给所有在线用户;也可指定用户列表 |
| 丢弃 | local3.* ~ 或 & stop |
~(旧语法)/ stop(新语法)丢弃匹配的日志,用于降噪 |
| 转发远程 | local5.* @@192.168.1.2:514 |
@ = UDP,@@ = TCP |
| 写数据库 | *.* :ommysql:127.0.0.1,Syslog,rsyslog,passwd |
需加载 ommysql 模块并导入建库脚本 |
| 执行程序 | *.* ^/usr/local/bin/alert.sh |
把日志作为参数交给外部脚本(可用于告警) |
| 命名管道 | *.* \|/var/log/mypipe |
写入 FIFO,由其他程序实时消费 |
特殊写法说明:
*.emerg *
将所有应用最严重的信息以广播形式发送给登录该系统的每一个用户。
mail.* -/var/log/maillog
将 mail 应用所有等级的日志写入 /var/log/maillog。路径前的 - 表示先将日志保存到内存,数据量够大时才写入文件:
- 优点:对日志量大的服务可提升写盘性能;
- 缺点:非正常关机时,内存中尚未落盘的日志会丢失。
完整的高级示例解析:
*.=crit;kern.none /var/adm/critical
# 除 kern 外所有服务的 crit 级别信息写入 /var/adm/critical
kern.* /var/adm/kernel
# kern 服务所有信息
kern.crit @@10.1.1.1:514
# kern 服务 crit 及以上信息发送到远程日志服务器 10.1.1.1 的 TCP 514 端口
# (单 @ 表示 UDP,双 @@ 表示 TCP)
kern.info;kern.!err /var/adm/kernel-info
# kern 服务 info 到 warning 的信息
mail.=info /dev/tty12
# mail 服务等于 info 级别的信息输出到第 12 个终端
mail.*;mail.!=info /var/adm/mail
# mail 服务除 info 之外的所有信息
mail,news.=info -/var/adm/info
# mail 与 news 的 info 级别信息同步写入 /var/adm/info
*.=emerg *
# 所有 emerg 信息发送给所有登录到系统上的用户
*.alert root,czl
# 所有 alert 信息发送给在线用户 root 和 czl
4.8 模板(template)自定义日志格式
模板用于定义写入文件或转发时的日志格式,也可用于动态生成文件名(远程日志按主机名分目录的关键)。
传统(legacy)语法:
# 定义日志内容格式
$template MyFormat,"%timegenerated% %HOSTNAME% %syslogtag% %fromhost-ip% %msg%\n"
# 使用模板:动作后加分号和模板名
*.info /var/log/custom.log;MyFormat
# 定义动态文件名模板(按主机名 + 日期分目录)
$template RemoteLogs,"/var/log/remote/%HOSTNAME%/%$YEAR%-%$MONTH%-%$DAY%.log"
*.* ?RemoteLogs # 动态文件名前用 ? 引用
& stop # 处理后停止,避免重复写入本地文件
RainerScript(新语法,rsyslog 7+ 推荐):
template(name="RemoteLogs" type="string"
string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
*.* action(type="omfile" dynaFile="RemoteLogs")
常用模板属性(property):
| 属性 | 含义 |
|---|---|
%timegenerated% |
日志生成时间(本机接收时间) |
%timereported% |
日志中自带的时间戳 |
%HOSTNAME% |
产生日志的主机名 |
%fromhost-ip% |
发送方 IP 地址 |
%syslogfacility-text% |
设施名(文本) |
%syslogseverity-text% |
级别名(文本) |
%syslogtag% |
标签(通常是 程序名[PID]:) |
%programname% |
程序名 |
%msg% |
日志正文 |
%$YEAR% %$MONTH% %$DAY% |
系统当前年、月、日,用于动态路径 |
内置模板:
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat # 传统格式(秒级精度)
$ActionFileDefaultTemplate RSYSLOG_FileFormat # 高精度时间戳 + 时区
4.9 logger 命令:手动写日志用于测试
logger 用于向 syslog 发送日志信息,特别适合调试配置文件,也可在脚本中记录日志。
logger "简单消息,默认以 user.notice 写入"
logger -t "Logger" -p mail.info "Testing log info"
logger -t "Cron" -p cron.info "Testing cron info"
logger -t "哈哈" "你好"
logger -p local5.info -t SSHTEST "远程日志测试消息"
logger -i -t myapp "带 PID 的消息"
logger -f /etc/fstab # 把文件内容逐行写入日志
logger -s -p user.err "同时输出到标准错误"
logger -n 192.168.1.2 -P 514 -d "直接发往远程日志服务器(UDP)"
| 选项 | 作用 |
|---|---|
-t TAG |
指定日志中的标签(tag) |
-p facility.level |
指定设施与级别,默认 user.notice |
-i |
在日志中记录 logger 进程的 PID |
-f FILE |
把文件内容作为日志消息 |
-s |
同时把消息输出到标准错误 |
-n HOST / -P PORT / -d |
直接发往远程主机(-d 用 UDP,-T 用 TCP) |
验证效果:
logger -t "哈哈" "你好"
tail -3 /var/log/messages
4.10 rsyslog 配置语法检查与调试
# 语法检查(不启动服务),CentOS 7
rsyslogd -N1
rsyslogd -N1 -f /etc/rsyslog.conf
# 查看生效的完整配置(含 include 的子文件)
rsyslogd -o /tmp/rsyslog.full.conf
# 前台调试运行,打印详细处理过程
rsyslogd -dn
# 服务管理(CentOS 6)
service rsyslog restart
/etc/init.d/rsyslog restart
chkconfig rsyslog on
# 服务管理(CentOS 7)
systemctl restart rsyslog
systemctl status rsyslog
systemctl enable rsyslog
journalctl -u rsyslog -n 50 # 查看 rsyslog 自身的错误
# 确认监听端口
netstat -ntlup | grep :514
ss -ntlup | grep :514
4.11 远程日志集中收集完整实操
将所有服务器的日志统一发送到一台机器上管理,便于集中存储、检索和审计。
实验拓扑:
日志服务器(Log Server) 日志客户端(Log Client)
192.168.1.2 <------ 192.168.1.61
rsyslog rsyslog
需求:把 192.168.1.61 上 ssh 服务的日志远程记录到 192.168.1.2 的 /var/log/ssh。
所有机器先关闭防火墙和 SELinux(生产环境应改为放行 514 端口):
# CentOS 6
iptables -F
setenforce 0
# CentOS 7
systemctl stop firewalld
setenforce 0
# 或者只放行 514
firewall-cmd --permanent --add-port=514/tcp
firewall-cmd --permanent --add-port=514/udp
firewall-cmd --reload
semanage port -a -t syslogd_port_t -p tcp 514 # SELinux 开启时需放行端口
步骤一:日志服务器端开启 514 端口接收(192.168.1.2)
编辑 /etc/rsyslog.conf,将模块区第 17、18 行的注释打开:
$ModLoad imtcp
$InputTCPServerRun 514
也可同时启用 UDP:
$ModLoad imudp
$UDPServerRun 514
TCP/UDP 传输可同时开启,不影响远程日志服务器工作。UDP 开销小但可能丢包,TCP 可靠、推荐用于重要日志。
重启服务并验证端口监听:
/etc/init.d/rsyslog restart # CentOS 6
systemctl restart rsyslog # CentOS 7
netstat -ntlup | grep :514
输出:
tcp 0 0 0.0.0.0:514 0.0.0.0:* LISTEN 7295/rsyslogd
tcp 0 0 :::514 :::* LISTEN 7295/rsyslogd
syslog 默认端口为 514。
步骤二:客户端配置转发(192.168.1.61)
先让 ssh 使用独立的 local5 设施,便于服务端区分:
vim /etc/ssh/sshd_config
在文件后添加:
SyslogFacility local5
/etc/init.d/sshd restart # CentOS 6
systemctl restart sshd # CentOS 7
配置 rsyslog 远程发送:
vim /etc/rsyslog.conf
local5.* @@192.168.1.2:514
@@表示 TCP 协议;UDP 只有一个@。若要把本机全部日志都转发,可写
*.* @@192.168.1.2:514。
/etc/init.d/rsyslog restart
带磁盘队列的可靠转发(生产环境推荐,服务端宕机时本地缓存不丢日志):
$WorkDirectory /var/lib/rsyslog
$ActionQueueFileName fwdRule1
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionQueueType LinkedList
$ActionResumeRetryCount -1
*.* @@192.168.1.2:514
步骤三:服务端指定日志文件
回到 192.168.1.2,编辑 /etc/rsyslog.conf,在最后添加:
local5.* /var/log/ssh
/etc/init.d/rsyslog restart
步骤四:测试
从其他机器 ssh 连接 192.168.1.61,然后在 192.168.1.2 上查看:
cat /var/log/ssh
tail -f /var/log/ssh
即可看到 192.168.1.61 的日志。也可用 logger 在客户端直接造一条测试日志:
# 客户端执行
logger -p local5.info -t TEST "hello from 192.168.1.61"
# 服务端查看
tail -f /var/log/ssh
步骤五:按主机名分目录存储(多客户端场景)
若 100 台主机日志都传到同一个文件,如何区分来源?日志中有一列是主机名(主机头),例如 qianyun.org 中的 qianyun,所以所有主机必须绑定不同且唯一的主机名。
更好的做法是在服务端用动态文件名模板按主机名分目录存储:
vim /etc/rsyslog.d/remote.conf
# 加载 TCP/UDP 接收模块
$ModLoad imtcp
$InputTCPServerRun 514
$ModLoad imudp
$UDPServerRun 514
# 定义按主机名 + 程序名分目录的动态路径
$template RemoteHost,"/var/log/remote/%HOSTNAME%/%$YEAR%-%$MONTH%-%$DAY%-%PROGRAMNAME%.log"
# 只处理来自远程的日志(本机日志仍按默认规则走)
:fromhost-ip, !isequal, "127.0.0.1" ?RemoteHost
& stop
RainerScript 等价写法:
module(load="imtcp")
input(type="imtcp" port="514")
template(name="RemoteHost" type="string"
string="/var/log/remote/%HOSTNAME%/%$YEAR%-%$MONTH%-%$DAY%-%PROGRAMNAME%.log")
if $fromhost-ip != "127.0.0.1" then {
action(type="omfile" dynaFile="RemoteHost")
stop
}
重启并查看效果:
systemctl restart rsyslog
tree /var/log/remote/
# /var/log/remote/
# ├── web01
# │ └── 2026-08-22-sshd.log
# └── db01
# └── 2026-08-22-mysqld.log
集中收集的日志同样需要配置
logrotate(见第六节),否则日志服务器磁盘很快被写满。需要检索、聚合、可视化时,请使用 ELK 平台,参见
08-04-ELK日志分析.md。
五、systemd-journald(CentOS 7+)
5.1 与 rsyslog 的关系与共存
CentOS 7 起,systemd 自带日志服务 systemd-journald,它与 rsyslog 并存而非替代。只看 /var/log/messages 会漏掉大量信息,尤其是服务启动失败的原因。
内核 / 服务 stdout+stderr / syslog() 调用 / journal API
↓
systemd-journald(统一收集,二进制结构化存储)
↓ ↓
journalctl 查询 转发给 rsyslog(ForwardToSyslog=yes)
↓
/var/log/messages 等文本文件
journald是采集者:把内核缓冲区、服务的标准输出/错误、syslog()调用全部收进来。rsyslog从 journal 读取(imjournal模块)后按facility.priority规则落到文本文件(即第四节的配置)。- 所以同一条日志可能同时出现在
journalctl和/var/log/messages中。
CentOS 6 与 CentOS 7 的差异对照:
| 项目 | CentOS 6 | CentOS 7 |
|---|---|---|
| 日志服务 | 仅 rsyslog(RHEL 5 为 syslog) |
systemd-journald + rsyslog 并存 |
| 服务管理 | service rsyslog restart / /etc/init.d/rsyslog restart |
systemctl restart rsyslog |
| 内核日志 | dmesg、/var/log/dmesg |
dmesg、journalctl -k |
| 服务日志查看 | tail -f /var/log/messages |
journalctl -u <unit>、tail -f /var/log/messages |
| 启动日志 | /var/log/boot.log(local7.*) |
journalctl -b、/var/log/boot.log |
| 日志持久化 | 天然持久(文本文件) | journal 默认易失,需手动开启持久化 |
| 轮转机制 | logrotate |
文本日志用 logrotate;journal 自带轮转(SystemMaxUse=) |
rsyslog 与 journald 对比
| 对比项 | rsyslog(/var/log/messages) |
journald(journalctl) |
|---|---|---|
| 存储格式 | 纯文本,可直接 cat/grep/awk |
二进制,必须用 journalctl 读取 |
| 数据结构 | 单行非结构化字符串 | 结构化字段(_PID、_UID、_SYSTEMD_UNIT 等) |
| 查询能力 | 依赖 grep/sed 文本匹配 |
内置按 unit/时间/级别/字段过滤 |
| 持久化 | 默认持久,写入 /var/log/ |
默认易失,需手动开启持久化 |
| 轮转机制 | 由 logrotate 管理 |
自带轮转,受 SystemMaxUse= 控制 |
| 日志分割 | 按 facility.priority 分文件 |
单一日志库,查询时动态过滤 |
| 采集范围 | 走 syslog() 的程序 |
额外覆盖服务 stdout/stderr、内核、早期启动 |
| 远程传输 | 成熟(@@host:514,见 4.11 节) |
需 systemd-journal-remote,生态较弱 |
| 适用场景 | 长期归档、集中日志平台、文本工具链 | 本地故障排查、服务启动调试、精确定位 |
实践建议:排查当下问题用
journalctl,长期归档与集中分析用 rsyslog。两者不是二选一,CentOS 7 默认就是 journald 采集 + 转发 rsyslog 落盘的组合。
5.2 存储位置:volatile 与 persistent
| 路径 | 类型 | 说明 |
|---|---|---|
/run/log/journal/ |
易失(volatile) | 位于 tmpfs,重启即丢失,CentOS 7 默认行为 |
/var/log/journal/ |
持久(persistent) | 落到磁盘,重启后仍可查询历史日志 |
注意:这是新手最容易踩的坑——服务器异常重启后想查故障原因,执行
journalctl -b -1却发现没有任何记录,因为默认配置下 journal 日志随重启一起消失了。生产环境务必开启持久化。
5.3 开启日志持久化
方式一:创建目录(journald 检测到目录存在即自动持久化)
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal # 修正目录权限与属主
sudo systemctl restart systemd-journald
方式二:显式修改配置文件 /etc/systemd/journald.conf
[Journal]
Storage=persistent # auto(默认)/ volatile / persistent / none
SystemMaxUse=500M # journal 最多占用 500M 磁盘
SystemKeepFree=1G # 至少给磁盘留 1G 空闲
SystemMaxFileSize=50M # 单个日志文件最大 50M
MaxRetentionSec=1month # 日志最长保留 1 个月
MaxFileSec=1week # 单个日志文件最长跨度 1 周后强制轮转
RateLimitInterval=30s # 限流窗口
RateLimitBurst=1000 # 限流窗口内最多接收条数(防日志风暴)
ForwardToSyslog=yes # 是否转发给 rsyslog
Compress=yes # 大条目压缩存储
sudo systemctl restart systemd-journald
journalctl --disk-usage # 验证是否已写入 /var/log/journal
ls -ld /var/log/journal/*
Storage 取值 |
行为 |
|---|---|
auto |
/var/log/journal/ 存在则持久化,否则易失(默认) |
persistent |
自动创建目录并持久化 |
volatile |
仅存内存,重启丢失 |
none |
不存储,仅转发 |
5.4 日志转发给 rsyslog
CentOS 7 中 rsyslog 通过 imjournal 模块读取 journal:
# /etc/rsyslog.conf
$ModLoad imjournal
$imjournalStateFile imjournal.state
$OmitLocalLogging on # on 表示不再从 /dev/log 直接读(避免重复)
若希望由 journald 主动转发,则在 /etc/systemd/journald.conf 中设置:
[Journal]
ForwardToSyslog=yes # 转发给 rsyslog(写入 /var/log/messages 等)
ForwardToKMsg=no # 转发到内核环形缓冲区
ForwardToConsole=no # 转发到控制台
ForwardToWall=yes # 高级别消息广播给所有登录用户
systemctl restart systemd-journald
systemctl restart rsyslog
5.5 journalctl 常用选项全览
核心查询用法
journalctl # 查看全部日志(分页,最早在前)
journalctl -n 50 # 只看最后 50 行
journalctl -n # 默认最后 10 行
journalctl -r # 倒序,最新的在最前面
journalctl -f # 实时跟踪(等价于 tail -f)
journalctl --no-pager # 不分页,直接输出(便于管道处理)
journalctl -a # 完整显示(不截断长字段、显示不可打印字符)
按 unit 过滤(最常用)
journalctl -u nginx.service # 只看 nginx 服务日志
journalctl -u nginx -f # 实时跟踪 nginx(.service 可省略)
journalctl -u nginx -u php-fpm # 同时看多个服务
journalctl -u nginx --since today # 组合时间条件
journalctl --user-unit myapp # 用户级 unit
按启动次数过滤
journalctl -b # 本次启动以来的日志
journalctl -b -1 # 上一次启动的日志(排查异常重启的关键)
journalctl -b -2 # 上上次启动
journalctl --list-boots # 列出所有可用的启动记录及其编号
按时间过滤
journalctl --since "2026-08-01 00:00" --until "2026-08-02 00:00"
journalctl --since today
journalctl --since yesterday --until now
journalctl --since "1 hour ago"
journalctl --since "-30min"
journalctl --since "09:00" --until "10:30"
按级别与来源过滤
journalctl -p err # err 及以上级别(级别名同 4.4 节 severity)
journalctl -p 3 # 也可用数值(3 = err)
journalctl -p warning..err # 指定级别区间
journalctl -k # 只看内核日志(等价于 dmesg)
journalctl -k -p err # 内核错误
journalctl -k -b -1 # 上次启动的内核日志
按结构化字段匹配(journal 的核心优势)
journalctl _PID=1234 # 指定进程 ID
journalctl _UID=1000 # 指定用户 ID
journalctl _GID=100 # 指定组 ID
journalctl _COMM=sshd # 指定进程名
journalctl _EXE=/usr/sbin/sshd # 指定可执行文件
journalctl _SYSTEMD_UNIT=sshd.service
journalctl _TRANSPORT=kernel # 来源通道:kernel/syslog/journal/stdout/audit
journalctl SYSLOG_FACILITY=10 # 按 syslog 设施数值(10 = authpriv)
journalctl PRIORITY=3
journalctl /usr/sbin/sshd # 按可执行文件路径(简写形式)
journalctl -F _SYSTEMD_UNIT # 列出该字段的所有可能取值
journalctl _COMM=sshd _PID=1234 # 多字段为 AND
journalctl _COMM=sshd + _COMM=su # "+" 表示 OR
journalctl --fields # 列出日志中出现过的所有字段名
输出格式(-o)
journalctl -u nginx -o short # 默认格式,类似传统 syslog 单行
journalctl -u nginx -o short-precise # 带毫秒精度的时间戳
journalctl -u nginx -o short-iso # ISO 8601 时间格式
journalctl -u nginx -o short-monotonic # 相对开机的单调时间
journalctl -u nginx -o verbose # 显示全部结构化字段(排障最全)
journalctl -u nginx -o json # 结构化 JSON,一行一条
journalctl -u nginx -o json-pretty # 格式化 JSON,便于脚本解析
journalctl -u nginx -o cat # 只输出消息正文,不带前缀
journalctl -u nginx -o export # 二进制导出格式,用于传输/备份
附加解释与错误排查
journalctl -x # 为日志条目附加解释性说明
journalctl -xe # 附加说明并跳到末尾(排查服务启动失败第一步)
journalctl --verify # 校验日志文件完整性
journalctl -xe 是服务启动失败的标准排查动作:
sudo systemctl start tomcat
# Job for tomcat.service failed because ... See "systemctl status tomcat.service"
# and "journalctl -xe" for details.
sudo journalctl -xe
-x:为日志条目附加解释性说明(Subject/Defined-By/Support 等帮助文本)。-e:直接跳到日志末尾,即最新的错误现场。
配合使用:
sudo systemctl status tomcat -l --no-pager # 先看状态摘要与最后几行日志
sudo journalctl -u tomcat -n 100 --no-pager # 再看该服务完整错误上下文
sudo journalctl -u tomcat -b --no-pager # 看本次启动以来的全部记录
常见能从 journalctl -xe 中直接定位的问题:
| 日志关键字 | 原因 |
|---|---|
Failed to determine user credentials |
User= 指定的用户不存在 |
No such file or directory |
ExecStart 路径写错或缺少解释器 |
Executable path is not absolute |
ExecStart 必须用绝对路径 |
PID file ... not readable |
Type=forking 但 PIDFile= 路径不对 |
Start operation timed out |
Type= 选错(unit 文件编写详见 08-01) |
status=203/EXEC |
脚本无执行权限或 shebang 有问题 |
5.6 磁盘占用与清理
journalctl --disk-usage # 查看当前日志占用空间
journalctl --vacuum-size=200M # 清理旧日志,保留总量不超过 200M
journalctl --vacuum-time=2weeks # 删除 2 周以前的日志
journalctl --vacuum-files=5 # 只保留最近 5 个日志文件
journalctl --rotate # 立即触发一次 journal 轮转
journalctl --flush # 把 /run 中的日志刷到 /var/log/journal
journalctl --verify # 校验日志文件完整性
手动 vacuum 是应急手段;长期控制应在
/etc/systemd/journald.conf中设置SystemMaxUse=,让 journald 自动按上限滚动,无需依赖logrotate(journal 有自己的轮转机制,不受第六节 logrotate 管理)。
六、日志轮转 logrotate
6.1 为什么要轮转
随着系统运行,日志文件会持续增大,带来三个问题:
- 单个文件过大,
grep/vim打开缓慢甚至不可用; - 占满
/var或根分区磁盘,导致服务无法写入、系统异常; - 历史日志与当前日志混在一起,不利于归档和审计。
logrotate 用于对日志文件进行切割、备份、归档、压缩、删除,并创建新日志文件,避免单个日志文件无限增长。
可以按 大小 或 时间(天/周/月/年) 进行轮转。查看 /var/log/ 目录常能看到形如 messages-20140724 的文件名,这表明该日志在 7 月 24 号被轮转过。
ls /var/log/
# messages messages-20140724 messages-20140717.gz ...
6.2 配置文件结构
| 路径 | 作用 |
|---|---|
/etc/logrotate.conf |
主配置文件,定义全局属性,并引用 /etc/logrotate.d/ 下的文件 |
/etc/logrotate.d/ |
子配置目录,各应用服务独立配置,其中定义会覆盖主配置;通常每个软件包在此放置一份 |
/var/lib/logrotate/logrotate.status |
默认状态文件,记录每个日志上次轮转的时间(判断周期是否到达的依据) |
/etc/cron.daily/logrotate |
每日触发脚本 |
/etc/anacrontab |
定义每日任务执行时间,从而触发 /etc/cron.daily/logrotate |
/usr/lib/systemd/system/logrotate.timer |
CentOS 7.4+ 改用 systemd timer 触发 |
可通过 man logrotate 查阅 CONFIGURATION FILE 段的完整指令说明。
6.3 触发机制:cron 与 systemd timer
logrotate 本身不是守护进程,它是被周期性调用的一次性命令。
CentOS 6 / 早期 CentOS 7(cron 方式):
/etc/cron.daily/logrotate 是一个 shell 脚本,每天由 cron 在 /etc/crontab 指定的时间启动:
cat /etc/cron.daily/logrotate
#!/bin/sh
/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
/usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0
/etc/anacrontab 决定 daily 任务的实际执行时间与关机补跑策略。
CentOS 7.4+ / CentOS 8(systemd timer 方式):
systemctl list-timers | grep logrotate
systemctl status logrotate.timer
systemctl cat logrotate.timer
# /usr/lib/systemd/system/logrotate.timer
[Unit]
Description=Daily rotation of log files
[Timer]
OnCalendar=daily
AccuracySec=1h
Persistent=true
[Install]
WantedBy=timers.target
systemctl enable --now logrotate.timer
systemctl start logrotate.service # 立即手动跑一次
若需要比"每天一次"更频繁的轮转(例如每小时、每 5 分钟),可以自行添加 cron 条目或自定义 timer:
*/5 * * * * /usr/sbin/logrotate /etc/logrotate.d/nginx
6.4 主配置文件详解
vim /etc/logrotate.conf
weekly # 日志文件每周轮转一次(另有 daily/monthly/yearly)
rotate 4 # 轮转后只保留四份
create # 轮转后创建新的空日志文件
#compress # 默认轮转后的日志文件不压缩
#dateext # 轮转的日志文件加时间后缀,测试时建议注释
include /etc/logrotate.d # 引入子配置目录
/var/log/wtmp {
monthly # 此文件每月轮转,覆盖上面的每周
create 0664 root utmp # 轮转文件的权限、owner、group
minsize 1M # 最小 1M 和每月时间两条件满足其一即轮转
rotate 1
}
/var/log/btmp {
missingok # 如果日志丢失也不报错
monthly
create 0600 root utmp
rotate 1
}
6.5 常用指令全表
| 指令 | 作用 |
|---|---|
daily |
每天轮转一次 |
weekly |
每周轮转一次 |
monthly |
每月轮转一次 |
yearly |
每年轮转一次 |
hourly |
每小时轮转(需配合每小时的触发任务) |
size SIZE |
文件只要大于指定大小就轮转,忽略周期(如 size 30k、size 100M) |
minsize SIZE |
文件大于指定大小 且 周期到了才轮转(如 minsize 30K) |
maxsize SIZE |
周期未到但已超过该大小也立即轮转 |
rotate N |
同一文件最多保留 N 个轮转副本,超过后删除;rotate 0 表示不保留 |
compress |
用 gzip 压缩轮转后的日志 |
nocompress |
不压缩(默认) |
delaycompress |
下一次轮转时才压缩(常与 compress 配合,避免程序仍持有旧文件句柄导致压缩到写入中的文件) |
compresscmd / uncompresscmd |
指定压缩/解压命令(如改用 xz) |
compressoptions |
传给压缩命令的参数(如 -9) |
missingok |
日志文件不存在时不报错,继续处理下一个 |
nomissingok |
文件不存在时报错(默认) |
notifempty |
日志文件为空时不轮转 |
ifempty |
即使为空也轮转(默认) |
create [mode owner group] |
轮转后立即创建新的空日志文件并指定权限、属主、属组(如 create 0664 root utmp) |
nocreate |
不创建新文件 |
copytruncate |
先复制原文件再清空原文件(truncate),进程句柄不变,无需重启服务 |
nocopytruncate |
复制但不清空原文件(默认关闭) |
copy |
只复制不清空,也不轮转原文件 |
dateext |
用日期作为轮转后文件的后缀(messages-20260822),而不是数字 .1 .2 |
dateformat |
自定义日期格式,如 dateformat -%Y%m%d%H(支持 %Y %m %d %H %s) |
dateyesterday |
用前一天的日期作后缀(更符合"这份日志属于昨天"的语义) |
olddir DIR |
把轮转后的文件移动到指定目录 |
noolddir |
轮转后文件与原文件同目录(默认) |
createolddir |
olddir 不存在时自动创建 |
sharedscripts |
无论有多少文件待轮转,prerotate/postrotate 只执行一次(与通配符配合使用) |
nosharedscripts |
每个文件都执行一次脚本(默认) |
prerotate ... endscript |
符合轮转条件时,轮转前执行的 shell 代码 |
postrotate ... endscript |
轮转后执行的 shell 代码 |
firstaction ... endscript |
所有文件轮转前,最先执行一次 |
lastaction ... endscript |
所有文件轮转后,最后执行一次 |
su USER GROUP |
以指定用户/组身份轮转(日志目录属主非 root 时必需) |
include DIR/FILE |
引入子配置目录或文件 |
mail EMAIL |
轮转后发送邮件给指定用户 |
nomail |
不发送邮件 |
maillast |
发送即将被删除的最老一份(主题为 about-to-expire) |
mailfirst |
发送刚轮转出的最新一份(主题为 just-rotated) |
tabooext [+] LIST |
忽略指定扩展名的配置文件 |
start N |
轮转后缀的起始编号(默认 1) |
shred |
删除旧日志时用 shred 安全擦除 |
6.6 copytruncate 与 create 的区别与适用场景
这是 logrotate 最重要的一个知识点。
问题的来源:某服务将日志写入 aaa.log,手动执行 mv aaa.log aaa.log.1 && touch aaa.log,新日志会写入哪个文件?
答案:仍然写入 aaa.log.1。
原因:服务进程通过文件描述符写入文件,同一分区内 mv 不会改变 inode,服务仍持有旧 inode。新建的空文件 inode 不同,服务不会自动切换。
因此 logrotate 提供两种解决思路:
| 方式 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
create + postrotate 信号 |
重命名原文件 → 创建新文件 → 给进程发 HUP/USR1 让它重新打开日志 |
不丢日志、不占额外 IO、日志边界清晰 | 需要程序支持重新打开日志的信号 | rsyslog、Nginx、Apache、sshd 等标准守护进程 |
copytruncate |
把原文件内容复制成副本 → 然后把原文件截断为 0 字节;进程句柄和 inode 都不变 | 无需通知进程、无需重启,通用性最强 | 复制与截断之间写入的日志会丢失;大文件复制占用双倍磁盘与 IO | 程序不支持重新打开日志(很多 Java 应用、Tomcat catalina.out、自研程序、MySQL 部分日志) |
create 方式(推荐,配合信号):
/var/log/nginx/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
copytruncate 方式:
/usr/local/tomcat/logs/catalina.out {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
使用
copytruncate时不要同时写create,两者语义冲突(create会被忽略)。
为什么要 kill -HUP?
一个服务日志记录到 aaa.log,手动 mv aaa.log aaa.log.1 再 touch aaa.log,新日志仍会记录到 aaa.log.1,因为同分区 mv 文件 inode 不变。需要 kill -HUP 其 pid(有些服务是 kill -USR1)让服务重新打开日志文件:
kill -HUP `cat /var/run/syslogd.pid`
常见服务的"重新打开日志"信号:
| 服务 | 信号 / 命令 |
|---|---|
| rsyslog / syslogd | kill -HUP $(cat /var/run/syslogd.pid) 或 systemctl reload rsyslog |
| Nginx | kill -USR1 $(cat /var/run/nginx.pid) |
| Apache httpd | apachectl graceful 或 systemctl reload httpd |
| MySQL | mysqladmin flush-logs |
| sshd | kill -HUP $(cat /var/run/sshd.pid) |
| 不支持信号的程序 | 使用 copytruncate |
6.7 子配置文件详解
vim /etc/logrotate.d/syslog
未指定的参数以主配置的默认方式轮转:
/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
sharedscripts # 切换时脚本只执行一次
postrotate # rotate 后执行的脚本
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}
脚本相关指令:
| 指令 | 含义 |
|---|---|
sharedscripts |
对配置中的多个日志只执行一次脚本 |
prerotate ... endscript |
轮转前执行的脚本 |
postrotate ... endscript |
轮转后执行的脚本 |
postrotate 中对 rsyslog 的 pid 发信号,让其刷新并重新打开日志文件(pid 实际不变)。
6.8 手动执行与调试
# 调试/预演:只打印将要做什么,不真正执行(最重要的调试手段)
logrotate -d /etc/logrotate.conf
logrotate -d /etc/logrotate.d/nginx
# 显示详细过程
logrotate -v /etc/logrotate.conf
# 强制轮转,忽略周期与大小条件(测试用)
logrotate -f /etc/logrotate.conf
logrotate -fv /etc/logrotate.conf
logrotate -f /etc/logrotate.d/nginx
# 指定状态文件(不污染系统状态)
logrotate -d -s /tmp/test.status /etc/logrotate.d/nginx
# 查看轮转状态记录
cat /var/lib/logrotate/logrotate.status
# 查看轮转结果
ls -lh /var/log/
| 选项 | 作用 |
|---|---|
-d / --debug |
调试模式(隐含 -v),只输出计划动作,不做任何修改,也不更新状态文件 |
-v / --verbose |
输出详细过程 |
-f / --force |
强制轮转,忽略周期与大小条件 |
-s FILE / --state FILE |
指定状态文件 |
-m CMD / --mail CMD |
指定发信命令 |
--usage / --help |
帮助 |
-f强制轮转方便测试,后面只能接主配置文件或具体子配置文件;可加v参数查看过程。
6.9 自定义应用轮转配置完整示例
6.9.1 简单示例:为 ssh 日志单独配置轮转
写在 /etc/logrotate.conf 中:
/var/log/ssh {
monthly
rotate 2
missingok
create 0644 root utmp
}
6.9.2 Nginx 轮转配置
vim /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 每天轮转
rotate 30 # 保留 30 天
missingok # 文件不存在不报错
notifempty # 空文件不轮转
compress # gzip 压缩
delaycompress # 延迟一轮再压缩
dateext # 用日期做后缀
dateformat -%Y%m%d
create 0640 nginx adm # 新文件权限
olddir /var/log/nginx/old # 归档到单独目录
createolddir
sharedscripts # 多个 .log 只执行一次脚本
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
6.9.3 MySQL / MariaDB 轮转配置
vim /etc/logrotate.d/mysql
/var/log/mysqld.log /var/log/mysql/slow.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
create 0640 mysql mysql
su mysql mysql
sharedscripts
postrotate
# 用 flush-logs 让 mysqld 重新打开日志文件
if test -x /usr/bin/mysqladmin && \
/usr/bin/mysqladmin ping &>/dev/null; then
/usr/bin/mysqladmin --defaults-file=/root/.my.cnf flush-logs
fi
endscript
}
/root/.my.cnf中保存轮转专用账号的凭据,需chmod 600:[mysqladmin] user=root password=YourPassword
6.9.4 Tomcat catalina.out 轮转配置(copytruncate 场景)
vim /etc/logrotate.d/tomcat
/usr/local/tomcat/logs/catalina.out {
daily
rotate 14
missingok
notifempty
compress
delaycompress
dateext
copytruncate # Tomcat 不支持重新打开日志,必须用 copytruncate
size 100M # 超过 100M 立即轮转,不等周期
}
6.9.5 Apache httpd 轮转配置
/var/log/httpd/*log {
daily
rotate 52
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
/bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true
endscript
}
6.9.6 综合例题:针对 ssh 服务做日志轮转(每两天一次、保留所有日志、按时间分类)
touch /etc/sshlogrotate.conf
vim /etc/sshlogrotate.conf
daily
rotate 4
create
/var/log/ssh {
create 0600 root sshd
sharedscripts
prerotate
mkdir -p /tmp/ssh/`date +%F`
cp -rf /var/log/ssh.* /tmp/ssh/`date +%F`
endscript
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
echo "ssh轮转成功了" | mail -s "logrotated succeed" root
endscript
}
添加定时任务,每两天执行一次:
crontab -e
0 2 */2 * * logrotate -f /etc/sshlogrotate.conf
6.10 logrotate 邮件报警
6.10.1 安装并启动 postfix
Linux 默认可本机收发邮件,需启动 postfix 服务:
yum install postfix* -y
/etc/init.d/postfix restart # CentOS 6
chkconfig postfix on
systemctl restart postfix # CentOS 7
systemctl enable postfix
测试邮件:
# 将字符串作为邮件正文发给 root
echo "邮件内容" | mail -s "邮件主题" root
# 将 /etc/fstab 内容作为邮件正文
mail -s "邮件主题" root < /etc/fstab
# 查看 root 的邮件
cat /var/mail/root
mail -u root
6.10.2 使用内置 mail 参数
轮转后向 test 用户发送邮件:
/var/log/ssh {
monthly
rotate 2
missingok
mail test # 指定邮件接收者
maillast # 轮转后发邮件,主题为 about-to-expire 的文件 /var/log/ssh.3
}
/var/log/ssh {
monthly
rotate 2
missingok
mail test
mailfirst # 轮转后发邮件,主题为 just-rotated 的文件 /var/log/ssh.1
}
| 参数 | 含义 |
|---|---|
mail EMAIL |
轮转后发送邮件给指定用户 |
maillast |
发送即将被删除的最老一份(about-to-expire) |
mailfirst |
发送刚轮转出的最新一份(just-rotated) |
6.10.3 自定义邮件正文与主题
使用 postrotate 调用 mail 命令可灵活控制:
/var/log/ssh {
monthly
rotate 2
missingok
sharedscripts
postrotate
echo "ssh轮转成功了.xxxxx" | mail -s "logrotated succeed" test
endscript
}
6.11 date 时间格式(编写归档脚本常用)
date -d "4 days ago" +%d # 4 天前的日
date -d "-4 days" +%d # 4 天前的日
date -d "+4 days" +%d # 4 天后的日
date -d "4 days" +%d # 4 天后的日
date -d "tomorrow" +%d
date -d "yesterday" +%d
date -d "4 month" +%m
date -d "1 year" +%Y
date +%F # 2026-08-22
date +%Y%m%d%H%M%S
更多格式参见
man date。
6.12 日志文件的 append-only 属性保护
为避免现有日志被意外覆盖/删除,可给日志文件加 a 属性,只允许在文件尾部追加,不能修改已有数据、不能删除文件。
sudo chattr +a filename # 添加 a 属性
sudo chattr -a filename # 删除 a 属性
sudo lsattr filename # 查看隐藏属性
注意:这只适用于以追加方式打开日志的程序(如
rsyslog);wtmp、btmp不能使用这种方法。加了a属性的文件也无法被 logrotate 用create方式轮转(重命名会失败),需先chattr -a。
七、日志分析实战
7.1 分析方法概览
- 脚本分析:使用 shell/
grep/awk/sed等自行编写分析脚本,最灵活; - 软件分析:常见开源软件有
awstats、webalizer(多用于 Web 日志); - 系统自带:
logwatch可分析/var/log/下的日志并以邮件形式发送报告; - 专业工具:如 Oracle 等数据库的日志巡检工具;
- 集中平台:日志量大、需要检索与可视化时使用 ELK,参见
08-04-ELK日志分析.md;需要指标告警时使用 Zabbix,参见08-05-Zabbix监控.md。
7.2 grep / awk / sed 常用套路
# 关键字过滤(不区分大小写、显示行号)
grep -in 'error' /var/log/messages
# 多关键字(或)
grep -E 'error|fail|warn' /var/log/messages
# 排除干扰行
grep -i error /var/log/messages | grep -v 'ignored'
# 显示匹配行的上下文(前 3 行、后 3 行)
grep -B 3 -A 3 'Segmentation fault' /var/log/messages
grep -C 5 'OOM' /var/log/messages
# 只统计出现次数
grep -c 'Failed password' /var/log/secure
# 递归搜索整个日志目录
grep -rn 'connection refused' /var/log/ 2>/dev/null
# 压缩日志直接搜索
zgrep 'error' /var/log/messages-20260801.gz
zcat /var/log/nginx/access.log-*.gz | awk '{print $1}' | sort | uniq -c
# awk 取字段(默认空格分隔)
awk '{print $1, $2, $3, $5}' /var/log/secure
# awk 条件过滤 + 统计
awk '$6 ~ /Failed/ {print $11}' /var/log/secure | sort | uniq -c | sort -rn
# sed 提取指定行区间
sed -n '100,200p' /var/log/messages
# sed 按时间范围截取(从某时间戳到某时间戳)
sed -n '/Aug 22 09:00/,/Aug 22 10:00/p' /var/log/messages
# 实时跟踪 + 过滤
tail -f /var/log/nginx/access.log | grep --line-buffered ' 500 '
7.3 统计登录失败与暴力破解 IP
# 1) 统计失败登录的来源 IP 及次数(降序)
grep 'Failed password' /var/log/secure \
| awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -20
# 2) 另一种取 IP 方式(更稳健,用正则抽取)
grep 'Failed password' /var/log/secure \
| grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| sort | uniq -c | sort -rn | head -20
# 3) 统计被尝试的用户名
grep 'Failed password' /var/log/secure \
| awk '{for(i=1;i<=NF;i++) if($i=="for") print $(i+1)}' \
| sort | uniq -c | sort -rn
# 4) 统计无效用户(不存在的用户名)
grep 'Invalid user' /var/log/secure | awk '{print $8, $10}' | sort | uniq -c | sort -rn
# 5) 成功登录记录(对照检查是否被攻破)
grep 'Accepted password' /var/log/secure | awk '{print $1,$2,$3,$9,$11}'
# 6) 用二进制日志统计失败登录
lastb | awk '{print $3}' | sort | uniq -c | sort -rn | head -20
# 7) journald 方式(CentOS 7)
journalctl -u sshd --since today | grep 'Failed password'
journalctl _COMM=sshd -p warning --since "-1day"
# 8) 找出失败超过 10 次的 IP,写入黑名单(可配合 firewalld/iptables 封禁)
grep 'Failed password' /var/log/secure \
| grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| sort | uniq -c | awk '$1>10 {print $2}' > /tmp/blacklist.txt
while read ip; do
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=$ip drop"
done < /tmp/blacklist.txt
firewall-cmd --reload
一个完整的暴力破解检测脚本示例:
#!/bin/bash
# deny_hack.sh —— 统计 SSH 失败登录并封禁高频 IP
LOG=/var/log/secure
LIMIT=10
DENY=/etc/hosts.deny
grep 'Failed password' $LOG \
| grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| sort | uniq -c | sort -rn \
| while read count ip; do
if [ "$count" -gt "$LIMIT" ]; then
grep -q "$ip" $DENY || echo "sshd:$ip:deny" >> $DENY
logger -t deny_hack -p auth.warning "blocked $ip ($count failures)"
fi
done
7.4 统计访问量与状态码(Web 日志)
以 Nginx/Apache 的组合日志格式为例:
192.168.1.100 - - [22/Aug/2026:10:23:45 +0800] "GET /index.html HTTP/1.1" 200 1024 "-" "Mozilla/5.0"
$1 $4 $6 $7 $8 $9 $10
LOG=/var/log/nginx/access.log
# 1) 总请求数
wc -l $LOG
# 2) 独立 IP 数(UV 粗略估算)
awk '{print $1}' $LOG | sort -u | wc -l
# 3) 访问量最高的前 20 个 IP
awk '{print $1}' $LOG | sort | uniq -c | sort -rn | head -20
# 4) 状态码分布
awk '{print $9}' $LOG | sort | uniq -c | sort -rn
# 5) 所有 404 请求及其 URL
awk '$9==404 {print $7}' $LOG | sort | uniq -c | sort -rn | head -20
# 6) 所有 5xx 错误(服务端故障)
awk '$9 ~ /^5/ {print $0}' $LOG | tail -50
# 7) 访问量最高的 URL(热点资源)
awk '{print $7}' $LOG | sort | uniq -c | sort -rn | head -20
# 8) 流量统计(第 10 字段为响应字节数,换算为 MB)
awk '{sum+=$10} END {printf "total: %.2f MB\n", sum/1024/1024}' $LOG
# 9) 消耗流量最多的 URL
awk '{a[$7]+=$10} END {for(i in a) printf "%.2f MB %s\n", a[i]/1024/1024, i}' $LOG \
| sort -rn | head -20
# 10) 每小时请求量(PV 趋势)
awk -F'[:[]' '{print $2":"$3}' $LOG | sort | uniq -c
# 11) 每分钟请求量峰值
awk '{print $4}' $LOG | cut -d: -f1-3 | sort | uniq -c | sort -rn | head -10
# 12) User-Agent 统计(识别爬虫)
awk -F'"' '{print $6}' $LOG | sort | uniq -c | sort -rn | head -20
# 13) 来源页面(Referer)统计
awk -F'"' '{print $4}' $LOG | sort | uniq -c | sort -rn | head -20
# 14) 某个 IP 的全部行为
grep '^192.168.1.100 ' $LOG | awk '{print $4, $7, $9}'
# 15) 平均响应时间(需日志格式中含 $request_time,假设为最后一个字段)
awk '{sum+=$NF; n++} END {printf "avg: %.3f s\n", sum/n}' $LOG
7.5 按时间段筛选
# 1) sed 按起止行内容截取(syslog 格式)
sed -n '/Aug 22 09:00:00/,/Aug 22 10:00:00/p' /var/log/messages
# 2) awk 匹配时间字段(Web 日志)
awk '$4 >= "[22/Aug/2026:09:00:00" && $4 <= "[22/Aug/2026:10:00:00"' \
/var/log/nginx/access.log
# 3) grep 按小时粗筛
grep '22/Aug/2026:09:' /var/log/nginx/access.log
# 4) journald 精确时间过滤(最方便)
journalctl --since "2026-08-22 09:00" --until "2026-08-22 10:00"
journalctl --since "-2 hours" -p err
# 5) 找出指定时间之后修改过的日志文件
find /var/log -type f -newermt "2026-08-22 09:00" -ls
# 6) 按天分割大日志
awk '{print > ("/tmp/split-" $1 "-" $2 ".log")}' /var/log/messages
7.6 logwatch 自动分析报告
安装 logwatch:
yum install logwatch -y
rpm -qa | grep logwatch
logwatch-7.3.6-49.el6.noarch
查看配置文件清单:
rpm -qc logwatch-7.3.6-49.el6.noarch
/etc/logwatch/conf/ignore.conf
/etc/logwatch/conf/logwatch.conf
/etc/logwatch/conf/override.conf
查看主配置:
vim /usr/share/logwatch/default.conf/logwatch.conf
cat /usr/share/logwatch/default.conf/logwatch.conf | grep -v '#' | grep -v ^$
LogDir = /var/log # 日志目录
TmpDir = /var/cache/logwatch
MailTo = root # 发邮件给谁
MailFrom = Logwatch
Print =
Range = yesterday # 分析日志的日期范围
Detail = Low # 详细程度:Low/Medium/High
Service = All
mailer = "sendmail -t"
运行分析:
/etc/init.d/postfix restart
logwatch
cat /var/mail/root
# 常用参数
logwatch --detail High --range today --service sshd --print
logwatch --range "between -7 days and today" --mailto root
7.7 日志合并
日志合并可通过追加、排序(sort)实现。
示例文件:
cat 1.txt
1
3
5
7
cat 2.txt
8
4
9
cat 3.txt
6
10
2
合并并按数字排序输出到 4.txt:
sort -n -o 4.txt 1.txt 2.txt 3.txt
其他常用合并方式:
cat access.log-2026082*.log > /tmp/all.log # 简单追加
zcat access.log-*.gz | sort -k4 > /tmp/all.log # 解压后按时间字段排序
sort -m -k4 a.log b.log > merged.log # 已排序文件的归并(省内存)
7.8 日志量与磁盘占用治理
# 1) 查看各分区使用率,确认 /var 是否告急
df -h
df -i # inode 也可能先耗尽
# 2) 定位大日志文件
du -sh /var/log/
du -sh /var/log/* | sort -rh | head -20
find /var/log -type f -size +100M -exec ls -lh {} \;
# 3) journal 占用
journalctl --disk-usage
# 4) 找出已被删除但仍被进程占用的文件(磁盘不释放的典型原因)
lsof | grep deleted
lsof +L1
# 5) 正确清空一个正在被写入的大日志(不要 rm!)
> /var/log/huge.log
truncate -s 0 /var/log/huge.log
cat /dev/null > /var/log/huge.log
# 6) 清理超过 30 天的历史日志
find /var/log -name "*.gz" -mtime +30 -delete
find /var/log -name "*.log-*" -mtime +30 -delete
# 7) 应急清理 journal
journalctl --vacuum-size=200M
journalctl --vacuum-time=7d
治理原则:
- 不要用
rm删除正在被写入的日志,进程仍持有 inode,磁盘空间不会释放;应使用> file清空,或走 logrotate 的copytruncate。 - 为所有自定义应用日志配置 logrotate,不要只依赖手工清理。
- 为 journal 设置
SystemMaxUse=,避免无上限增长。 - 高日志量服务考虑降低级别(把
debug/info调为warning),或用rsyslog的限流指令。 - 大流量场景把日志目录单独挂载一个分区/LVM,避免写满根分区导致系统不可用。
rsyslog 限流与降噪示例:
# 单个进程 5 秒内最多 500 条,超出丢弃(防日志风暴)
$SystemLogRateLimitInterval 5
$SystemLogRateLimitBurst 500
# 丢弃指定噪音日志
:msg, contains, "Deprecated warning" ~
if $programname == 'noisy-app' and $syslogseverity >= 6 then stop
八、审计 auditd
auditd 是内核审计子系统的用户态守护进程,记录谁在什么时间对什么文件/系统调用做了什么,日志写入 /var/log/audit/audit.log。它独立于 rsyslog 与 journald,是满足合规审计(等级保护、PCI-DSS)的关键组件。
8.1 安装与服务管理
yum install audit audit-libs -y
# CentOS 6
service auditd start
chkconfig auditd on
service auditd status
# CentOS 7(注意:auditd 不能用 systemctl restart,需用 service)
systemctl start auditd
systemctl enable auditd
service auditd restart # CentOS 7 上重启 auditd 的正确方式
systemctl status auditd
主要文件:
| 路径 | 作用 |
|---|---|
/etc/audit/auditd.conf |
守护进程配置:日志大小、轮转、磁盘满时的动作 |
/etc/audit/audit.rules(CentOS 6)/ /etc/audit/rules.d/*.rules(CentOS 7) |
持久化审计规则 |
/var/log/audit/audit.log |
审计日志 |
/etc/audit/auditd.conf 关键项:
log_file = /var/log/audit/audit.log
max_log_file = 100 # 单文件最大 100 MB
num_logs = 10 # 保留 10 份
max_log_file_action = ROTATE # 达到上限后轮转(IGNORE/SYSLOG/SUSPEND/ROTATE/KEEP_LOGS)
space_left = 200 # 剩余 200MB 触发 space_left_action
space_left_action = SYSLOG
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
flush = INCREMENTAL_ASYNC
auditd 有自己的轮转机制(
max_log_file+num_logs),一般不需要也不建议再用 logrotate 管理audit.log。
8.2 auditctl 规则管理
auditctl -s # 查看审计状态(enabled、pid、丢包数等)
auditctl -l # 列出当前生效的规则
auditctl -D # 删除所有规则(临时)
auditctl -e 1 # 启用审计(0 禁用,2 锁定至重启)
auditctl -b 8192 # 设置内核缓冲区大小
auditctl -r 100 # 设置每秒消息速率上限
文件/目录监控规则(watch 规则):
# -w 监控路径 -p 权限(r读 w写 x执行 a属性变更) -k 关键字(便于检索)
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/shadow -p wa -k shadow_changes
auditctl -w /etc/sudoers -p wa -k sudoers_changes
auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config
auditctl -w /var/log/audit/ -p wa -k audit_log_tamper
auditctl -w /usr/bin/passwd -p x -k passwd_exec
auditctl -w /etc/ -p wa -k etc_changes # 监控整个目录(递归)
# 删除某条规则(把 -w 换成 -W,参数保持一致)
auditctl -W /etc/passwd -p wa -k passwd_changes
系统调用监控规则(syscall 规则):
# -a 追加规则 always,exit:在系统调用退出时总是记录
# -F 过滤条件 -S 系统调用名
# 监控所有删除/重命名操作(uid>=1000 即普通用户)
auditctl -a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat \
-F auid>=1000 -F auid!=4294967295 -k delete
# 监控权限变更
auditctl -a always,exit -F arch=b64 -S chmod -S fchmod -S chown -S fchown \
-F auid>=1000 -k perm_mod
# 监控挂载操作
auditctl -a always,exit -F arch=b64 -S mount -k mounts
# 监控指定用户的所有命令执行
auditctl -a always,exit -F arch=b64 -S execve -F auid=1001 -k user1001_cmd
# 监控对某目录的访问失败(权限拒绝)
auditctl -a always,exit -F dir=/data/secret -F success=0 -k secret_denied
规则持久化:
# CentOS 7:写入 rules.d 下的文件
vim /etc/audit/rules.d/audit.rules
## 先删除已有规则
-D
## 缓冲区
-b 8192
## 关键文件监控
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/group -p wa -k group_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config
## 删除操作监控
-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete
## 权限变更监控
-a always,exit -F arch=b64 -S chmod -S fchmod -S chown -S fchown -F auid>=1000 -k perm_mod
## 锁定规则,禁止运行时修改(放在最后,需重启生效)
# -e 2
augenrules --load # 加载 rules.d 下的规则(CentOS 7)
service auditd restart
auditctl -l # 验证
8.3 ausearch 检索审计日志
ausearch -k passwd_changes # 按关键字(-k)检索
ausearch -k delete -i # -i 把数字 ID 翻译成名字(可读性关键)
ausearch -f /etc/passwd -i # 按文件名检索
ausearch -ui 1000 -i # 按用户 ID
ausearch -ul root -i # 按登录用户名
ausearch -p 1234 # 按进程 ID
ausearch -sc execve -i # 按系统调用
ausearch -m USER_LOGIN -i # 按消息类型
ausearch -m USER_LOGIN --success no -i # 登录失败事件
ausearch -ts today -i # 时间起点:today/yesterday/recent/this-week
ausearch -ts 08/22/2026 09:00:00 -te 08/22/2026 10:00:00 -i
ausearch -ts recent -m AVC -i # SELinux 拒绝事件
ausearch -x /usr/bin/passwd -i # 按可执行文件
ausearch -i --input /var/log/audit/audit.log.1 # 指定旧日志文件
常见消息类型(-m):
| 类型 | 含义 |
|---|---|
USER_LOGIN / USER_LOGOUT |
用户登录/登出 |
USER_AUTH / USER_ACCT |
认证、账号检查 |
USER_CMD |
通过 sudo 执行的命令 |
CRED_ACQ / CRED_DISP |
凭据获取/释放 |
SYSCALL / PATH / CWD |
系统调用及其涉及的路径 |
CONFIG_CHANGE |
审计规则本身被修改 |
AVC |
SELinux 访问拒绝 |
ANOM_ABEND |
进程异常终止 |
8.4 aureport 生成审计报表
aureport # 汇总报表
aureport -au # 认证事件报表(authentication)
aureport -au --failed # 认证失败报表(找暴力破解)
aureport -au --success # 认证成功报表
aureport -l # 登录报表
aureport -l --failed -i
aureport -u # 用户活动报表
aureport -f # 文件访问报表
aureport -f --failed -i # 访问被拒的文件
aureport -s # 系统调用报表
aureport -x # 可执行文件报表
aureport -x --summary # 汇总(按次数统计)
aureport -c # 配置变更报表
aureport -m # 账号修改报表
aureport -k # 按关键字汇总
aureport -p # 进程报表
aureport -ts today -te now -i --summary # 指定时间范围
aureport --start yesterday --end now -i
8.5 监控文件变更完整实操
# 1) 添加规则:监控 /etc/passwd 的写入与属性变更
auditctl -w /etc/passwd -p wa -k passwd_watch
auditctl -l
# 2) 触发一次变更
useradd testaudit
# 3) 检索审计记录
ausearch -k passwd_watch -i | tail -40
输出片段(-i 翻译后)示例:
type=SYSCALL msg=audit(08/22/2026 10:15:32.123:456) : arch=x86_64 syscall=openat
success=yes exit=4 ... auid=root uid=root gid=root euid=root
comm=useradd exe=/usr/sbin/useradd key=passwd_watch
type=PATH msg=audit(...) : item=0 name=/etc/passwd inode=1234567 ...
关键字段解读:
| 字段 | 含义 |
|---|---|
auid |
审计用户 ID(原始登录用户,即使 su 到 root 也不变,追责关键) |
uid / euid |
实际/有效用户 ID |
comm |
触发事件的命令名 |
exe |
命令的完整路径 |
key |
规则中 -k 指定的关键字 |
success |
操作是否成功 |
syscall |
触发的系统调用 |
# 4) 生成文件访问报表
aureport -f -i --summary
# 5) 清理规则
auditctl -W /etc/passwd -p wa -k passwd_watch
补充:
/var/log/audit/audit.log只有 root 可读;SELinux 的拒绝事件也记录在此,ausearch -m AVC -ts recent是排查 SELinux 问题的标准手段(详见防火墙与 SELinux 相关文档)。
九、排错清单
9.1 日志不写入 / 日志为空
| 检查点 | 命令 / 处理 |
|---|---|
| 日志服务是否运行 | systemctl status rsyslog / service rsyslog status;ps -ef \| grep rsyslog |
| 配置语法是否有误 | rsyslogd -N1;查看 journalctl -u rsyslog -n 50 中的报错 |
| 配置是否已重载 | systemctl restart rsyslog(改完必须重启/reload) |
| 选择器写错 | 用 logger -p local5.info -t TEST "hi" 造日志验证规则是否命中 |
前面的规则用了 stop/~ |
匹配到更早的规则被丢弃,检查规则顺序 |
| 目标目录不存在或无权限 | ls -ld /var/log/xxx/;rsyslog 需要目录存在(或用 $CreateDirs on) |
| 磁盘或 inode 满 | df -h、df -i |
文件被加了 a/i 属性 |
lsattr /var/log/xxx;必要时 chattr -a |
| SELinux 阻止写入非标准路径 | ausearch -m AVC -ts recent;semanage fcontext -a -t var_log_t "/data/logs(/.*)?" 后 restorecon -Rv /data/logs |
| 轮转后进程仍写旧 inode | 见 6.6 节,需 kill -HUP/USR1 或改用 copytruncate |
| 日志被限流丢弃 | 检查 $SystemLogRateLimitBurst、journald 的 RateLimitBurst |
| journal 查不到历史 | journal 默认易失,需开启持久化(5.3 节) |
9.2 日志文件过大占满磁盘
| 现象 | 原因与处理 |
|---|---|
df -h 显示满,但 du -sh 加起来远小于占用 |
有被删除但仍被占用的文件:lsof \| grep deleted,重启对应进程或用 > /proc/<pid>/fd/<n> 清空 |
| 某个日志文件几十 G | 用 > file 或 truncate -s 0 file 清空,不要 rm;随后补上 logrotate 配置 |
| logrotate 已配置但文件仍增长 | 检查 logrotate -d 输出、/var/lib/logrotate/logrotate.status 中的时间、size/minsize 条件是否满足 |
| journal 占用过大 | journalctl --disk-usage;journalctl --vacuum-size=200M;配置 SystemMaxUse= |
df -i 显示 inode 满 |
大量小日志碎片文件,find /var/log -type f \| wc -l,清理历史文件、启用 compress |
| 应用 debug 级别开太多 | 调高日志级别,或用 rsyslog 限流/丢弃规则降噪 |
| 根本治理 | 日志目录单独分区/LVM;集中收集到日志服务器(4.11 节)并在本地只留短期 |
9.3 时间戳 / 时区不对
| 检查点 | 命令 / 处理 |
|---|---|
| 系统时区 | timedatectl(CentOS 7);timedatectl set-timezone Asia/Shanghai;CentOS 6:cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime |
| 系统时间是否准确 | date;ntpdate ntp.aliyun.com;systemctl enable --now chronyd;chronyc sources -v |
| 硬件时钟与系统时钟不一致 | hwclock --show;hwclock -w(系统→硬件)/ hwclock -s(硬件→系统) |
| RTC 按 UTC 还是本地时间 | timedatectl 输出中的 RTC in local TZ;timedatectl set-local-rtc 0 |
| 日志时间戳无年份、无时区 | rsyslog 传统格式只到秒且无时区,改用高精度模板:$ActionFileDefaultTemplate RSYSLOG_FileFormat |
| 远程日志时间与服务端不符 | 远程日志默认保留发送端时间戳;确保所有客户端 NTP 同步;或在服务端模板改用 %timegenerated% 统一为接收时间 |
| Docker/容器内日志时区错 | 挂载 /etc/localtime:/etc/localtime:ro 或设置 TZ=Asia/Shanghai |
dmesg 时间看不懂 |
用 dmesg -T |
| journal 显示时区 | journalctl --utc 用 UTC 显示;否则按本地时区 |
9.4 轮转不生效
| 检查点 | 命令 / 处理 |
|---|---|
| 配置语法/匹配是否正确 | logrotate -d /etc/logrotate.d/xxx(预演,重点看 "considering log ... " 与 "log does not need rotating") |
| 周期未到 | cat /var/log/logrotate.status 查看上次轮转时间;测试用 logrotate -f 强制 |
| 触发任务未运行 | systemctl list-timers \| grep logrotate;systemctl status logrotate.timer;CentOS 6 检查 crond 是否运行、/etc/cron.daily/logrotate 是否可执行 |
| 配置文件名含被忽略的扩展名 | logrotate 忽略 .rpmsave、.rpmnew、.dpkg-dist、~ 等(tabooext);文件名不要带点号后缀 |
| 配置文件权限/属主不对 | 必须 root 可读;目录 /etc/logrotate.d 权限 755 |
| 日志文件为空 | 配了 notifempty 时空文件不轮转,属正常行为 |
| 日志目录属主非 root | 报 parent directory has insecure permissions 或 skipping,需加 su USER GROUP 指令 |
| 轮转成功但进程还写旧文件 | 缺少 postrotate 信号,或该程序不支持,改用 copytruncate(6.6 节) |
| 同一日志被两处配置重复管理 | 例如自己写的配置与 rpm 自带配置冲突,报 duplicate log entry,删掉其中一份 |
文件加了 a 属性 |
create 方式无法重命名,先 chattr -a 或改用 copytruncate |
| SELinux 阻止 | ausearch -m AVC -ts recent \| grep logrotate |
9.5 远程日志收不到
按"客户端 → 网络 → 服务端"顺序排查:
# ---------- 服务端 ----------
# 1) 模块是否加载、端口是否监听
grep -E 'imtcp|imudp|514' /etc/rsyslog.conf
netstat -ntlup | grep :514
ss -ntlup | grep :514
# 2) 防火墙是否放行
iptables -L -n | grep 514 # CentOS 6
firewall-cmd --list-ports # CentOS 7
firewall-cmd --permanent --add-port=514/tcp; firewall-cmd --reload
# 3) SELinux 是否允许绑定 514
getenforce
semanage port -l | grep syslog
semanage port -a -t syslogd_port_t -p tcp 514
# 4) 抓包确认是否收到数据
tcpdump -i any -nn port 514
tcpdump -i eth0 -nn -A udp port 514
# ---------- 客户端 ----------
# 5) 转发规则是否正确(@ = UDP,@@ = TCP,端口不能漏)
grep '@' /etc/rsyslog.conf
# 6) 网络连通性
ping 192.168.1.2
telnet 192.168.1.2 514
nc -zv 192.168.1.2 514
nc -uzv 192.168.1.2 514
# 7) 造一条测试日志
logger -p local5.info -t REMOTETEST "hello $(hostname) $(date)"
# 8) 查看客户端自身报错
journalctl -u rsyslog -n 50
tail -f /var/log/messages | grep rsyslogd
常见原因汇总:
| 原因 | 处理 |
|---|---|
服务端未打开 imtcp/imudp 注释 |
取消注释并重启 rsyslog |
| 防火墙/安全组未放行 514 | 放行 TCP 与 UDP 514 |
| SELinux 未允许非默认端口 | semanage port -a -t syslogd_port_t -p tcp 514 |
客户端写成单 @ 但服务端只开 TCP(或反之) |
协议对齐:@=UDP,@@=TCP |
| 客户端规则未匹配到日志 | 检查 facility 是否与应用配置一致(如 SyslogFacility local5) |
| 服务端有规则但没有落盘文件 | 服务端需要一条 local5.* /var/log/ssh 之类的动作规则 |
| 多主机日志混在一起无法区分 | 主机名必须唯一,并用动态模板按 %HOSTNAME% 分目录(4.11 节步骤五) |
| UDP 丢包 | 改用 TCP,并配置磁盘队列(4.11 节步骤二) |
| 服务端磁盘满 | df -h,为集中日志配置 logrotate |
服务端日志被 stop 规则提前丢弃 |
调整规则顺序 |
9.6 其他常见坑
rsyslog配置中,-/path表示不立即刷盘,非正常关机会丢日志。logrotate的size会忽略周期立即轮转,minsize则需同时满足大小和周期。- 给日志文件加
a属性仅适用于追加写入的日志;wtmp、btmp不适用。 - CentOS 7 默认不持久化 journal 日志,重启后
/run/log/journal/内容丢失;需创建/var/log/journal/并重启systemd-journald。 - CentOS 7 上重启
auditd要用service auditd restart,systemctl restart auditd可能被拒绝。 - 二进制日志(
wtmp/btmp/utmp/lastlog/journal)不能用cat/grep直接读。 - 用
rm删日志不会释放磁盘(进程仍持有 inode),要用> file清空。 journalctl和/var/log/messages可能显示同一条日志,不是重复记录问题。
十、练习
10.1 rsyslog 基础练习
- 把所有应用的日志(info 及以上)写入
/var/log/logfile,该如何配置? - 把 ssh 的日志指定记录到
/var/log/ssh下,有哪些方法? - 上网查阅并了解日志分析与日志合并的方法、概念。
参考答案
题 1:
vim /etc/rsyslog.conf
*.info /var/log/logfile
service rsyslog restart
ls -l /var/log/logfile
系统会自动创建该文件:
-rw------- 1 root root 784 10月 26 01:20 /var/log/logfile
题 2:将 ssh 日志记录到 /var/log/ssh 的方法
方法一:将 ssh 托管到 xinetd 下,直接写文件:
log_type = file /var/log/ssh
方法二:托管到 xinetd 下,走 syslog:
log_type = syslog local5 info
并在 /etc/rsyslog.conf 中添加:
local5.info /var/log/ssh
方法三:不托管到 xinetd,直接使用 sshd:
vim /etc/ssh/sshd_config
SyslogFacility local5
/etc/init.d/sshd restart
vim /etc/rsyslog.conf
local5.* /var/log/ssh
/etc/init.d/rsyslog restart
10.2 logrotate 顺序思考题
先执行:
logger -t "哈哈" "你好"
tail -3 /var/log/messages
默认此信息在当前 messages 中。
场景 A:
sharedscripts
postrotate
logger -t "呵呵" "再见"
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
答:logger 在 kill -HUP 之前执行,rsyslog 仍持有旧文件句柄,日志写入旧文件,轮转后位于 messages.1。
场景 B:
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
logger -t "呵呵" "再见"
endscript
答:kill -HUP 先执行,rsyslog 重新打开新文件,日志写入 messages。
10.3 日志轮转练习(clientssh.log 完整题)
题目
- 为某个服务日志(假设为
/var/log/clientssh.log)配置日志轮转,要求:- 配置写到
/etc/logrotate.conf中,或者写到/etc/logrotate.d/目录下的子配置文件中; - 每天轮转一次;
- 最多保留 4 个副本。
- 配置写到
- 在对
/var/log/clientssh.log进行轮转之前,自动把最后一个副本(即/var/log/clientssh.log.4)拷贝到/backup/4天前的年/4天前的月/4天前的天.clientssh.log。- 例如:今天是
2014-07-28,则拷贝后的文件名为/backup/2014/7/24.clientssh.log。
- 例如:今天是
- 轮转完成后,发一封邮件通知
root用户,邮件主题和正文自定义。
参考解答
编辑 /etc/logrotate.conf(也可在 /etc/logrotate.d/ 下新建独立文件):
vim /etc/logrotate.conf
加入以下配置:
/var/log/clientssh.log {
rotate 4
daily
create 0664 root root
sharedscripts
prerotate
mkdir -p /backup/`date -d "-4 days" +%Y`/`date -d "-4 days" +%m`/
cp /var/log/clientssh.log.4 /backup/`date -d "-4 days" +%Y`/`date -d "-4 days" +%m`/`date -d "-4 days" +%d`.clientssh.log
endscript
sharedscripts
postrotate
kill -HUP `cat /var/run/sshd.pid`
echo "日志轮转了" | mail -s 'ssh日志轮转了' root
endscript
}
指令解析
| 指令/脚本段 | 作用 |
|---|---|
rotate 4 |
最多保留 4 个历史副本 |
daily |
每天轮转一次 |
create 0664 root root |
轮转后以指定权限和属主重新创建空日志文件 |
sharedscripts |
多次匹配日志时只执行一次脚本(与通配符配合使用) |
prerotate ... endscript |
轮转之前执行的 Shell 命令:创建按年月日分层的备份目录,并将 .log.4 拷贝为 4 天前日期命名的归档文件 |
postrotate ... endscript |
轮转之后执行的 Shell 命令:给 sshd 发送 HUP 信号让其重新打开日志文件,并向 root 发送通知邮件 |
date -d "-4 days" +%Y/%m/%d |
取 4 天前的年、月、日,用于按题目命名归档文件 |
关于 kill -HUP
kill -HUP $(cat /var/run/sshd.pid)(原文写作反引号形式)并非杀掉 sshd,而是通知它重新读取配置/重新打开日志文件。如果对应服务不支持 HUP 重新打开日志,可改用 copytruncate 方式,无需重启进程。
手动测试
配置完成后,可使用以下命令手动触发一次轮转(调试用):
logrotate -d /etc/logrotate.conf # 预演,不真正执行
logrotate -f /etc/logrotate.conf # 强制执行一次轮转
确认邮件是否发出:
mail -u root
10.4 练习:will-log 独立轮转配置
为 will-log 配置独立轮转:
- 创建 15M 的日志文件
/var/log/will-log; - 创建独立轮转配置
/etc/logrotate.d/will-log; - 同一个文件最多轮转 7 次;
- 只要文件大于 20M 就轮转,不管周期是否已到;
- 轮转之后创建新文件,权限 0664,属主 root:root;
- 用日期作为轮转后文件的后缀名(
dateext); - 如果日志文件不存在,不报错(
missingok); - 该服务每天自动轮转一次(
daily); - 每分钟向该日志文件追加 10M 数据;
- 每分钟调用一次该轮转服务(可用
logrotate /etc/logrotate.d/will-log或配合 cron)。
参考配置:
/var/log/will-log {
daily
rotate 7
size 20M
create 0664 root root
dateext
missingok
}
参考操作步骤:
# 1) 创建 15M 日志文件
head -c 15M /dev/zero > /var/log/will-log
# 2) 写入轮转配置
vim /etc/logrotate.d/will-log
# 3) 每分钟追加 10M 数据 + 每分钟触发轮转
crontab -e
* * * * * head -c 10M /dev/zero >> /var/log/will-log
* * * * * /usr/sbin/logrotate /etc/logrotate.d/will-log
# 4) 观察结果
watch -n 10 'ls -lh /var/log/will-log*'
10.5 练习:远程日志集中收集
- 准备两台机器:日志服务器
192.168.1.2、客户端192.168.1.61。 - 在服务端开启 TCP 514 接收,验证端口监听。
- 在客户端把 sshd 的 facility 改为
local5,并把local5.*转发到服务端。 - 在服务端把
local5.*落盘到/var/log/ssh,从第三台机器 ssh 客户端并验证日志。 - 进阶:在服务端使用动态模板,把不同客户端的日志分别存放到
/var/log/remote/<主机名>/<日期>-<程序名>.log。 - 进阶:为
/var/log/remote/下的日志编写 logrotate 配置,按天轮转、压缩、保留 30 天。
(参考步骤见 4.11 节全部内容;轮转配置可参考 6.9 节。)
10.6 练习:journald 持久化与查询
- 确认当前 journal 是易失还是持久(查看
/run/log/journal/与/var/log/journal/)。 - 通过创建目录与修改
journald.conf两种方式开启持久化,并限制最大占用 500M。 - 重启系统,使用
journalctl -b -1验证能查到上次启动的日志。 - 分别用
-u、-p、--since、-k、_COMM=查询 sshd 相关日志。 - 用
-o json-pretty输出一条日志,观察结构化字段。 - 用
--disk-usage与--vacuum-time=1week完成一次清理。
10.7 练习:日志分析
- 统计
/var/log/secure中失败登录次数最多的前 10 个 IP。 - 找出这些 IP 尝试过的用户名清单。
- 统计 Nginx 访问日志的状态码分布、Top 20 URL、每小时 PV。
- 找出 5xx 错误对应的完整请求行,并按 URL 汇总。
- 编写脚本:每 5 分钟检测失败登录超过 10 次的 IP 并写入
/etc/hosts.deny,同时用logger记录一条auth.warning日志。
(参考命令见 7.3、7.4 节。)
10.8 练习:auditd 文件审计
- 安装并启动 auditd,确认
auditctl -s输出。 - 添加规则监控
/etc/passwd、/etc/shadow、/etc/sudoers的写入与属性变更。 - 执行
useradd、passwd、visudo触发事件。 - 用
ausearch -k <key> -i检索并解读auid、comm、exe字段。 - 用
aureport -f -i --summary、aureport -au --failed生成报表。 - 把规则写入
/etc/audit/rules.d/audit.rules实现持久化,重启后用auditctl -l验证。
十一、命令速查
| 类别 | 命令 | 作用 |
|---|---|---|
| 文本日志 | tail -f /var/log/messages |
实时跟踪系统日志 |
| 文本日志 | tail -n 100 FILE / less FILE |
查看尾部 / 分页阅读 |
| 文本日志 | grep -in 'error' FILE |
关键字检索 |
| 文本日志 | zgrep 'error' FILE.gz |
检索压缩日志 |
| 登录记录 | last / last -F / last -x |
读 wtmp:登录历史、完整时间、开关机记录 |
| 登录记录 | lastb |
读 btmp:失败登录记录 |
| 登录记录 | who / who -aH / who -b / w |
读 utmp:在线用户、启动时间 |
| 登录记录 | lastlog / lastlog -u USER |
用户最后一次登录 |
| 消息 | write / wall / mesg |
发送/控制终端消息 |
| 内核日志 | dmesg -T / dmesg -w / dmesg -l err |
内核环形缓冲区(务必加 -T) |
| rsyslog | logger -t TAG -p facility.priority "msg" |
发送测试日志 |
| rsyslog | rsyslogd -N1 |
配置语法检查 |
| rsyslog | systemctl restart rsyslog / service rsyslog restart |
重启日志服务(C7/C6) |
| rsyslog | netstat -ntlup \| grep :514 / ss -ntlup \| grep :514 |
确认远程接收端口 |
| rsyslog | @host:514 / @@host:514 |
转发:UDP / TCP |
| journal | journalctl -u UNIT -f |
实时跟踪指定服务日志 |
| journal | journalctl -xe |
排查服务启动失败的第一步 |
| journal | journalctl -b / -b -1 / --list-boots |
本次/上次启动的日志 |
| journal | journalctl -p err --since today |
按级别与时间过滤 |
| journal | journalctl -k |
只看内核日志 |
| journal | journalctl _COMM=sshd _PID=123 |
按结构化字段过滤 |
| journal | journalctl -o verbose / -o json-pretty / -o cat |
切换输出格式 |
| journal | journalctl --disk-usage |
查看日志占用空间 |
| journal | journalctl --vacuum-size=200M / --vacuum-time=2weeks |
清理日志 |
| journal | /etc/systemd/journald.conf → Storage=persistent |
开启持久化 |
| 轮转 | logrotate -d /etc/logrotate.conf |
调试预演(不修改文件) |
| 轮转 | logrotate -fv /etc/logrotate.conf |
强制轮转并显示过程 |
| 轮转 | cat /var/lib/logrotate/logrotate.status |
查看上次轮转时间 |
| 轮转 | systemctl list-timers \| grep logrotate |
查看轮转触发计划(C7.4+) |
| 轮转 | kill -HUP / kill -USR1 / copytruncate |
让进程重新打开日志的三种思路 |
| 属性 | chattr +a / -a / lsattr |
日志追加保护属性 |
| 审计 | auditctl -l / -s / -w PATH -p wa -k KEY |
审计规则管理 |
| 审计 | ausearch -k KEY -i / -f FILE -i / -ts today |
检索审计日志 |
| 审计 | aureport -au --failed / -f --summary |
生成审计报表 |
| 审计 | augenrules --load / service auditd restart |
加载持久化规则 |
| 分析 | awk '{print $1}' access.log \| sort \| uniq -c \| sort -rn |
统计 Top IP |
| 分析 | logwatch --detail High --range today --print |
自动分析报告 |
| 分析 | sort -n -o out.txt a.txt b.txt |
合并并排序多份日志 |
| 治理 | du -sh /var/log/* \| sort -rh \| head |
定位大日志 |
| 治理 | lsof \| grep deleted |
找出已删除但仍占用磁盘的日志 |
| 治理 | > FILE / truncate -s 0 FILE |
正确清空正在写入的日志 |
十二、相关文档
| 主题 | 文档 |
|---|---|
| 系统启动流程、运行级别、systemd 服务管理与 unit 编写、at/cron/systemd timer 定时任务 | 08-01 |
| ELK(Elasticsearch + Logstash + Kibana)日志集中检索与可视化平台 | 08-04-ELK日志分析.md |
| Zabbix 监控与告警 | 08-05-Zabbix监控.md |
| dump/restore 文件系统备份与恢复、LVM 快照备份 | 06-备份与恢复.md |
| 防火墙放行 514 端口、SELinux 端口与文件上下文 | 防火墙与 SELinux 相关文档 |
此处评论已关闭