Linux 日誌切割神器 logrotate 原理介紹和配置詳解
前言
在 Linux 環境中能夠幫助我們分析問題蛛絲馬跡的有效辦法之一便是日誌,常見的如操作系統 syslog 日誌/var/log/messages
,應用程序 Nginx 日誌/var/log/nginx/*.log
。但如果服務器數量較多,日誌文件大小增長較快,不斷消耗磁盤空間就會觸發告警,如果需要人爲定期按照各種維度去手動清理日誌就顯得十分棘手。爲了節省空間和方便整理,可以將日誌文件按時間或大小分成多份,刪除時間久遠的日誌文件,這就是通常說的日誌滾動 (log rotation)。logrotate(GitHub 地址) 誕生於 1996/11/19 是一個 Linux 系統日誌的管理工具,本文會詳細介紹 Linux 日誌切割神器 logrotate 的原理和配置。
logrotate 簡介
logrotate 是一個 linux 系統日誌的管理工具。可以對單個日誌文件或者某個目錄下的文件按時間 / 大小進行切割,壓縮操作;指定日誌保存數量;還可以在切割之後運行自定義命令。
logrotate 是基於 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。在 centos 上腳本內容是這樣的:
系統自帶 cron task:/etc/cron.daily/logrotate
,每天運行一次。
[root@gop-sg-192-168-56-103 logrotate.d]# 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/logrotate.conf
爲配置文件執行了 logrotate。就是這樣實現了每天執行一次 logrotate。
因爲我的系統執行 /etc/cron.daily
目錄下的腳本不是我想滾動日誌的時間,所以我把 /etc/cron.daily/logrotate
拷了出來,改了一下 logrotate 配置文件的路徑,然後在 crontab 里加上一條指定時間執行這個腳本的記錄,自定義週期滾動日誌就大功告成了。這種自定義的方式有兩點要注意:
- 配置文件裏一定要配置
rotate 文件數目
這個參數。如果不配置默認是 0 個,也就是隻允許存在一份日誌,剛切分出來的日誌會馬上被刪除。多麼痛的領悟,說多了都是淚。 - 執行 logrotate 命令最好加
-f
參數,不然有時候配置文件修改的內容不生效。
很多程序的會用到 logrotate 滾動日誌,比如 nginx。它們安裝後,會在 /etc/logrotate.d
這個目錄下增加自己的 logrotate 的配置文件。logrotate 什麼時候執行 /etc/logrotate.d
下的配置呢?看到 /etc/logrotate.conf
裏這行,一切就不言而喻了。
include /etc/logrotate.d
logrotate 原理
logrotate 是怎麼做到滾動日誌時不影響程序正常的日誌輸出呢?logrotate 提供了兩種解決方案。 1. create 2. copytruncate
Linux 文件操作機制
介紹一下相關的 Linux 下的文件操作機制。
Linux 文件系統裏文件和文件名的關係如下圖。
目錄也是文件,文件裏存着文件名和對應的 inode 編號。通過這個 inode 編號可以查到文件的元數據和文件內容。文件的元數據有引用計數、操作權限、擁有者 ID、創建時間、最後修改時間等等。文件件名並不在元數據裏而是在目錄文件中。因此文件改名、移動,都不會修改文件,而是修改目錄文件。
借《UNIX 環境高級編程》裏的圖說一下進程打開文件的機制。
進程每新打開一個文件,系統會分配一個新的文件描述符給這個文件。文件描述符對應着一個文件表。表裏面存着文件的狀態信息(O_APPEND
/O_CREAT
/O_DIRECT
...)、當前文件位置和文件的 inode 信息。系統會爲每個進程創建獨立的文件描述符和文件表,不同進程是不會共用同一個文件表。正因爲如此,不同進程可以同時用不同的狀態操作同一個文件的不同位置。文件表中存的是 inode 信息而不是文件路徑,所以文件路徑發生改變不會影響文件操作。
create
這也就是默認的方案,可以通過 create 命令配置文件的權限和屬組設置;這個方案的思路是重命名原日誌文件,創建新的日誌文件。詳細步驟如下:
- 重命名正在輸出日誌文件,因爲重命名只修改目錄以及文件的名稱,而進程操作文件使用的是 inode,所以並不影響原程序繼續輸出日誌。
- 創建新的日誌文件,文件名和原日誌文件一樣,注意,此時只是文件名稱一樣,而 inode 編號不同,原程序輸出的日誌還是往原日誌文件輸出。
- 最後通過某些方式通知程序,重新打開日誌文件;由於重新打開日誌文件會用到文件路徑而非 inode 編號,所以打開的是新的日誌文件。
如上也就是 logrotate 的默認操作方式,也就是 mv+create 執行完之後,通知應用重新在新文件寫入即可。mv+create 成本都比較低,幾乎是原子操作,如果應用支持重新打開日誌文件,如 syslog, nginx, mysql 等,那麼這是最好的方式。
不過,有些程序並不支持這種方式,壓根沒有提供重新打開日誌的接口;而如果重啓應用程序,必然會降低可用性,爲此引入瞭如下方式。
copytruncate
該方案是把正在輸出的日誌拷 (copy) 一份出來,再清空 (trucate) 原來的日誌;詳細步驟如下:
- 將當前正在輸出的日誌文件複製爲目標文件,此時程序仍然將日誌輸出到原來文件中,此時,原文件名也沒有變。
- 清空日誌文件,原程序仍然還是輸出到預案日誌文件中,因爲清空文件只把文件的內容刪除了,而 inode 並沒改變,後續日誌的輸出仍然寫入該文件中。
如上所述,對於 copytruncate 也就是先複製一份文件,然後清空原有文件。
通常來說,清空操作比較快,但是如果日誌文件太大,那麼複製就會比較耗時,從而可能導致部分日誌丟失。不過這種方式不需要應用程序的支持即可。
配置 logrotate
執行文件: /usr/sbin/logrotate
主配置文件: /etc/logrotate.conf
自定義配置文件: /etc/logrotate.d/*.conf
修改配置文件後,並不需要重啓服務。 由於 logrotate 實際上只是一個可執行文件,不是以 daemon 運行。
/etc/logrotate.conf
- 頂層主配置文件,通過 include 指令,會引入 /etc/logrotate.d
下的配置文件
[root@gop-sg-192-168-56-103 wangao]# cat /etc/logrotate.conf
# see "man logrotate" for details
# rotate log files weekly
weekly
# 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
# RPM packages drop log rotation information into this directory
include /etc/logrotate.d
# no packages own wtmp and btmp -- we'll rotate them here
/var/log/wtmp {
monthly
create 0664 root utmp
minsize 1M
rotate 1
}
/var/log/btmp {
missingok
monthly
create 0600 root utmp
rotate 1
}
# system-specific logs may be also be configured here.
/etc/logrotate.d/
通常一些第三方軟件包,會把自己私有的配置文件,也放到這個目錄下。 如 yum,zabbix-agent,syslog,nginx 等。
[root@gop-sg-192-168-56-103 logrotate.d]# cat yum
/var/log/yum.log {
missingok
notifempty
size 30k
yearly
create 0600 root root
}
運行 logrotate
具體 logrotate 命令格式如下:
logrotate [OPTION...] <configfile>
-d, --debug :debug 模式,測試配置文件是否有錯誤。
-f, --force :強制轉儲文件。
-m, --mail=command :壓縮日誌後,發送日誌到指定郵箱。
-s, --state=statefile :使用指定的狀態文件。
-v, --verbose :顯示轉儲過程。
crontab 定時
通常慣用的做法是配合 crontab 來定時調用。
crontab -e
*/30 * * * * /usr/sbin/logrotate /etc/logrotate.d/rsyslog > /dev/null 2>&1 &
手動運行
debug 模式:指定 [-d|--debug]
logrotate -d <configfile>
並不會真正進行 rotate 或者 compress 操作,但是會打印出整個執行的流程,和調用的腳本等詳細信息。
verbose 模式: 指定 [-v|--verbose]
logrotate -v <configfile>
會真正執行操作,打印出詳細信息(debug 模式,默認是開啓 verbose)
logrotate 參數
詳細介紹請自行 man logrotate
, 或者在線 man page。
主要介紹下完成常用需求會用到的一些參數。
一個典型的配置文件如下:
[root@localhost ~]# vim /etc/logrotate.d/log_file
/var/log/log_file {
monthly
rotate 5
compress
delaycompress
missingok
notifempty
create 644 root root
postrotate
/usr/bin/killall -HUP rsyslogd
endscript
}
- monthly: 日誌文件將按月輪循。其它可用值爲
daily
,weekly
或者yearly
。 - rotate 5: 一次將存儲 5 個歸檔日誌。對於第六個歸檔,時間最久的歸檔將被刪除。
- compress: 在輪循任務完成後,已輪循的歸檔將使用 gzip 進行壓縮。
- delaycompress: 總是與 compress 選項一起用,delaycompress 選項指示 logrotate 不要將最近的歸檔壓縮,壓縮 將在下一次輪循週期進行。這在你或任何軟件仍然需要讀取最新歸檔時很有用。
- missingok: 在日誌輪循期間,任何錯誤將被忽略,例如 “文件無法找到” 之類的錯誤。
- notifempty: 如果日誌文件爲空,輪循不會進行。
- create 644 root root: 以指定的權限創建全新的日誌文件,同時 logrotate 也會重命名原始日誌文件。
- postrotate/endscript: 在所有其它指令完成後,postrotate 和 endscript 裏面指定的命令將被執行。在這種情況下,rsyslogd 進程將立即再次讀取其配置並繼續運行。
上面的模板是通用的,而配置參數則根據你的需求進行調整,不是所有的參數都是必要的。
/var/log/log_file {
size=50M
rotate 5
dateext
create 644 root root
postrotate
/usr/bin/killall -HUP rsyslogd
endscript
}
在上面的配置文件中,我們只想要輪詢一個日誌文件,size=50M 指定日誌文件大小可以增長到 50MB,dateext 指 示讓舊日誌文件以創建日期命名。
常見配置參數
- daily :指定轉儲週期爲每天
- weekly :指定轉儲週期爲每週
- monthly :指定轉儲週期爲每月
- rotate count :指定日誌文件刪除之前轉儲的次數,0 指沒有備份,5 指保留 5 個備份
- tabooext [+] list:讓 logrotate 不轉儲指定擴展名的文件,缺省的擴展名是:.rpm-orig, .rpmsave, v, 和~
- missingok:在日誌輪循期間,任何錯誤將被忽略,例如 “文件無法找到” 之類的錯誤。
- size size:當日志文件到達指定的大小時才轉儲,bytes (缺省) 及 KB (sizek) 或 MB (sizem)
- compress: 通過 gzip 壓縮轉儲以後的日誌
- nocompress: 不壓縮
- copytruncate:用於還在打開中的日誌文件,把當前日誌備份並截斷
- nocopytruncate: 備份日誌文件但是不截斷
- create mode owner group : 轉儲文件,使用指定的文件模式創建新的日誌文件
- nocreate: 不建立新的日誌文件
- delaycompress: 和 compress 一起使用時,轉儲的日誌文件到下一次轉儲時才壓縮
- nodelaycompress: 覆蓋 delaycompress 選項,轉儲同時壓縮。
- errors address : 專儲時的錯誤信息發送到指定的 Email 地址
- ifempty :即使是空文件也轉儲,這個是 logrotate 的缺省選項。
- notifempty :如果是空文件的話,不轉儲
- mail address : 把轉儲的日誌文件發送到指定的 E-mail 地址
- nomail : 轉儲時不發送日誌文件
- olddir directory:儲後的日誌文件放入指定的目錄,必須和當前日誌文件在同一個文件系統
- noolddir: 轉儲後的日誌文件和當前日誌文件放在同一個目錄下
- prerotate/endscript: 在轉儲以前需要執行的命令可以放入這個對,這兩個關鍵字必須單獨成行
更多信息請參考 man logrotate 幫助文檔
手動運行 logrotate 演練
logrotate 可以在任何時候從命令行手動調用。 調用 /etc/lograte.d/ 下配置的所有日誌:
[root@localhost ~]# logrotate /etc/logrotate.conf
要爲某個特定的配置調用 logrotate:
[root@localhost ~]# logrotate /etc/logrotate.d/log_file
排障過程中的最佳選擇是使用-d
選項以預演方式運行 logrotate。要進行驗證,不用實際輪循任何日誌文件, 可以模擬演練日誌輪循並顯示其輸出。
[root@localhost ~]# logrotate -d /etc/logrotate.d/log_file
reading config file /etc/logrotate.d/log_file
reading config info for /var/log/log_file
Handling 1 logs
rotating pattern: /var/log/log_file monthly (5 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/log_file
log does not need rotating
not running postrotate script, since no logs were rotated
正如我們從上面的輸出結果可以看到的,logrotate 判斷該輪循是不必要的。如果文件的時間小於一天,這就會發生了。
強制輪循即使輪循條件沒有滿足,我們也可以通過使用-f
選項來強制 logrotate 輪循日誌文件,-v
參數提供了詳細的輸出。
[root@localhost ~]# logrotate -vf /etc/logrotate.d/log_file
reading config file /etc/logrotate.d/log_file
reading config info for /var/log/log_file
Handling 1 logs
rotating pattern: /var/log/log_file forced from command line (5 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/log_file
log needs rotating
rotating log /var/log/log_file, log->rotateCount is 5
dateext suffix '-20180503'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
previous log /var/log/log_file.1 does not exist
renaming /var/log/log_file.5.gz to /var/log/log_file.6.gz (rotatecount 5, logstart 1, i 5),
old log /var/log/log_file.5.gz does not exist
renaming /var/log/log_file.4.gz to /var/log/log_file.5.gz (rotatecount 5, logstart 1, i 4),
old log /var/log/log_file.4.gz does not exist
renaming /var/log/log_file.3.gz to /var/log/log_file.4.gz (rotatecount 5, logstart 1, i 3),
old log /var/log/log_file.3.gz does not exist
renaming /var/log/log_file.2.gz to /var/log/log_file.3.gz (rotatecount 5, logstart 1, i 2),
old log /var/log/log_file.2.gz does not exist
renaming /var/log/log_file.1.gz to /var/log/log_file.2.gz (rotatecount 5, logstart 1, i 1),
old log /var/log/log_file.1.gz does not exist
renaming /var/log/log_file.0.gz to /var/log/log_file.1.gz (rotatecount 5, logstart 1, i 0),
old log /var/log/log_file.0.gz does not exist
log /var/log/log_file.6.gz doesn't exist -- won't try to dispose of it
fscreate context set to unconfined_u:object_r:var_log_t:s0
renaming /var/log/log_file to /var/log/log_file.1
creating new /var/log/log_file mode = 0644 uid = 0 gid = 0
running postrotate script
set default create context
logrotate 配置文件實例
syslog
[root@gop-sg-192-168-56-103 logrotate.d]# cat syslog
/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
missingok
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}
zabbix-agent
[root@gop-sg-192-168-56-103 logrotate.d]# cat zabbix-agent
/var/log/zabbix/zabbix_agentd.log {
weekly
rotate 12
compress
delaycompress
missingok
notifempty
create 0664 zabbix zabbix
}
nginx
[root@gop-sg-192-168-56-103 logrotate.d]# cat nginx
/var/log/nginx/*.log /var/log/nginx/*/*.log{
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
[ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
endscript
}
influxdb
[root@gop-sg-192-168-56-103 logrotate.d]# cat influxdb
/var/log/influxdb/influxd.log {
daily
rotate 7
missingok
dateext
copytruncate
compress
}
關於 USR1 信號解釋
USR1 亦通常被用來告知應用程序重載配置文件;例如,向 Apache HTTP 服務器發送一個 USR1 信號將導致以下步驟的發生:停止接受新的連接,等待當前連接停止,重新載入配置文件,重新打開日誌文件,重啓服務器,從而實現相對平滑的不關機的更改。
對於 USR1 和 2 都可以用戶自定義的,在 POSIX 兼容的平臺上,SIGUSR1 和 SIGUSR2 是發送給一個進程的信號,它表示了用戶定義的情況。它們的符號常量在頭文件 signal.h 中定義。在不同的平臺上,信號的編號可能發生變化,因此需要使用符號名稱。
kill -HUP pid
killall -HUP pName
其中 pid 是進程標識,pName 是進程的名稱。
如果想要更改配置而不需停止並重新啓動服務,可以使用上面兩個命令。在對配置文件作必要的更改後,發出該命令以動態更新服務配置。根據約定,當你發送一個掛起信號 (信號 1 或 HUP) 時,大多數服務器進程 (所有常用的進程) 都會進行復位操作並重新加載它們的配置文件。
logrotate 日誌切割輪詢
由於 logrotate 是基於 cron 運行的,所以這個日誌輪轉的時間是由 cron 控制的,具體可以查詢 cron 的配置文件 /etc/anacrontab,過往的老版本的文件爲(/etc/crontab)
查看輪轉文件:/etc/anacrontab
[root@gop-sg-192-168-56-103 logrotate.d]# cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
使用 anacrontab 輪轉的配置文件,日誌切割的生效時間是在凌晨 3 點到 22 點之間,而且隨機延遲時間是 45 分鐘,但是這樣配置無法滿足我們在現實中的應用
現在的需求是將切割時間調整到每天的晚上 12 點,即每天切割的日誌是前一天的 0-24 點之間的內容,操作如下:
mv /etc/anacrontab /etc/anacrontab.bak //取消日誌自動輪轉的設置
使用 crontab 來作爲日誌輪轉的觸發容器來修改 logrotate 默認執行時間
[root@gop-sg-192-168-56-103 logrotate.d]# vim /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
# run-parts
01 * * * * root run-parts /etc/cron.hourly
59 23 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly
參考文章
How To Manage Logfiles with Logrotate on Ubuntu 16.04
How to Use logrotate to Manage Log Files
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://zhuanlan.zhihu.com/p/90507023