前言
在Linux環境中能夠幫助我們分析問題蛛絲馬跡的有效辦法之一便是日志,常見的如操作系統syslog日志/var/log/messages
,應用程序Nginx日志/var/log/nginx/*.log
。但如果服務器數量較多,日志文件大小增長較快,不斷消耗磁盤空間就會觸發告警,如果需要人為定期按照各種維度去手動清理日志就顯得十分棘手。為了節省空間和方便整理,可以將日志文件按時間或大小分成多份,刪除時間久遠的日志文件,這就是通常說的日志滾動(log rotation)。logrotate(GitHub地址) 誕生於 1996/11/19 是一個Linux系統日志的管理工具,本文會詳細介紹Linux日志切割神器logroate的原理和配置
logrotate簡介
logrotate ‐ rotates, compresses, and mails system logs
logrotate is designed to ease administration of systems that generate large numbers of log files. It allows automatic rotation, compression, removal, and mailing of log files. Each log file may be handled daily, weekly, monthly, or when it grows too large.
Normally, logrotate is run as a daily cron job. It will not modify a log more than once in one day unless the criterion for that log is based on the log's size and logrotate is being run more than once each day, or unless the -f or --force option is used.
Any number of config files may be given on the command line. Later config files may override the options given in earlier files, so the order in which the logrotate config files are listed is important. Normally, a single config file which includes any other config files which are needed should be used. See below for more information on how to
use the include directive to accomplish this. If a directory is given on the command line, every file in that directory is used as a config file.
If no command line arguments are given, logrotate will print version and copyright information, along with a short usage summary. If any errors occur while rotating logs, logrotate will exit with non-zero status.
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 提供了兩種解決方案。
- create
- copytruncate
Linux 文件操作機制
介紹一下相關的 Linux 下的文件操作機制。
Linux 文件系統里文件和文件名的關系如下圖。
[圖片上傳失敗...(image-bf501c-1573030378572)]
目錄也是文件,文件里存着文件名和對應的 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
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。