线上应用一般会产生大量日志文件,如 Java 应用不同级别的 log、Nginx 的 access.log 和 error.log,日积月累会极其占用磁盘空间,因此需要定期对 log 进行压缩、备份和删除,本文先分享我们的应用日志和 Nginx 日志的压缩策略和方式。
先了解两个概念:
- Bash Shell:Unix、Linux 系统的一种脚本文件,是大多数 Linux 发行版中的默认 Shell,以
.sh
结尾,使用 $ sh name.sh
命令执行。 .tar.gz
格式文件:.tar
为多个文件归档为一个文件的格式,会保留文件的元数据信息,.gz
为将单个文件使用 Gzip 压缩后的格式。通过 Tar + Gzip 压缩比 Zip 压缩效率高且节省空间更多,环境上也更为兼容。
为了更好的排查 log,我们将 Micro Service 架构下 Java 应用的 log 组织为了如下示例结构(应用中使用 Logback 工具):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| log
├── micro-service1
│ ├── 2022-06-01
│ │ ├── error.micro-service1-pod-id.log
│ │ └── info.micro-service1-pod-id.log
│ ├── 2022-06-02
│ │ ├── error.micro-service1-pod-id.log
│ │ └── info.micro-service1-pod-id.log
│ └── 2022-06-03
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
└── micro-service2
├── 2022-06-01
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
├── 2022-06-02
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
└── 2022-06-03
├── error.micro-service1-pod-id.log
└── info.micro-service1-pod-id.log
|
Bash Shell 也以此结构为基准进行编写:
1. 新建 Bash Shell 文件 java-log-compress.sh
,指定解释器路径,定义初始变量
1
2
3
4
5
6
| #!/bin/bash
# log 文件目录绝对路径
base_dir="/var/log"
# 当前日期
date="$(date "+%Y-%m-%d")"
|
2. 循环 Micro Service 文件列表
1
2
3
4
5
6
7
8
9
| # 进入 log 目录
cd $base_dir
# 将以 micro-service 开头的所有目录名存入变量
services=$(ls -f micro-service*/)
# 循环目录名 list
for service in $services
do
echo "service: $service"
done
|
使用变量时 {}
符号可选,与字符串拼接时必须加,否则会识别错误。
3. 循环 log 日期列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| for service in $services
do
echo "service: $service"
# 进入 log 目录
++ cd $base_dir
# 判断是否存在 micro service 目录
++ if [ -d $service ]; then
# 进入 micro service 目录
++ cd $service
# 将当前路径存入变量
++ service_path=$(pwd)
++ echo "start compress"
# 将所有日期目录名存入变量
++ date_dirs=$(ls -d *)
# 循环日期目录 list
++ for dir in $date_dirs
++ do
++ echo "date_dir: $dir"
++ done
++ fi
done
|
在 service 目录里重新 cd $base_dir
而不直接 cd $service
的目的是防止下次循环在该 service 目录直接去 cd
下一个 service 目录导致报错。
此时执行 sh java-log-compress.sh
的打印:
1
2
3
4
5
6
7
8
9
10
| service: micro-service1/
start compress
date_dir: 2022-06-01
date_dir: 2022-06-02
date_dir: 2022-06-03
service: micro-service2/
start compress
date_dir: 2022-06-01
date_dir: 2022-06-02
date_dir: 2022-06-03
|
4. 执行压缩
1
2
3
4
5
6
7
8
9
10
| for date_dir in $date_dirs
do
echo "date_dir: $date_dir"
# 判断该日期是否为今天
++ if [ $date_dir != $date ]; then
++ echo "run tar -zcvf ${date_dir}.tar.gz $date_dir"
# 将日期目录压缩为 .tar.gz 文佳
++ tar -zcvf ${date_dir}.tar.gz $date_dir
fi
done
|
此时执行 sh java-log-compress.sh
后的目录结构如下,日期不为今天的 log 目录(存量数据)都被压缩完成,可使用 tar -zxvf *.tar.gz
解压查看 log 内容是否正常。
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
| log
├── micro-service1
│ ├── 2022-06-01
│ │ ├── error.micro-service1-pod-id.log
│ │ └── info.micro-service1-pod-id.log
│ ├── 2022-06-01.tar.gz
│ ├── 2022-06-02
│ │ ├── error.micro-service1-pod-id.log
│ │ └── info.micro-service1-pod-id.log
│ ├── 2022-06-02.tar.gz
│ └── 2022-06-03
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
└── micro-service2
├── 2022-06-01
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
├── 2022-06-01.tar.gz
├── 2022-06-02
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
├── 2022-06-02.tar.gz
└── 2022-06-03
├── error.micro-service1-pod-id.log
└── info.micro-service1-pod-id.log
|
5. 删除已被压缩的 log 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| cd $service
service_path=$(pwd)
echo "start compress"
date_dirs=$(ls -d *)
for date_dir in $date_dirs
do
echo "date_dir: $date_dir"
if [ $date_dir != $date ]; then
echo "run tar -zcvf ${date_dir}.tar.gz $date_dir"
tar -zcvf ${date_dir}.tar.gz $date_dir
# 判断是否存在压缩完成的文件
++ if [ -f ${service_path}/${date_dir}.tar.gz ]; then
# 删除源文件夹
++ rm -rf ${service_path}/${date_dir}
++ fi
fi
done
|
执行 sh java-log-compress.sh
后的目录结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
| log
├── micro-service1
│ ├── 2022-06-01.tar.gz
│ ├── 2022-06-02.tar.gz
│ └── 2022-06-03
│ ├── error.micro-service1-pod-id.log
│ └── info.micro-service1-pod-id.log
└── micro-service2
├── 2022-06-01.tar.gz
├── 2022-06-02.tar.gz
└── 2022-06-03
├── error.micro-service1-pod-id.log
└── info.micro-service1-pod-id.log
|
完整的 Bash Shell:
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
| #!/bin/bash
base_dir="/var/log"
date="$(date "+%Y-%m-%d")"
cd $base_dir
services=$(ls -d micro-service*/)
for service in $services
do
echo "service: $service"
cd $base_dir
if [ -d $service ]; then
cd $service
service_path=$(pwd)
echo "start compress"
date_dirs=$(ls -d *)
for date_dir in $date_dirs
do
echo "date_dir: $date_dir"
if [ $date_dir != $date ]; then
echo "run tar -zcvf ${date_dir}.tar.gz $date_dir"
tar -zcvf ${date_dir}.tar.gz $date_dir
if [ -f ${service_path}/${date_dir}.tar.gz ]; then
rm -rf ${service_path}/${date_dir}
fi
fi
done
fi
done
|
Nginx 的 access.log 和 error.log 默认为单文件,为方便日常排查问题和节省磁盘空间同样推荐使用 Bash 脚本切割和压缩 log 文件。
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
| #!bin/bash
# 目标目录
logs_path="/usr/local/nginx/logs/history"
# 当前 log 目标
current_logs_path="/usr/local/nginx/logs"
# 昨天的日期
yesterday=$(date -d "yesterday" +%Y-%m-%d)
# 新建日期文件夹
mkdir ${logs_path}/${yesterday}
# 如果存在 log,则移动当前 log 至备份目录
if [ -f ${current_logs_path}/access.log ]; then
mv ${current_logs_path}/access.log ${logs_path}/${yesterday}/access.log
fi
if [ -f ${current_logs_path}/error.log ]; then
mv ${current_logs_path}/error.log ${logs_path}/${yesterday}/error.log
fi
# 执行压缩
cd $logs_path
tar -zcvf ${yesterday}.tar.gz ${yesterday}
# 删除源文件夹
if [ -f ${logs_path}/${yesterday}.tar.gz ]; then
rm -rf ${logs_path}/${yesterday}
fi
# 向 Nginx 主进程发送 USR1 信号以打开新的 log 文件
nginx_pid=$(cat /usr/local/nginx/logs/nginx.pid)
kill -USR1 $nginx_pid
|
Cron 是 Linux 中基于时间的任务管理工具,也就是定时任务。使用以下命令可新增/编辑 Cron job:
新增以下两个定时任务(需确保两个 Bash shell 文件具有可执行权限,若无权限可使用 chmod +x filename
命令添加):
1
2
3
4
| # 凌晨3点执行压缩 Java 应用 log 脚本
0 3 * * * /usr/local/bash-shell/java-log-compress.sh >/dev/null 2>&1
# 0点执行 Nginx log 切分、压缩
0 0 * * * /usr/local/bash-shell/nginx-log-compress.sh >/dev/null 2>&1
|
>/dev/null 2>&1
表示将 Cron job 错误级别的输出(2>
)等同于(&
)标准级别的输出(1
),都丢到黑洞里(/dev/null
),表示不记录输出,想要记录输出内容可设定为 >/var/log/cron.log 2>&1
。
添加成功后查看已有的 Cron job:
- Bash 脚本教程 | WangDoc.com
- Difference Between Zip and Gzip (With Table) | AskAnyDifference.com