公司的业务服务会打日志到磁盘, 程序已经做了日志切割, 时间一长磁盘占满会导致业务异常, 普通的清理逻辑是使用 crontab 定时删除文件, 比如每天 1 点删除创建日期超过 30 天的日志文件, 大部分情况下工作是 OK 的, 但是执行周期太长, 如果当天日志暴增, 不能及时删除文件. 也会有隐患, 优化版脚本如下:

日志清理分以下几种:

  1. 历史日志, 并且未在写入的日志, 这种可以直接删除掉.
  2. 当前正在写入的日志, 需要清理, 这种不能删除, 可以通过清空文件的方式来释放空间

set -euo pipefail 的作用:
set -e: 脚本中有任何一条命令执行失败, 整个脚本就失败
set -u: 脚本中的变量有任何一个变量为空, 整个脚本执行失败
-o pipefail: 脚本中的管道中任何一条命令执行失败, 也会认为脚本执行失败

脚本内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash
set -uo pipefail

# 磁盘的文件系统(df -h里面显示的)
FILESYSTEM="/dev/vda1"
# 业务日志目录
LOGS_FOLDER="/logs/"
# 这里的日志文件不做处理, 一般是需要保留的当天日志.
EXCLUDE_LOGS="a.log|b.log|c.log|d.log"
# 这里的日志只清空,不删除
TSF_LOGS=(
/var/log/stdout
/root/agent.log
)
# 清理日志记录
LOG=/root/clean.log
# 当前时间
NOW=$(date +'%Y-%m-%d %H:%M:%S')

USEAGE=$(df -h | grep -vE 'tmpfs|cdrom' | grep "${FILESYSTEM}" | awk '{print $(NF-1)}' | cut -d "%" -f1)

echo "${NOW} filesystem used ${USEAGE}%" >> ${LOG} 2>&1

if [ $USEAGE -ge 80 ]; then
for i in ${TSF_LOGS[*]};
do
echo "${NOW} clean $i" >> ${LOG} 2>&1
echo "" > $i
done
logfile=$(ls -t ${LOGS_FOLDER} | grep -Ev ${EXCLUDE_LOGS} | tail -1)
echo "${NOW} clean ${logfile}" >> ${LOG} 2>&1
rm -f ${LOGS_FOLDER}${logfile}
fi

磁盘使用率低于 80% 不会执行操作, 所以可以缩短 crontab 执行的间隔, 我一般设置成 5 分钟检测一次, crontab 内容:

1
*/5 * * * * /data/tools/clean.sh > /dev/null 2>&1

注意: crontab 最后需要保留一个空行