logrotate機制和原理

轉自:http://blog.lightxue.com/how-logrotate-works/


日誌實在是太有用了,它記錄了程序運行時各種信息。通過日誌可以分析用戶行爲,記錄運行軌跡,查找程序問題。可惜磁盤的空間是有限的,就像飛機裏的黑匣子,記錄的信息再重要也只能記錄最後一段時間發生的事。爲了節省空間和整理方便,日誌文件經常需要按時間或大小等維度分成多份,刪除時間久遠的日誌文件。這就是通常說的日誌滾動(log rotation)。

最近整理nginx日誌,用了一個類Unix系統上的古老工具——logrotate,發現意外的好用。想了解這個工具的用法推薦看這裏。我瞭解了一下這個工具的運行機制和原理,覺得挺有趣的。

運行機制

logrotate在很多Linux發行版上都是默認安裝的。系統會定時運行logrotate,一般是每天一次。系統是這麼實現按天執行的。crontab會每天定時執行/etc/cron.daily目錄下的腳本,而這個目錄下有個文件叫logrotate。在centos上腳本內容是這樣的:

# vi /etc/cron.daily/logrotate

/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1
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里加上一條指定時間執行這個腳本的記錄,自定義週期滾動日誌就大功告成了。這種自定義的方式有兩點要注意:

  1. 配置文件裏一定要配置rotate 文件數目這個參數。如果不配置默認是0個,也就是隻允許存在一份日誌,剛切分出來的日誌會馬上被刪除。多麼痛的領悟,說多了都是淚。

  2. 執行logrotate命令最好加-f參數,不然有時候配置文件修改的內容不生效。

很多程序的會用到logrotate滾動日誌,比如nginx。它們安裝後,會在/etc/logrotate.d這個目錄下增加自己的logrotate的配置文件。logrotate什麼時候執行/etc/logrotate.d下的配置呢?看到/etc/logrotate.conf裏這行,一切就不言而喻了。

include /etc/logrotate.d

原理

logrotate是怎麼做到滾動日誌時不影響程序正常的日誌輸出呢?logrotate提供了兩種解決方案。

Linux文件操作機制

介紹一下相關的Linux下的文件操作機制。

Linux文件系統裏文件和文件名的關係如下圖。


目錄也是文件,文件裏存着文件名和對應的inode編號。通過這個inode編號可以查到文件的元數據和文件內容。文件的元數據有引用計數、操作權限、擁有者ID、創建時間、最後修改時間等等。文件件名並不在元數據裏而是在目錄文件中。因此文件改名、移動,都不會修改文件,而是修改目錄文件。

借《UNIX環境高級編程》裏的圖說一下進程打開文件的機制。


方案1:create

默認方案沒有名字,姑且叫它create吧。因爲這個方案會創建一個新的日誌文件給程序輸出日誌,而且第二個方案名copytruncate是個配置項,與create配置項是互斥的。

這個方案的思路是重命名原日誌文件,創建新的日誌文件。詳細步驟如下:

  1. 重命名程序當前正在輸出日誌的程序。因爲重命名只會修改目錄文件的內容,而進程操作文件靠的是inode編號,所以並不影響程序繼續輸出日誌。

  2. 創建新的日誌文件,文件名和原來日誌文件一樣。雖然新的日誌文件和原來日誌文件的名字一樣,但是inode編號不一樣,所以程序輸出的日誌還是往原日誌文件輸出。

  3. 通過某些方式通知程序,重新打開日誌文件。程序重新打開日誌文件,靠的是文件路徑而不是inode編號,所以打開的是新的日誌文件。

什麼方式通知程序我重新打開日誌呢,簡單粗暴的方法是殺死進程重新打開。很多場景這種作法會影響在線的服務,於是有些程序提供了重新打開日誌的接口,比如可以通過信號通知nginx。各種IPC方式都可以,前提是程序自身要支持這個功能。

有個地方值得一提,一個程序可能輸出了多個需要滾動的日誌文件。每滾動一個就通知程序重新打開所有日誌文件不太划得來。有個sharedscripts的參數,讓程序把所有日誌都重命名了以後,只通知一次。

方案2:copytruncate

如果程序不支持重新打開日誌的功能,又不能粗暴地重啓程序,怎麼滾動日誌呢?copytruncate的方案出場了。

這個方案的思路是把正在輸出的日誌拷(copy)一份出來,再清空(trucate)原來的日誌。詳細步驟如下:

  1. 拷貝程序當前正在輸出的日誌文件,保存文件名爲滾動結果文件名。這期間程序照常輸出日誌到原來的文件中,原來的文件名也沒有變。

  2. 清空程序正在輸出的日誌文件。清空後程序輸出的日誌還是輸出到這個日誌文件中,因爲清空文件只是把文件的內容刪除了,文件的inode編號並沒有發生變化,變化的是元信息中文件內容的信息。

結果上看,舊的日誌內容存在滾動的文件裏,新的日誌輸出到空的文件裏。實現了日誌的滾動。

這個方案有兩個有趣的地方。

  1. 文件清空並不影響到輸出日誌的程序的文件表裏的文件位置信息,因爲各進程的文件表是獨立的。那麼文件清空後,程序輸出的日誌應該接着之前日誌的偏移位置輸出,這個位置之前會被\0填充纔對。但實際上logroate清空日誌文件後,程序輸出的日誌都是從文件開始處開始寫的。這是怎麼做到的?這個問題讓我糾結了很久,直到某天靈光一閃,這不是logrotate做的,而是成熟的寫日誌的方式,都是用O_APPEND的方式寫的。如果程序沒有用O_APPEND方式打開日誌文件,變會出現copytruncate後日志文件前面會被一堆\0填充的情況。

  2. 日誌在拷貝完到清空文件這段時間內,程序輸出的日誌沒有備份就清空了,這些日誌不是丟了嗎?是的,copytruncate有丟失部分日誌內容的風險。所以能用create的方案就別用copytruncate。所以很多程序提供了通知我更新打開日誌文件的功能來支持create方案,或者自己做了日誌滾動,不依賴logrotate。

總結

logrotate是個優秀的日誌滾動工具,它是用蜂蜜,川貝,桔梗,加上天山雪蓮配製而成,不須冷藏,也沒有防腐劑,除了毒性猛烈之外,味道還很好吃。實在是居家旅行、殺人滅口必備良藥!


發佈了1 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章