在 CentOS Linux 上使用 Linux Shell 脚本自动化按天分割、压缩(延迟压缩)和定期清理日志文件

本文最后一次更新于:2019 年 10 月 25 日 14:37:41

最近 Ricky 这边有一个需求,需要对服务器上的日志进行相应地自动化处理:

  • 定期清理:当分区的已使用空间超过一个预先设定好的阈值时(如当分区的已使用空间超过 80 % 时)就自动清理日志,清理周期是一天一次;
  • 按天分割:有的日志文件如 access.log 会不断地增长,现在需要对其进行分割操作(假设今天的日期是 2019 年 1 月 25 日,当时间到达 2019 年 1 月 26 日 0 点 0 分时,自动分割出一个 access.log.20190125 的日志文件);
  • 压缩:对 access.log.20190125 这样的日志文件进行压缩,即生成压缩包 access.log.20190125.tar.gz 并删除日志文件 access.log.20190125 ;
  • 延迟压缩:有的开发可能需要查看最近几天的日志,为了方便开发查看日志,最近几天的日志文件可延迟压缩(假设今天的日期是 2019 年 1 月 25 日,根据开发的要求暂时不压缩最近一天的日志,则当时间到达 2019 年 1 月 26 日 0 点 0 分时,压缩的是 2019 年 1 月 24 日的日志文件 access.log.20190124 ,过了 24 小时以后再压缩 access.log.20190125 )。

部署:

1 、编写脚本,在命令行界面输入:

[root@host ~]# vi /root/log.sh

键入小写字母 i ,进入编辑模式,将 “ 附录 ” 中的 log.sh 复制粘贴进去。

按一次 ESC 键退出编辑模式,然后键入 “ :wq ” 保存并退出。

2 、创建并修改配置文件,在命令行界面输入:

[root@host ~]# vi /root/log.config

键入小写字母 i ,进入编辑模式,将 “ 附录 ” 中的 log.config 复制粘贴进去(请根据实际需要修改相应地配置)。

按一次 ESC 键退出编辑模式,然后键入 “ :wq ” 保存并退出。

3 、为上述脚本赋予可执行权限,并创建日志文件:

[root@host ~]# chmod +x /root/log.sh
[root@host ~]# touch /root/log.log

4 、让上述脚本每天凌晨 0 点 0 分自动运行一次,在命令行界面输入:

[root@host ~]# echo "0 0 * * * sh /root/log.sh" >> /var/spool/cron/root

至此,部署完成。

应用举例:

Ricky 将会通过应用举例的方式来说明这个脚本具体是如何使用的。

举例用的操作系统版本号如下所示:

[root@host ~]# cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core) 
[root@host ~]#
简单使用和定期清理日志:

1 、先创建一些日志文件用来模拟生产环境:

[root@host ~]# mkdir -p /www/log/applog/
[root@host ~]# mkdir -p /www/log/accesslog/
[root@host ~]# echo 123 > /www/log/applog/www.test.com.log
[root@host ~]# echo 123 > /www/log/applog/www.test.net.log
[root@host ~]# echo 123 > /www/log/accesslog/www.test.com.log
[root@host ~]# echo 123 > /www/log/accesslog/www.test.net.log.20190128.00
[root@host ~]# echo 123 > /www/log/accesslog/www.test.net.log.20190128.01
[root@host ~]# echo 123 > /www/log/accesslog/www.test.net.log.20190128.02

现在使用 tree 命令来看一下日志文件夹的目录结构:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 6 files
[root@host ~]#

2 、修改配置文件,具体配置如下所示:

[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz
[root@host ~]#

这里解释说明一下上述配置的作用:

  • mount :设置日志文件所在的挂载点,具体挂载点请用 df -h 命令查看:
    [root@host ~]# df -h
    Filesystem      Size  Used Avail Use% Mounted on
    devtmpfs        3.9G     0  3.9G   0% /dev
    tmpfs           3.9G     0  3.9G   0% /dev/shm
    tmpfs           3.9G  393M  3.6G  10% /run
    tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
    /dev/sda2        20G  4.5G   16G  23% /
    /dev/sda1       197M  174M   24M  89% /boot
    /dev/sda5       109G   87G   23G  80% /www
    tmpfs           798M     0  798M   0% /run/user/2032
    tmpfs           798M     0  798M   0% /run/user/0
    [root@host ~]#
  • mount_used_size_percent :设置该挂载点最多可以使用多少空间,单位是百分比;
  • log_dir :设置日志文件所在的文件夹,用于自动清理日志文件(从文件修改时间是最旧的日志文件开始删除,包括子文件夹下的日志文件),直到上述挂载点的可使用空间最多不超过 mount_used_size_percent 这个百分比为止(除非待删除的日志文件已经全部删除完毕);
  • log_format_regex :是一串正则表达式,用于自定义待删除的日志文件格式,防止误删除其他文件。

上述配置的举例、注意事项:

( 1 )mount 注意事项:

  • 因为 mount 仅允许配置一次,如果您在多个挂载点下都有日志需要删除,那怎么办呢?您可以做两份 log.config 配置,如 log.config 和 log_2.config,然后分别执行脚本:sh log.sh config_file=/root/log.config log_file=/root/log.log 和 sh log.sh config_file=/root/log_2.config log_file=/root/log_2.log 即可,具体示例如下所示。

( 2 )log_format_regex 举例和注意事项:

  • 如待删除的日志文件均为压缩格式,则这么配置即可(支持正则表达式,其中 ” \ ” 是转义字符,” | ” 是 ” 或 ” ,” $ ” 是匹配结尾):log_format_regex=(\.tar|\.gz|\.tar\.gz|\.bz2|\.tar\.bz2|\.bz|\.tar\.bz|\.Z|\.tar\.Z|\.tgz|\.tar\.tgz|\.zip|\.lha|\.rar)$;
  • 请不要把 .log 这个关键字写入进去,因为很多正在写入的日志文件都是以 xxx.log 命名的,这些文件是不能匹配到并删除的;
  • 请不要在符号 “ | ” 之前加转义字符 “ \ ”(因为该字符代表 ” 或 ” ,不能转义成普通字符),否则将无法正确匹配日志文件。

上述配置的取值范围:

  • mount :该值必须配置且仅允许配置一次
  • mount_used_size_percent :该值必须配置且仅允许配置一次,取值范围在 50 ≤ x ≤ 95 之间;
  • log_dir :该值必须配置且允许配置多次,如果文件夹不存在会将错误信息写入到日志文件 log.log 并终止运行
  • log_format_regex :该值必须配置且仅允许配置一次

3 、现在我们来看看这个脚本是如何运行的,如果直接运行会弹出如下提示:

[root@host ~]# sh log.sh 
Please run the script at 0 a.m.
[root@host ~]#

因为该脚本最主要的一个功能就是自动化按天分割日志文件,为了让该功能能够准确地运行,建议您在每天 0 点 0 分的时候才执行该脚本(或者说在每天 0 点 0 分的时候才执行 “ 按天分割日志 ” 的操作),所以 Ricky 这里限制了该脚本的运行时间(该脚本只允许在每天 0 点 0 分至 0 点 59 分之间运行)。

那如何调试脚本呢?您需要这么运行:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]#

即开启调试模式,强行让脚本执行起来(请不用在生产环境上调试,以免误删除重要文件)。

同时,log.config 配置文件和 log.log 该脚本的日志文件默认是放置在 /root/ 目录下的,如果您想自定义路径,可以这么运行脚本:

[root@host ~]# sh log.sh config_file=/ricky/log.config
config file ( /ricky/log.config ) not found !
[root@host ~]# 
[root@host ~]# sh log.sh log_file=/ricky/log.log
log file ( /ricky/log.log ) not found !
[root@host ~]# 
[root@host ~]# sh log.sh config_file=/ricky/log.config log_file=/ricky/log.log
config file ( /ricky/log.config ) not found !
[root@host ~]# 
[root@host ~]# sh log.sh log_file=/ricky/log.log config_file=/ricky/log.config
config file ( /ricky/log.config ) not found !
[root@host ~]# 
[root@host ~]# sh log.sh log_file=/ricky/log.log config_file=/ricky/log.config debug_mode=yes
config file ( /ricky/log.config ) not found !
[root@host ~]#

如上所示,脚本会自动判断 config_file 和 log_file 这两个文件是否存在;如果这两个文件有一个不存在,脚本都会终止运行。

4 、此时 /www/log/ 的目录结构和 log.config 配置文件如下所示:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 6 files
[root@host ~]# cat /root/log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz
[root@host ~]#

执行该脚本:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]#

脚本执行完毕后 /www/log/ 的目录结构和 log.log 日志文件如下所示:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 6 files
[root@host ~]# cat /root/log.log 
2019-10-24 17:13 - debug_mode is enabled ! Do NOT use in production environment !
2019-10-24 17:13 - === delete log ===
2019-10-24 17:13 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-10-24 17:13 - === cut log ===
2019-10-24 17:13 - === gz log ===
===========================
[root@host ~]#

此时 /www/log/ 目录结构无任何变化,因为我们在 log.config 配置文件里面配置了这两句:

log_dir=/www/log
log_format_regex=\.tar\.gz

所以脚本只会在 /www/log/ 目录(包括子目录)里删除文件名带有 tar.gz 字样的文件,而 /www/log 目录下并无带有 tar.gz 字样的文件。

此时 log.log 日志文件里还记录了一条日志:

2019-10-24 17:13 - Delete all the log files is completed , but did not reach the 50 % used precent !

该条日志的意思是说所有的日志文件已经删除完毕了(因为确实也不存在文件名带有 tar.gz 字样的文件了),但是挂载点 /www 的已使用空间依然超过了 50 % ,df -h 命令的执行结果如下所示:

[root@host ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G  369M  3.6G  10% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/sda2        10G  6.5G  3.5G  66% /
/dev/sda5        48G   30G   18G  63% /www
/dev/sda1       197M  107M   90M  55% /boot
[root@host ~]#

5 、现在我们多创建几个文件再测试一次:

[root@host ~]# echo 456 > /www/log/accesslog/www.test.net.log.tar
[root@host ~]# echo 456 > /www/log/accesslog/www.test.net.log.tar.gz
[root@host ~]# echo 456 > /www/log/applog/www.test.net.log.tar
[root@host ~]# echo 456 > /www/log/applog/www.test.net.log.tar.gz
[root@host ~]# echo 456 > /www/log/applog/www.test.net.tar.gz.log
[root@host ~]# 
[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   ├── www.test.net.log.20190128.02
│   ├── www.test.net.log.tar
│   └── www.test.net.log.tar.gz
└── applog
    ├── www.test.com.log
    ├── www.test.net.log
    ├── www.test.net.log.tar
    ├── www.test.net.log.tar.gz
    └── www.test.net.tar.gz.log

2 directories, 11 files
[root@host ~]#

执行该脚本:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]#

查看 /www/log/ 目录我们可以发现,凡是文件名带有 tar.gz 字样的文件都删除掉了:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   ├── www.test.net.log.20190128.02
│   └── www.test.net.log.tar
└── applog
    ├── www.test.com.log
    ├── www.test.net.log
    └── www.test.net.log.tar

2 directories, 8 files
[root@host ~]#

如下所示,log.log 日志文件中会记录下被删除掉的日志文件:

[root@host ~]# cat log.log
2019-10-24 17:18 - debug_mode is enabled ! Do NOT use in production environment !
2019-10-24 17:18 - === delete log ===
/www/log/accesslog/www.test.net.log.tar.gz
/www/log/applog/www.test.net.log.tar.gz
/www/log/applog/www.test.net.tar.gz.log
2019-10-24 17:18 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-10-24 17:18 - === cut log ===
2019-10-24 17:18 - === gz log ===
===========================
[root@host ~]#

6 、如果您只想删除以 .tar.gz 结尾的文件,只需要这么配置:

[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz$
[root@host ~]#

加一个正则表达式里的 $ 符号即可(即只匹配以 .tar.gz 结尾的文件),这样文件 www.test.net.tar.gz.log 就不会被删除了(亲测有效)。

7 、log_dir 允许配置多个值,如:

[root@host ~]# cat /root/log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log/accesslog
log_dir=/www/log/applog
log_dir=/tmp/applog
log_format_regex=\.tar\.gz
[root@host ~]#

脚本会预先判断这些文件夹是否存在,如果其中一个文件夹是不存在的,脚本会将错误信息写入到日志文件 log.log 并终止运行。具体报错信息如下所示:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]# 
[root@host ~]# 
[root@host ~]# cat log.log 
2019-01-29 15:17 - debug_mode is enabled ! Do NOT use in production environment !
2019-01-29 15:17 - log_dir /tmp/applog does not exist !
[root@host ~]#

8 、mount 仅允许配置一次,如果您在多个挂载点下都有日志需要删除,那怎么办呢?您可以做两份 log.config 配置,如 log.config 和 log_2.config,然后这么执行脚本即可:

[root@host ~]# sh log.sh config_file=/root/log.config log_file=/root/log.log
[root@host ~]# sh log.sh config_file=/root/log_2.config log_file=/root/log_2.log

crontab 这么配置即可:

[root@host ~]# echo "0 0 * * * sh /root/log.sh config_file=/root/log.config log_file=/root/log.log" >> /var/spool/cron/root
[root@host ~]# echo "0 0 * * * sh /root/log.sh config_file=/root/log_2.config log_file=/root/log_2.log" >> /var/spool/cron/root
按天分割日志:

1 、修改配置文件,具体配置如下所示:

[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

cut_log=/www/log/accesslog/www.test.com.log
[root@host ~]#

这里解释说明一下上述配置的作用:

  • cut_log :设置需要进行按天分割的日志文件的文件路径。

上述配置的举例:

( 1 )cut_log 举例:

  • 比如:cut_log=/www/accesslog/www.test.com/access.log ,假设今天的日期是 2019 年 1 月 29 日,那么到了 2019 年 1 月 30 日凌晨 0 点 0 分会分卷一份 access.log.20190129 出来。
  • 允许设置多条,如:
    cut_log=/www/accesslog/www.test.com/access.log
    cut_log=/www/accesslog/www.test.net/access.log
  • 也可以不设置,如:
    cut_log=

上述配置的取值范围:

  • cut_log :该值可不配置且允许配置多次,日志文件路径必须是绝对路径;如果文件不存在会将错误信息写入到日志文件 log.log 中,但脚本不会终止运行,而是会忽略掉这个不存在的文件,然后继续往下执行。

2 、此时 /www/log/ 的目录结构和 log.config 配置文件如下所示:

[root@host ~]# tree /www/log
/www/log
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 6 files
[root@host ~]# 
[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

cut_log=/www/log/accesslog/www.test.com.log
cut_log=/www/log/applog/www.test.com.log
[root@host ~]#

当前两个日志文件 /www/log/accesslog/www.test.com.log 和 /www/log/applog/www.test.com.log 的内容如下所示:

[root@host ~]# cat /www/log/accesslog/www.test.com.log
123
[root@host ~]# cat /www/log/applog/www.test.com.log
123
[root@host ~]#

执行完脚本以后会发现多出了两个日志文件 /www/log/accesslog/www.test.com.log.20190129 和 /www/log/applog/www.test.com.log.20190129(假设执行脚本的时间是 2019 年 1 月 30 日 0 点 0 分):

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]# 
[root@host ~]# 
[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.com.log.20190129
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    ├── www.test.com.log.20190129
    └── www.test.net.log

2 directories, 8 files
[root@host ~]#

这里需要注意的是,该脚本自动分割出来的日志文件是以 .20190129 这种格式结尾的,而不是 .2019-01-29 这种格式,更不是 .2019-1-29 这种格式。

同时原日志文件已经被清空:

[root@host ~]# cat /www/log/accesslog/www.test.com.log

[root@host ~]# cat /www/log/applog/www.test.com.log

[root@host ~]# cat /www/log/accesslog/www.test.com.log.20190129 
123
[root@host ~]# cat /www/log/applog/www.test.com.log.20190129 
123
[root@host ~]#

如下所示,log.log 日志文件中会记录下被分割的日志文件:

[root@host ~]# cat log.log
2019-01-30 00:00 - debug_mode is enabled ! Do NOT use in production environment !
2019-01-30 00:00 - === delete log ===
2019-01-30 00:00 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-01-30 00:00 - === cut log ===
/www/log/accesslog/www.test.com.log.20190129
/www/log/applog/www.test.com.log.20190129
2019-01-30 00:00 - === gz log ===
===========================
[root@host ~]#

3 、脚本会预先判断这些文件是否存在,如果其中一个文件是不存在的,脚本会将错误信息写入到日志文件 log.log 中,但脚本不会终止运行,而是会忽略掉这个不存在的文件,然后继续往下执行。此时 /www/log/ 的目录结构和 log.config 配置文件如下所示:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 6 files
[root@host ~]# 
[root@host ~]# 
[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

cut_log=/www/log/accesslog/www.test.com.log
cut_log=/www/log/applog/www.test.com.log123
[root@host ~]#

具体报错信息如下所示:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]# 
[root@host ~]# cat log.log 
2019-10-24 20:57 - debug_mode is enabled ! Do NOT use in production environment !
2019-10-24 20:57 - === delete log ===
2019-10-24 20:57 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-10-24 20:57 - === cut log ===
/www/log/accesslog/www.test.com.log.20191023
2019-10-24 20:57 - cut_log /www/log/applog/www.test.com.log123 does not exist ! The file is ignored and the program continues ...
2019-10-24 20:57 - === gz log ===
===========================
[root@host ~]#

日志文件 /www/log/accesslog/www.test.com.log 不会因为日志文件 /www/log/applog/www.test.com.log123 不存在而分卷失败:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.com.log.20191023
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 7 files
[root@host ~]#
[root@host ~]# cat /www/log/accesslog/www.test.com.log.20191023 
123
[root@host ~]# cat /www/log/accesslog/www.test.com.log

[root@host ~]#
压缩日志:

1 、修改配置文件,具体配置如下所示:

[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

gz_log=/www/log/applog/www.test.com.log
gz_delay_day=
[root@host ~]#

这里解释说明一下上述配置的作用:

  • gz_log :设置需要进行压缩的日志文件的文件路径(支持自定义日期格式和模糊匹配);
  • gz_delay_day :设置延迟压缩的天数。

上述配置的举例:

( 1 )gz_log 举例:

  • 支持自定义日期格式,其中:
    ” %YYYY ” 是年
    ” %MMMM ” 是月
    ” %DDDD ” 是日;
  • 支持模糊匹配:比如文件 /tmp/app.log.2019-01-30-00 和 /tmp/app.log.2019-01-30-01 这两个文件 ,只需要这么设置 gz_log=/tmp/app.log.%YYYY-%MMMM-%DDDD 即可自动将上述两个文件一同打入压缩包 /tmp/app.log.2019-01-30.tar.gz 。

( 2 )gz_delay_day 举例:

  • 支持延迟压缩日志文件:有的开发可能需要查看最近几天的日志,压缩后就不容易直接查看了;为了方便开发查看最近几天的日志,那么每天就不能压缩昨天的日志了,而是每天压缩前天或者大前天的日志,这样开发就还可以查看昨天或者前天的日志。其中:gz_delay_day=1 表示压缩昨天的日志,gz_delay_day=2 表示压缩前天的日志,gz_delay_day=3 表示压缩大前天的日志。

上述配置的取值范围:

  • gz_log :该值可不配置且允许配置多次,日志文件路径必须是绝对路径;如果文件不存在会将错误信息写入到日志文件 log.log 中,但脚本不会终止运行,而是会忽略掉这个不存在的文件,然后继续往下执行。
  • gz_delay_day :该值可不配置且仅允许配置一次,取值范围是 x ≥ 1 ,默认值是 1 。

2 、现在打算对日志文件 /www/log/applog/www.test.com.log 进行压缩,此时 /www/log/ 的目录结构和 log.config 配置文件如下所示:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log
    └── www.test.net.log

2 directories, 6 files
[root@host ~]# 
[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

gz_log=/www/log/applog/www.test.com.log
gz_delay_day=
[root@host ~]#

当前日志文件 /www/log/applog/www.test.com.log 的内容如下所示:

[root@host ~]# cat /www/log/applog/www.test.com.log
123
[root@host ~]#

执行完脚本以后会发现多出了一个压缩包 www.test.com.log.tar.gz ,同时原日志文件 www.test.com.log 已经被删除:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]# 
[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    ├── www.test.com.log.tar.gz
    └── www.test.net.log

2 directories, 6 files
[root@host ~]#

我们解压看看:

[root@host ~]# mkdir /tmp/log/
[root@host ~]# mv /www/log/applog/www.test.com.log.tar.gz /tmp/log/ 
[root@host ~]# cd /tmp/log/
[root@host log]# tar zxf www.test.com.log.tar.gz 
[root@host log]# ls
www.test.com.log  www.test.com.log.tar.gz
[root@host log]# 
[root@host log]# cat www.test.com.log
123
[root@host log]#

可以看到日志文件 www.test.com.log 就是原来那个。

如下所示,log.log 日志文件中会记录下被压缩的日志文件:

[root@host ~]# cat log.log
2019-10-25 00:33 - debug_mode is enabled ! Do NOT use in production environment !
2019-10-25 00:33 - === delete log ===
2019-10-25 00:33 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-10-25 00:33 - === cut log ===
2019-10-25 00:33 - === gz log ===
/www/log/applog :
www.test.com.log
===========================
[root@host ~]#

3 、我们现在来看看自定义日期格式和模糊匹配的使用,现在我们要压缩:

  • /www/log/accesslog/www.test.net.log.20190128.00
  • /www/log/accesslog/www.test.net.log.20190128.01
  • /www/log/accesslog/www.test.net.log.20190128.02

这三个日志文件,此时 /www/log/ 的目录结构和 log.config 配置文件如下所示:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.net.log.20190128.00
│   ├── www.test.net.log.20190128.01
│   └── www.test.net.log.20190128.02
└── applog
    └── www.test.net.log

2 directories, 5 files
[root@host ~]# 
[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

gz_log=/www/log/accesslog/www.test.net.log.%YYYY%MMMM%DDDD
gz_delay_day=3
[root@host ~]#

假设今天的日期是 2019 年 1 月 31 日,那么 30 日是昨天,29 日是前天,28 日是大前天,所以 gz_delay_day 的值为 3 。

执行完脚本以后会发现多出了一个压缩包 www.test.net.log.20190128.tar.gz ,同时三个日志文件已经被删除:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]# 
[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   └── www.test.net.log.20190128.tar.gz
└── applog
    └── www.test.net.log

2 directories, 3 files
[root@host ~]#

我们同样解压出来看看:

[root@host ~]# mkdir /tmp/log_20190128/
[root@host ~]# mv /www/log/accesslog/www.test.net.log.20190128.tar.gz /tmp/log_20190128/
[root@host ~]# cd /tmp/log_20190128/
[root@host log_20190128]# tar zxf www.test.net.log.20190128.tar.gz 
[root@host log_20190128]# ls
www.test.net.log.20190128.00  www.test.net.log.20190128.01  www.test.net.log.20190128.02  www.test.net.log.20190128.tar.gz
[root@host log_20190128]#

可以看到三个日志文件都在压缩包中。

如下所示,log.log 日志文件中会记录下被压缩的日志文件:

[root@host ~]# cat log.log
2019-01-31 00:00 - debug_mode is enabled ! Do NOT use in production environment !
2019-01-31 00:00 - === delete log ===
2019-01-31 00:00 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-01-31 00:00 - === cut log ===
2019-01-31 00:00 - === gz log ===
/www/log/accesslog :
www.test.net.log.20190128.00
www.test.net.log.20190128.01
www.test.net.log.20190128.02
===========================
[root@host ~]#

4 、其他自定义日期格式的例子,假设有的程序已经能够每小时自动生成一个日志文件,例如:

  • /www/log/accesslog/www.test.net.log.2019-01-30-00
  • /www/log/accesslog/www.test.net.log.2019-01-30-01
  • /www/log/accesslog/www.test.net.log.2019-01-30-02
  • ……
  • /www/log/accesslog/www.test.net.log.2019-01-30-23

那么只需要这么设置即可:

gz_log=/www/log/accesslog/www.test.net.log.%YYYY-%MMMM-%DDDD

这样,上述 24 个日志文件将会在 2019 年 1 月 31 日 0 点 0 分统一压缩进压缩包 www.test.net.log.2019-01-30.tar.gz ,然后再删除上述 24 个日志文件。

按天分割和压缩(延迟压缩)的功能是可以互相独立使用的;当然也可以结合起来使用,具体请看下方的 “ 综合应用 ” 。

综合应用:

现在有如下所示的四个日志文件:

[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   └── www.test.com.log.20190129
└── applog
    ├── www.test.net.log
    └── www.test.net.log.20190129

2 directories, 4 files
[root@host ~]#

现在的需求是:

( 1 )当 /www 的挂载点的可使用空间超过 50 % 时,自动删除 /www/log/ 目录下(包括子目录)文件名带有 tar.gz 字样的文件,以释放硬盘空间。

( 2 )假设今天的日期是 2019 年 1 月 30 日,当时间走到 2019 年 1 月 31 日 0 点 0 分时,需要对日志文件做一个分割:

  • /www/log/accesslog/www.test.com.log → /www/log/accesslog/www.test.com.log.20190130
  • /www/log/applog/www.test.net.log → /www/log/applog/www.test.net.log.20190130

( 3 )然后再对前天的日志文件进行一个压缩:

  • /www/log/accesslog/www.test.com.log.20190129 → /www/log/accesslog/www.test.com.log.20190129.tar.gz
  • /www/log/applog/www.test.net.log.20190129 → /www/log/applog/www.test.net.log.20190129.tar.gz

那么,log.config 配置文件只需要这么配置即可:

[root@host ~]# cat log.config
mount=/www
mount_used_size_percent=50

log_dir=/www/log
log_format_regex=\.tar\.gz

cut_log=/www/log/accesslog/www.test.com.log
cut_log=/www/log/applog/www.test.net.log

gz_log=/www/log/accesslog/www.test.com.log.%YYYY%MMMM%DDDD
gz_log=/www/log/applog/www.test.net.log.%YYYY%MMMM%DDDD
gz_delay_day=2
[root@host ~]#

执行脚本后,结果如下所示:

[root@host ~]# sh log.sh debug_mode=yes
debug_mode is enabled ! Do NOT use in production environment !
[root@host ~]# 
[root@host ~]# tree /www/log/
/www/log/
├── accesslog
│   ├── www.test.com.log
│   ├── www.test.com.log.20190129.tar.gz
│   └── www.test.com.log.20190130
└── applog
    ├── www.test.net.log
    ├── www.test.net.log.20190129.tar.gz
    └── www.test.net.log.20190130

2 directories, 6 files
[root@host ~]#

log.log 日志文件如下所示:

[root@host ~]# cat log.log
2019-01-31 00:00 - debug_mode is enabled ! Do NOT use in production environment !
2019-01-31 00:00 - === delete log ===
2019-01-31 00:00 - Delete all the log files is completed , but did not reach the 50 % used precent !
2019-01-31 00:00 - === cut log ===
/www/log/accesslog/www.test.com.log.20190130
/www/log/applog/www.test.net.log.20190130
2019-01-31 00:00 - === gz log ===
/www/log/accesslog :
www.test.com.log.20190129
/www/log/applog :
www.test.net.log.20190129
===========================
[root@host ~]#

至此,该脚本介绍完毕。如果您遇到了什么 Bug ,或者还有其他一些需求,欢迎在下方留言,Ricky 会尽力去完善 ~ 该脚本会不定期更新,敬请持续关注 ~

附录:

1 、log.sh :
#!/bin/bash

config_file="/root/log.config"
log_file="/root/log.log"
debug_mode="no"

for i in $* ; do
        if [[ "$i" =~ "config_file=" ]] ; then
                config_file=`echo $i | awk -F'=' '{print $2}'`
        elif [[ "$i" =~ "log_file=" ]] ; then
                log_file=`echo $i | awk -F'=' '{print $2}'`
        elif [[ "$i" =~ "debug_mode=" ]] ; then
                debug_mode=`echo $i | awk -F'=' '{print $2}'`
        fi
done

if [ ! -f "$config_file" ] ; then
        echo "config file ( $config_file ) not found !"
        exit
fi

if [ ! -f "$log_file" ] ; then
        echo "log file ( $log_file ) not found !"
        exit
fi

if [ "$debug_mode" == "no" -a "`date '+%H'`" != "00" ] ; then
       echo Please run the script at 0 a.m.
       exit
elif [ "$debug_mode" != "no" ] ; then
        debug_mode="yes"
        echo debug_mode is enabled ! Do NOT use in production environment !
        echo $(date "+%F %H:%M") - debug_mode is enabled ! Do NOT use in production environment ! >> $log_file
fi

# 1. get mount
config_file_flag=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep -c "mount="`
if [ $config_file_flag == 1 ] ; then
        mount=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "mount=" | awk -F'=' '{print $2}'`
elif [ $config_file_flag == 0 ] ; then
        echo $(date "+%F %H:%M") - "mount parameter is not found !" >> $log_file
        exit
else
        echo $(date "+%F %H:%M") - "mount parameter is too many !" >> $log_file
        exit
fi

if [ `df -h | grep -E "${mount}$" | awk -F' ' '{print $5}' | awk -F'%' '{print $1}' | grep -c '^[[:digit:]]*$'` == 0 ] ; then
        echo $(date "+%F %H:%M") - "mount parameter is error !" >> $log_file
        exit
fi

# 2. get mount_used_size_percent
config_file_flag=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep -c "mount_used_size_percent="`
if [ $config_file_flag == 1 ] ; then
        mount_used_size_percent=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "mount_used_size_percent=" | awk -F'=' '{print $2}'`
elif [ $config_file_flag == 0 ] ; then
        echo $(date "+%F %H:%M") - "mount_used_size_percent parameter is not found !" >> $log_file
        exit
else
        echo $(date "+%F %H:%M") - "mount_used_size_percent parameter is too many !" >> $log_file
        exit
fi

if [ `echo $mount_used_size_percent | grep -c '^[[:digit:]]*$'` == 0 ] ; then
        echo $(date "+%F %H:%M") - "mount_used_size_percent parameter is not number !" >> $log_file
        exit
fi

if [ $mount_used_size_percent -lt 50 ] ; then
        echo $(date "+%F %H:%M") - "mount_used_size_percent parameter requires more than or equal to 50 !" >> $log_file
        exit
elif [ $mount_used_size_percent -gt 95 ] ; then
        echo $(date "+%F %H:%M") - "mount_used_size_percent parameter requires less than or equal to 95 !" >> $log_file
        exit
fi

# 3. get log_format_regex
config_file_flag=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep -c "log_format_regex="`
if [ $config_file_flag == 1 ] ; then
        log_format_regex=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "log_format_regex=" | awk -F'=' '{print $2}'`
elif [ $config_file_flag == 0 ] ; then
        echo $(date "+%F %H:%M") - "log_format_regex parameter is not found !" >> $log_file
        exit
else
        echo $(date "+%F %H:%M") - "log_format_regex parameter is too many !" >> $log_file
        exit
fi

# 4. get log_dir_list
config_file_flag=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep -c "log_dir="`
if [ $config_file_flag -ge 1 ] ; then
        log_dir_list=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "log_dir=" | awk -F'=' '{print $2}'`
elif [ $config_file_flag == 0 ] ; then
        echo $(date "+%F %H:%M") - "log_dir parameter is not found !" >> $log_file
        exit
fi

tmp_log_dir=""
for log_dir in $log_dir_list ; do
        if [ -d "$log_dir" ] ; then
                if [ -z "$tmp_log_dir" ] ; then
                        tmp_log_dir=$log_dir
                else
                        tmp_log_dir=${tmp_log_dir}" "${log_dir}
                fi
        else
                echo $(date "+%F %H:%M") - "log_dir $log_dir does not exist !" >> $log_file
                exit
        fi
done

# 5. delete log file
echo $(date "+%F %H:%M") - "=== delete log ===" >> $log_file
while [ `df -h | grep -E "${mount}$" | awk -F' ' '{print $5}' | awk -F'%' '{print $1}'` -gt $mount_used_size_percent ] ; do
        if [ `find $tmp_log_dir -type f | grep -cE "$log_format_regex"` -gt 0 ] ; then
                tmp_delete_file=`find $tmp_log_dir -type f | grep -E "$log_format_regex" | xargs ls -t | tail -1`
                echo $tmp_delete_file >> $log_file
                rm -f "$tmp_delete_file"
        else
                echo $(date "+%F %H:%M") - "Delete all the log files is completed , but did not reach the $mount_used_size_percent % used precent !" >> $log_file
                break
        fi
done

# 6. get cut_log_list
cut_log_list=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "cut_log=" | awk -F'=' '{print $2}'`

# 7. cut log
echo $(date "+%F %H:%M") - "=== cut log ===" >> $log_file
yesterday=$(date -d "-1 day" +%Y%m%d)
for cut_log in $cut_log_list ; do
        if [ -f "$cut_log" ] ; then
                cp $cut_log $cut_log.$yesterday && echo > $cut_log
                echo $cut_log.$yesterday >> $log_file
        else
                echo $(date "+%F %H:%M") - "cut_log $cut_log does not exist ! The file is ignored and the program continues ..." >> $log_file
        fi
done 

# 8. get gz_log_list
config_file_flag=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep -c "gz_delay_day="`
if [ $config_file_flag == 0 -o $config_file_flag == 1 ] ; then

        if [ $config_file_flag == 1 ] ; then
                gz_delay_day=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "gz_delay_day=" | awk -F'=' '{print $2}'`
        elif [ $config_file_flag == 0 ] ; then
                gz_delay_day=1
        fi

        if [ `echo $gz_delay_day | grep -c '^[[:digit:]]*$'` == 0 ] ; then
                echo $(date "+%F %H:%M") - "gz_delay_day parameter is not number !" >> $log_file
                exit
        fi

        if [ $gz_delay_day -lt 1 ] ; then
                echo $(date "+%F %H:%M") - "gz_delay_day parameter requires more than or equal to 1 !" >> $log_file
                exit
        fi

        gz_log_list=`cat $config_file | grep -v "#" | sed "s# ##g" | grep -vE "=$" | grep "gz_log=" | awk -F'=' '{print $2}'`
        date_year=`date -d "- ${gz_delay_day} day" +%Y`
        date_month=`date -d "- ${gz_delay_day} day" +%m`
        date_day=`date -d "- ${gz_delay_day} day" +%d`

elif [ $config_file_flag -gt 1 ] ; then
        echo $(date "+%F %H:%M") - "gz_delay_day parameter is too many !" >> $log_file
        exit
fi

# 9. gz log
echo $(date "+%F %H:%M") - "=== gz log ===" >> $log_file
for gz_log in $gz_log_list ; do
        gz_log=`echo $gz_log | sed "s#%YYYY#$date_year#g" | sed "s#%MMMM#$date_month#g" | sed "s#%DDDD#$date_day#g"`
        if [ `ls "$gz_log"* 2>/dev/null | grep -v "${gz_log}.tar.gz" | wc -l` == 0 ] ; then
                echo $(date "+%F %H:%M") - "gz_log $gz_log does not exist ! The file is ignored and the program continues ..." >> $log_file
        else
                cd `dirname ${gz_log}`
                echo `dirname ${gz_log}`" :" >> $log_file
                log_filename=`basename ${gz_log}`
                tar zcvf ${log_filename}.tar.gz --exclude=*.tar.gz ${log_filename}* >> $log_file
                ls ${log_filename}* | grep -v "${log_filename}.tar.gz" | xargs rm -f
        fi
done
echo "===========================" >> $log_file
2 、log.config :
# mount:设置日志文件所在的挂载点,如:mount=/www ,具体挂载点请用 df -h 命令查看。
# mount 取值范围:该值必须配置且仅允许配置一次。
# mount_used_size_percent:设置该挂载点最多可以使用多少空间,单位是百分比。
# mount_used_size_percent 取值范围:该值必须配置且仅允许配置一次,取值范围在 50 ≤ x ≤ 95 之间。
mount=
mount_used_size_percent=

# log_dir:设置日志文件所在的文件夹,如:log_dir=/www/accesslog ,用于自动清理日志文件(从文件修改时间是最旧的日志文件开始删除,包括子文件夹下的日志文件),
# 直到上述挂载点的可使用空间最多不超过 mount_used_size_percent 这个百分比为止(除非待删除的日志文件已经全部删除完毕)。
# log_dir 取值范围:该值必须配置且允许配置多次,如果文件夹不存在会将错误信息写入到日志文件 log.log 并终止运行。
# 允许设置多条,如:
# log_dir=/www/accesslog
# log_dir=/www/applog
log_dir=

# log_format_regex:是一串正则表达式,用于自定义待删除的日志文件格式,防止误删除其他文件。
# log_format_regex 取值范围:该值必须配置且仅允许配置一次。
# 1 、如待删除的日志文件均为压缩格式,则这么配置即可(其中 “ \ ” 是转义字符,“ | ” 是 “ 或 ” ):
# log_format_regex=(\.tar|\.gz|\.tar\.gz|\.bz2|\.tar\.bz2|\.bz|\.tar\.bz|\.Z|\.tar\.Z|\.tgz|\.tar\.tgz|\.zip|\.lha|\.rar)$ 
# 2 、请不要把 .log 这个关键字写入进去,因为很多正在写入的日志文件都是以 xxx.log 命名的,这些文件是不能匹配到并删除的。
# 3 、请不要在符号 “ | ” 之前加转义字符 “ \ ” ,否则将无法正确匹配日志文件。
log_format_regex=

# cut_log:设置需要进行按天分割的日志文件的文件路径。
# cut_log 的取值范围:该值可不配置且允许配置多次,日志文件路径必须是绝对路径,如果文件不存在会将错误信息写入到日志文件 log.log 并终止运行。
# 1 、比如:cut_log=/www/accesslog/www.test.com/access.log ,假设今天的日期是 2019 年 1 月 29 日,那么到了 2019 年 1 月 30 日凌晨 0 点 0 分会分卷一份 access.log.20190129 出来。
# 2 、允许设置多条,如:
# cut_log=/www/accesslog/www.test.com/access.log
# cut_log=/www/accesslog/www.test.net/access.log
# 3 、也可以不设置,如:
# cut_log=
cut_log=

# gz_log:设置需要进行压缩的日志文件(支持自定义日期格式和模糊匹配)的文件路径。
# gz_log 的取值范围:该值可不配置且允许配置多次,日志文件路径必须是绝对路径,如果文件不存在会将错误信息写入到日志文件 log.log 并终止运行。
# 1 、支持自定义日期格式,其中:
# " %YYYY " 是年
# " %MMMM " 是月
# " %DDDD " 是日
# 2 、支持模糊匹配:
# 比如文件 /tmp/app.log.2019-01-30-00 和 /tmp/app.log.2019-01-30-01 这两个文件 ,
# 只需要这么设置 gz_log=/tmp/app.log.%YYYY-%MMMM-%DDDD 即可自动将上述两个文件一同打入压缩包 /tmp/app.log.2019-01-30.tar.gz 。
# gz_delay_day:设置延迟压缩的天数。
# gz_delay_day 的取值范围:该值可不配置且仅允许配置一次,取值范围是 x ≥ 1 ,默认值是 1 。
# 1 、支持延迟压缩日志文件:有的开发可能需要查看最近几天的日志,压缩后就不容易直接查看了;
# 为了方便开发查看最近几天的日志,那么每天就不能压缩昨天的日志了,而是每天压缩前天或者大前天的日志,这样开发就还可以查看昨天或者前天的日志。
# 比如:gz_delay_day=1 表示压缩昨天的日志,gz_delay_day=2 表示压缩前天的日志,gz_delay_day=3 表示压缩大前天的日志。
gz_log=
gz_delay_day=

 

这篇文章对你有帮助吗?

相关文章

发表评论?

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据