1. 程式人生 > >logrotate日誌管理工具

logrotate日誌管理工具

日誌實在是太有用了,它記錄了程式執行時各種資訊。通過日誌可以分析使用者行為,記錄執行軌跡,查詢程式問題。可惜磁碟的空間是有限的,就像飛機裡的黑匣子,記錄的資訊再重要也只能記錄最後一段時間發生的事。為了節省空間和整理方便,日誌檔案經常需要按時間或大小等維度分成多份,刪除時間久遠的日誌檔案。這就是通常說的日誌滾動(log rotation)。

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

執行機制

logrotate在很多Linux發行版上都是預設安裝的。系統會定時執行logrotate,一般是每天一次。系統是這麼實現按天執行的。crontab會每天定時執行 /

etc/cron.daily目錄下的指令碼,而這個目錄下有個檔案叫 logrotate。在centos上指令碼內容是這樣的:

/etc/cron.daily/logrotate

123456 /usr/sbin/logrotate/etc/logrotate.conf>/dev/null2>&1EXITVALUE=$?if[$EXITVALUE!=0];then/usr/bin/logger-tlogrotate"ALERT exited abnormally with [$EXITVALUE]"fiexit0

可以看到這個指令碼主要做的事就是以 /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.下的配置呢?看到  /etc/logrotate.conf 裡這行,一切就不言而喻了。

1 include/etc/logrotate.d

相關原理

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

Linux檔案操作機制

介紹一下相關的Linux下的檔案操作機制。

Linux檔案系統裡檔案和檔名的關係如下圖。

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

借《UNIX環境高階程式設計》裡的圖說一下程序開啟檔案的機制。

程序每新開啟一個檔案,系統會分配一個新的檔案描述符給這個檔案。檔案描述符對應著一個檔案表。表裡面存著檔案的狀態資訊( O_APPEND/ O_CREAT/ O_DIRECT…)、當前檔案位置和檔案的inode資訊。系統會為每個程序建立獨立的檔案描述符和檔案表,不同程序是不會共用同一個檔案表。正因為如此,不同程序可以同時用不同的狀態操作同一個檔案的不同位置。檔案表中存的是inode資訊而不是檔案路徑,所以檔案路徑發生改變不會影響檔案操作。

方案1:create/rename

因為這個方案會建立一個新的日誌檔案給程式輸出日誌,而且第二個方案名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。

配置例項

12345678910111213141516171819202122232425262728 /var/log/messages{rotate5weeklypostrotate/sbin/killall-HUP syslogdendscript}"/var/log/httpd/access.log"/var/log/httpd/error.log{rotate5mail www@my.orgsize100ksharedscriptspostrotate/sbin/killall-HUP httpdendscript}/var/log/news/*{monthlyrotate2olddir/var/log/news/oldmissingokpostrotatekill-HUPcat/var/run/inn.pidendscriptnocompress}

配置選項說明

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:在轉儲以前需要執行的命令可以放入這個對,這兩個關鍵字必須單獨成行 postrotate/endscript:在轉儲以後需要執行的命令可以放入這個對,這兩個關鍵字必須單獨成行 sharedscripts:所有的日誌檔案都輪轉完畢後統一執行一次指令碼 daily:指定轉儲週期為每天 weekly:指定轉儲週期為每週 monthly:指定轉儲週期為每月 rotate count:指定日誌檔案刪除之前轉儲的次數,0 指沒有備份,5 指保留5 個備份 size size:當日志文件到達指定的大小時才轉儲,Size 可以指定 bytes (預設)以及KB (sizek)或者MB

命令引數說明

12345678 #logrotate--helpUsage:logrotate[OPTION...]-d,--debug除錯模式,輸出除錯結果,並不執行。隱式-v引數-f,--force強制模式,對所有相關檔案進行rotate-m,--mail=command傳送郵件(insteadof`/bin/mail\\')-s,--state=statefile狀態檔案,對於執行在不同使用者情況下有用-v,--verbose顯示debug資訊

可以使用 -d 引數來判斷所寫的logrotate配置檔案是否正確,但是需要注意的是,配置檔案的許可權必須為644並且擁有者必須為root使用者

此外,如果將logrotate用在crontab中執行,需要寫完整的路徑,如下:

1 *****root/usr/sbin/logrotate/home/john/Documents/k8s-log/config/logrotate.conf >/dev/null2>&1

參考連結: