logrotate機制與原理

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

/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里加上一條指定時間執行這個腳本的記錄,自定義週期滾動日誌就大功告成了。這種自定義的方式有兩點要注意:
配置文件裏必定要配置rotate 文件數目這個參數。若是不配置默認是0個,也就是隻容許存在一份日誌,剛切分出來的日誌會立刻被刪除。
執行logrotate命令最好加-f參數,否則有時候配置文件修改的內容不生效。
不少程序的會用到logrotate滾動日誌,好比nginx。它們安裝後,會在/etc/logrotate.d這個目錄下增長本身的logrotate的配置文件。logrotate何時執行/etc/logrotate.d下的配置呢?看到/etc/logrotate.conf裏這行,一切就不言而喻了。nginx

include /etc/logrotate.d

原理
logrotate是怎麼作到滾動日誌時不影響程序正常的日誌輸出呢?logrotate提供了兩種解決方案。
Linux文件操做機制
介紹一下相關的Linux下的文件操做機制。
Linux文件系統裏文件和文件名的關係以下圖。編程

目錄也是文件,文件裏存着文件名和對應的inode編號。經過這個inode編號能夠查到文件的元數據和文件內容。文件的元數據有引用計數、操做權限、擁有者ID、建立時間、最後修改時間等等。文件件名並不在元數據裏而是在目錄文件中。所以文件更名、移動,都不會修改文件,而是修改目錄文件。
借《UNIX環境高級編程》裏的圖說一下進程打開文件的機制。centos

進程每新打開一個文件,系統會分配一個新的文件描述符給這個文件。文件描述符對應着一個文件表。表裏面存着文件的狀態信息(O_APPEND/O_CREAT/O_DIRECT…)、當前文件位置和文件的inode信息。系統會爲每一個進程建立獨立的文件描述符和文件表,不一樣進程是不會共用同一個文件表。正由於如此,不一樣進程能夠同時用不一樣的狀態操做同一個文件的不一樣位置。文件表中存的是inode信息而不是文件路徑,因此文件路徑發生改變不會影響文件操做。日誌

方案1:createcode

默認方案沒有名字,姑且叫它create吧。由於這個方案會建立一個新的日誌文件給程序輸出日誌,並且第二個方案名copytruncate是個配置項,與create配置項是互斥的。
這個方案的思路是重命名原日誌文件,建立新的日誌文件。詳細步驟以下:
重命名程序當前正在輸出日誌的程序。由於重命名只會修改目錄文件的內容,而進程操做文件靠的是inode編號,因此並不影響程序繼續輸出日誌。
建立新的日誌文件,文件名和原來日誌文件同樣。雖然新的日誌文件和原來日誌文件的名字同樣,可是inode編號不同,因此程序輸出的日誌仍是往原日誌文件輸出。
經過某些方式通知程序,從新打開日誌文件。程序從新打開日誌文件,靠的是文件路徑而不是inode編號,因此打開的是新的日誌文件。
什麼方式通知程序我從新打開日誌呢,簡單粗暴的方法是殺死進程從新打開。不少場景這種做法會影響在線的服務,因而有些程序提供了從新打開日誌的接口,好比能夠經過信號通知nginx。各類IPC方式均可以,前提是程序自身要支持這個功能。
有個地方值得一提,一個程序可能輸出了多個須要滾動的日誌文件。每滾動一個就通知程序從新打開全部日誌文件不太划得來。有個sharedscripts的參數,讓程序把全部日誌都重命名了之後,只通知一次。orm

方案2:copytruncate接口

若是程序不支持從新打開日誌的功能,又不能粗暴地重啓程序,怎麼滾動日誌呢?copytruncate的方案出場了。
這個方案的思路是把正在輸出的日誌拷(copy)一份出來,再清空(trucate)原來的日誌。詳細步驟以下:
拷貝程序當前正在輸出的日誌文件,保存文件名爲滾動結果文件名。這期間程序照常輸出日誌到原來的文件中,原來的文件名也沒有變。
清空程序正在輸出的日誌文件。清空後程序輸出的日誌仍是輸出到這個日誌文件中,由於清空文件只是把文件的內容刪除了,文件的inode編號並無發生變化,變化的是元信息中文件內容的信息。
結果上看,舊的日誌內容存在滾動的文件裏,新的日誌輸出到空的文件裏。實現了日誌的滾動。
這個方案有兩個有趣的地方。
文件清空並不影響到輸出日誌的程序的文件表裏的文件位置信息,由於各進程的文件表是獨立的。那麼文件清空後,程序輸出的日誌應該接着以前日誌的偏移位置輸出,這個位置以前會被\0填充纔對。但實際上logroate清空日誌文件後,程序輸出的日誌都是從文件開始處開始寫的。這是怎麼作到的?這個問題讓我糾結了好久,直到某天靈光一閃,這不是logrotate作的,而是成熟的寫日誌的方式,都是用O_APPEND的方式寫的。若是程序沒有用O_APPEND方式打開日誌文件,變會出現copytruncate後日志文件前面會被一堆\0填充的狀況。
日誌在拷貝完到清空文件這段時間內,程序輸出的日誌沒有備份就清空了,這些日誌不是丟了嗎?是的,copytruncate有丟失部分日誌內容的風險。因此能用create的方案就別用copytruncate。因此不少程序提供了通知我更新打開日誌文件的功能來支持create方案,或者本身作了日誌滾動,不依賴logrotate。進程

相關文章
相關標籤/搜索