Linux日志切割神器logrotate原理介绍和配置详解

前言

在Linux环境中能够帮助我们分析问题蛛丝马迹的有效办法之一便是日志,常见的如操作系统syslog日志/var/log/syslog,应用程序Nginx日志/var/log/nginx/*.log。但如果服务器数量较多,日志文件大小增长较快,不断消耗磁盘空间就会触发告警,如果需要人为定期按照各种维度去手动清理日志就显得十分棘手。为了节省空间和方便整理,可以将日志文件按时间或大小分成多份,删除时间久远的日志文件,这就是通常说的日志滚动(log rotation)。logrotate(GitHub地址) 诞生于 1996/11/19 是一个Linux系统日志的管理工具,本文会详细介绍Linux日志切割神器logrotate的原理和配置。
logrotate 是一个 linux 系统日志的管理工具。可以对单个日志文件或者某个目录下的文件按时间 / 大小进行切割,压缩操作;指定日志保存数量;还可以在切割之后运行自定义命令。
logrotate 是基于 crontab 运行的(准确来说是基于timer和crontab定时机制),所以这个时间点是由 crontab 控制的,具体可以查询 crontab 的配置文件 /etc/anacrontab。 系统会按照计划的频率运行 logrotate,通常是每天。在大多数的 Linux 发行版本上,计划每天运行的脚本位于 /etc/cron.daily/logrotate。
主流 Linux 发行版上都默认安装有 logrotate 包,如果你的 linux 系统中找不到 logrotate, 可以使用 apt-get 或 yum 命令来安装。

logrotate运行机制

logrotate 在很多 Linux 发行版上都是默认安装的。系统会定时运行 logrotate,一般是每天一次。系统是这么实现按天执行的。crontab 会每天定时执行 /etc/cron.daily 目录下的脚本,而这个目录下有个文件叫 logrotate。在 ubuntu 上脚本内容是这样的:

方式1 contab模式

1)检查Cron服务的状态:

systemctl status cron.service

2)检查执行Logrotate的定时任务

#cat /etc/crontab 

17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6   * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

定时任务不执行一般问题都出现在后面的命令,所以检查一下,手动执行“test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )”,看是否报错。

3)检查定时任务下logrotate脚本

cron task:/etc/cron.daily/logrotate,每天运行一次logrotate脚本

#!/bin/sh
 
# skip in favour of systemd timer
# 如果systemd存在的话,就使用systemd timer启动,不再使用cron.daily拉起logrotate
if [ -d /run/systemd/system ]; then
    exit 0
fi

# this cronjob persists removals (but not purges)
if [ ! -x /usr/sbin/logrotate ]; then
    exit 0
fi
#  执行logrotate应用,其配置文件为/etc/logrotate.conf
/usr/sbin/logrotate /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit $EXITVALUE

再来看一下/etc/logrotate.conf配置文件内容:

# see "man logrotate" for details
# rotate log files weekly
weekly

# use the adm group by default, since this is the owning group
# of /var/log/syslog.
su root adm

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
#dateext

# uncomment this if you want your log files compressed
#compress

# packages drop log rotation information into this directory
# /var/log下每种日志的具体配置,可以看/etc/logrotate.d/ 目录下配置文件的具体配置
include /etc/logrotate.d

# system-specific logs may be also be configured here.

比如syslog的配置在/etc/logrotate.d/rsyslog中:

/var/log/syslog
{
    su root root
	rotate 7
	daily
	missingok
	notifempty
	delaycompress
	compress
	postrotate
		/usr/lib/rsyslog/rsyslog-rotate
	endscript
}

/var/log/mail.info
/var/log/mail.warn
/var/log/mail.err
/var/log/mail.log
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/lpr.log
/var/log/cron.log
/var/log/debug
/var/log/messages
{
   su root root
	rotate 4
	weekly
	missingok
	notifempty
	compress
	delaycompress
	sharedscripts
	postrotate
		/usr/lib/rsyslog/rsyslog-rotate
	endscript
}

4)检查logrotate服务的状态

#systemctl status logrotate.service

● logrotate.service - Rotate log files
     Loaded: loaded (/lib/systemd/system/logrotate.service; static; vendor preset: enabled)
     Active: failed (Result: exit-code) since Wed 2021-12-08 00:00:03 +08; 16h ago
TriggeredBy: ● logrotate.timer
       Docs: man:logrotate(8)
             man:logrotate.conf(5)
   Main PID: 2811680 (code=exited, status=1/FAILURE)

Dec 08 00:00:01 systemd[1]: Starting Rotate log files...
Dec 08 00:00:03 logrotate[2811680]: error: failed to rename /usr/squid/logs/access.log to /usr/squid/logs/access.log-20211208: Read-only file system

这里通过TriggeredBy:logrotate.timer可以发现logrotate确实是通过Systemd timer的方式来做定时任务的。

方式2 Systemd模式

使用Systemd timer模式主要是两个服务,一个是logrotate.service,logrotate.timer

1)查看logrotate.service的状态

#systemctl status logrotate.service

● logrotate.service - Rotate log files
     Loaded: loaded (/lib/systemd/system/logrotate.service; static; vendor preset: enabled)
     Active: failed (Result: exit-code) since Wed 2021-12-08 00:00:03 +08; 16h ago
TriggeredBy: ● logrotate.timer
       Docs: man:logrotate(8)
             man:logrotate.conf(5)
   Main PID: 2811680 (code=exited, status=1/FAILURE)

Dec 08 00:00:01 systemd[1]: Starting Rotate log files...
Dec 08 00:00:03 logrotate[2811680]: error: failed to rename /usr/squid/logs/access.log to /usr/squid/logs/access.log-20211208: Read-only file system

TriggeredBy: logrotate.timer;这里可以知道这个服务是由logrotate.timer触发执行的;这里有Read-only file system的报错,主要是Systemd的logrotate.service文件中,对logrotate加了文件读写保护,当 ProtectSystem=full这个参数时,会把 /usr/, /boot, /efi, /etc 挂载为只读,如果是 ProtectSystem=strict 那么整个文件系统都会挂载为只读。刚好我的日志目录就在/usr/下,因此需要再加一个参数:ReadWritePaths=/usr/squid/logs即可,然后systemctl daemon-reload && systemctl restart logrotate.service。这里需要思考的问题是日志目录设置在/usr/下是否合理?

[Unit]
Description=Rotate log files
Documentation=man:logrotate(8) man:logrotate.conf(5)
ConditionACPower=true

[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf

# performance options
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7

# hardening options
#  details: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
#  no ProtectHome for userdir logs
#  no PrivateNetwork for mail deliviery
#  no ProtectKernelTunables for working SELinux with systemd older than 235
#  no MemoryDenyWriteExecute for gzip on i686
PrivateDevices=true
PrivateTmp=true
ProtectControlGroups=true
ProtectKernelModules=true
ProtectSystem=full
RestrictRealtime=true
ReadWritePaths=/usr/squid/logs

2)检查logrotate.timer的状态

# systemctl status logrotate.timer   
● logrotate.timer - Daily rotation of log files
     Loaded: loaded (/lib/systemd/system/logrotate.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Thu 2021-08-05 11:08:00 +08; 4 months 3 days ago
    Trigger: Thu 2021-12-09 00:00:00 +08; 7h left
   Triggers: ● logrotate.service
       Docs: man:logrotate(8)
             man:logrotate.conf(5)

Trigger:可以看出他的触发时间
Triggers:可以看出他将触发的服务

查看logrotate.timer的配置

[Unit]
Description=Daily rotation of log files
Documentation=man:logrotate(8) man:logrotate.conf(5)

[Timer]
OnCalendar=daily
AccuracySec=12h
Persistent=true
#Unit:真正要执行的任务,默认是同名的带有.service后缀的单元

[Install]
WantedBy=timers.target

3)检查logrotate脚本
使用logrotate切割的脚本一般都放在/etc/logrotate.d/ 下,因此我的squid切割也在这个目录下,可以通过logrotate -d /etc/logrotate.d/squid 检测一下,-d表示debug模式;在这里debug时候发现logrotate有一个status状态文件/var/lib/logrotate/status,也就是记录下文件logrotate的时间,在今天有做过rotate的话,那么就不会再一次进行,如果想要测试,可以编辑这个文件你想要测试的文件的时间,比如发现里面有一条日志:“/usr/squid/logs/access.log” 2021-12-9-14:33:28 说明今天做了切割了,还想做测试则可以改为:“/usr/squid/logs/access.log” 2021-12-7-14:33:28,那么就可以再一次测试了。注意:这个status的状态只能改时间,直接删除这一条是不生效的。

4)验证

systemctl restart logrotate.service

重新执行systemctl restart logrotate.service后,就可以看到被切割的日志;如:access.log-20211209等

logrotate原理

logrotate 是怎么做到滚动日志时不影响程序正常的日志输出呢?logrotate 提供了两种解决方案。 1. create 2. copytruncate

Linux 文件操作机制

介绍一下相关的 Linux 下的文件操作机制。
Linux 文件系统里文件和文件名的关系如下图。
在这里插入图片描述
目录也是文件,文件里存着文件名和对应的 inode 编号。通过这个 inode 编号可以查到文件的元数据和文件内容。文件的元数据有引用计数、操作权限、拥有者 ID、创建时间、最后修改时间等等。文件件名并不在元数据里而是在目录文件中。因此文件改名、移动,都不会修改文件,而是修改目录文件。
借《UNIX 环境高级编程》里的图说一下进程打开文件的机制。

在这里插入图片描述
进程每新打开一个文件,系统会分配一个新的文件描述符给这个文件。文件描述符对应着一个文件表。表里面存着文件的状态信息(O_APPEND/O_CREAT/O_DIRECT…)、当前文件位置和文件的 inode 信息。系统会为每个进程创建独立的文件描述符和文件表,不同进程是不会共用同一个文件表。正因为如此,不同进程可以同时用不同的状态操作同一个文件的不同位置。文件表中存的是 inode 信息而不是文件路径,所以文件路径发生改变不会影响文件操作。

create

这也就是默认的方案,可以通过 create 命令配置文件的权限和属组设置;这个方案的思路是重命名原日志文件,创建新的日志文件。详细步骤如下:

1、重命名正在输出日志文件,因为重命名只修改目录以及文件的名称,而进程操作文件使用的是 inode,所以并不影响原程序继续输出日志。
2、创建新的日志文件,文件名和原日志文件一样,注意,此时只是文件名称一样,而 inode 编号不同,原程序输出的日志还是往原日志文件输出。
3、最后通过某些方式通知程序,重新打开日志文件;由于重新打开日志文件会用到文件路径而非 inode 编号,所以打开的是新的日志文件。
如上也就是 logrotate 的默认操作方式,也就是 mv+create 执行完之后,通知应用重新在新文件写入即可。mv+create 成本都比较低,几乎是原子操作,如果应用支持重新打开日志文件,如 syslog, nginx, mysql 等,那么这是最好的方式。

不过,有些程序并不支持这种方式,压根没有提供重新打开日志的接口;而如果重启应用程序,必然会降低可用性,为此引入了如下方式。

copytruncate

该方案是把正在输出的日志拷 (copy) 一份出来,再清空 (trucate) 原来的日志;详细步骤如下:

1、将当前正在输出的日志文件复制为目标文件,此时程序仍然将日志输出到原来文件中,此时,原文件名也没有变。
2、清空日志文件,原程序仍然还是输出到预案日志文件中,因为清空文件只把文件的内容删除了,而 inode 并没改变,后续日志的输出仍然写入该文件中。
如上所述,对于 copytruncate 也就是先复制一份文件,然后清空原有文件。
通常来说,清空操作比较快,但是如果日志文件太大,那么复制就会比较耗时,从而可能导致部分日志丢失。不过这种方式不需要应用程序的支持即可。

运行logrotate

具体 logrotate 命令格式如下:

logrotate [OPTION...] <configfile>
-d, --debug :debug 模式,测试配置文件是否有错误。
-f, --force :强制转储文件。
-m, --mail=command :压缩日志后,发送日志到指定邮箱。
-s, --state=statefile :使用指定的状态文件。
-v, --verbose :显示转储过程。

通常惯用的做法是配合 crontab 来定时调用。

crontab -e
# 每30分钟执行一次logrotate
*/30 * * * * /usr/sbin/logrotate /etc/logrotate.d/rsyslog > /dev/null 2>&1 &
# 或者
*/30 * * * * /usr/sbin/logrotate /etc/logrotate.conf > /dev/null 2>&1 &

logrotate参数说明

参数说明
daily
weekly
monthly
yearly
切割周期,多久切割一次。
daily : 切割周期为 每天
weekly : 切割周期为 每周
monthly : 切割周期为 每月
yearly: 切割周期为 每年
size size当日志文件到达指定的大小时才转储。
默认单位是 bytes。bytes (缺省) 及 KB (sizek) 或 MB (sizem)
如: size 30k、 size 50M
rotate count日志文件保留备份的个数。默认是 0
如:0 指没有备份;5 指保留最近的5个备份,其余的全部删除。
maxage count保留多少天的日志文件。
如:5 指保留最近的5天的日志文件,其余的全部删除。
如果 日志文件按天转储,则 rotatemaxage 基本上是一样的。
tabooext [+] list让 logrotate 不转储指定扩展名的文件,缺省的扩展名是:.rpm-orig, .rpmsave, v, 和~
missingokmissing ok ,在日志轮循期间,错误将被忽略。
例如 “文件无法找到” 之类的错误。
copytruncate
nocopytruncate
copytruncate: 用于还在打开中的日志文件,把当前日志备份并截断。 把正在输出的日志拷 (copy) 一份出来,再清空 (trucate) 原来的日志。
nocopytruncate: 备份日志文件,但是不截断 。
create mode owner group
nocreate
create mode owner group:转储文件,使用指定的文件模式创建新的日志文件。
nocreate: 不建立新的日志文件。
如: create 644 root root
以指定的权限创建全新的日志文件,同时 logrotate 也会重命名原始日志文件。
nocompress
compress
nocompress: 不压缩(默认)
compress: 通过 gzip 压缩转储以后的日志。如 XXX.gz
delaycompress
nodelaycompress
delaycompress: 和 compress 一起使用时,转储的日志文件到下一次转储时才压缩。
nodelaycompress: 覆盖 delaycompress 选项,转储同时压缩。
prerotate
endscript
在所有其它指令完成 执行。这两个关键字必须单独成行
postrotate
endscript
在所有其它指令完成 执行。这两个关键字必须单独成行
sharedscripts共享脚本,整个日志组运行一次脚本 。
比如 nginx 涉及到多个日志文件,一般使用 * 号 通配符:/var/log/nginx/*.log { ... }
sharedscripts 的作用是在 所有的日志文件 都轮转完毕后 统一执行一次脚本
如果没有 sharedscripts ,那么 每个日志文件 轮转完毕后 都会执行一次脚本
errors address转储时的错误信息发送到指定的 Email 地址。
mail address
nomail
mail address : 把转储的日志文件发送到指定的 E-mail 地址
nomail : (默认)转储时不发送日志文件
ifempty
notifempty
ifempty :if empty ,即使是空文件也转储(默认)。
notifempty :not if empty ,如果是空文件的话,不转储。
olddir directory
noolddir
olddir directory :储后的日志文件放入指定的目录,必须和当前日志文件在同一个文件系统。
noolddir : 转储后的日志文件和当前日志文件放在同一个目录下。
dateext在日志名称后面,添加日期后缀。默认是当前日期。默认是 -%Y%m%d 的后缀。
可用 dateformat 选项扩展配置。
如:nginx_access.log --> nginx_access.log-20210120
注意: 格式化日期中不要出现 分号(:) ,因为文件名称是不能含有分号的。
dateformatdateext 的拓展,必须配合 dateext 使用
在V3.9.0 之前,只支持 %Y%m%d%s 参数。
%s 不是秒,是时间戳,也者 随机数,具体我没有验证。
在V3.9.0 及之后,支持 %H 参数。
如: dateformat -%Y-%m-%d
结果:nginx_access.log --> nginx_access.log-2021-01-20
注意: 文件名称中禁止出现分号(:) ,因为文件名称是不能含有分号的。
dateyesterdaydateext 配合使用,日期提前一天。
比如,当前日期是2021-01-20,提前一天就是 2021-01-19
extensiondateextdateformat 配合使用,指定文件的后缀。
如:extension .log
结果:nginx_access.log --> nginx_access-2021-01-20.log
说明:nginx_access.log , 文件名是nginx_access,后缀是.log, 文件名 + -%Y-%m-%d 格式的日期 + .log 后缀,结果是 nginx_access-2021-01-20.log

本文参考连接

https://blog.csdn.net/xiaojin21cen/article/details/122309230
https://zhuanlan.zhihu.com/p/90507023
https://zhuanlan.zhihu.com/p/444111643

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值