Logrotate

為 Fluentd 文件配置 Logrotate。必要的?

  • August 24, 2020

我有以下 fluent.conf

<source>
 type forward
</source>

<source>
 type monitor_agent
 port 24220
</source>

# Listen DRb for debug
<source>
 type debug_agent
 port 24230
</source>


<source>
 type tail
 path /var/data/www/apps/app/logs/*.log
 pos_file /tmp/fluent.nginx.pos
 format syslog
 tag app.nginx-access
 # Regex fields
 format /^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)"$/
 # Date and time format
 time_format %d/%b/%Y:%H:%M:%S %z
</source>

<match app.**>
 type copy
 <store>
   type file
   path /var/log/fluent/app
 </store>
</match>

是否有必要使用 Logrotate @/var/log/fluent/app/*還是 Fluent 會自己處理?

Fluentd 的 out_file 外掛會自動按天對輸出文件進行分區,因此您不需要使用 logrotate。

如果要按不同粒度進行分區,請更改“time_slice_format”參數(預設為%Y%m%d)。

但是,這意味著輸出文件沒有目前的規範名稱。為此,您可以將參數“symlink_path”與“buffer_type file”一起使用。這不是 out_file 本身的特性,而是任何緩衝的輸出

正如@kiyoto-tamura 所指出的,fluentd可以按天對輸出文件進行分區,這是預設行為。

而且,正如@vaab 所指出的,fluentd不能刪除舊文件。因此,顯而易見的解決方案是禁用分區fluentd並讓logrotate處理分區以及監視文件的數量。

但是,這可能會帶來不必要的複雜性,尤其是對於簡單的情況:必須安裝、配置和監控一項附加服務,即logrotate.

此外,在 Windows 上進行類比logrotate可能會有問題(除非您對在生產環境中安裝cygwin或使用0.0.0.x版本感到滿意)。

因此,另一個可行的解決方案是讓fluentd輸出文件像往常一樣按天分區,但定期刪除舊文件。

在類 UNIX 系統中,這很容易實現,可以通過編寫單行 shell 腳本呼叫find通過cron.

相同的邏輯適用於 Windows 環境。例如,可以編寫一個 PowerShell 腳本並通過系統任務調度程序對其進行調度(請參閱 Gist以了解可讀性)。

以下清單定義了這樣一個腳本作為範例:

# delete-old-service-logs.ps1

[CmdletBinding()]

param(
 [Parameter(Position=0, Mandatory=$true)]
 [ValidateScript({
   if( -Not ($_ | Test-Path) ){
     throw "Specified logs dir path does not exist (path='$_')"
   }
   if(-Not ($_ | Test-Path -PathType Container) ){
     throw "Specified logs dir path does not point to a directory (path='$_')"
   }
   return $true
 })]
 [string]$LogsDirPath,

 [Parameter(Position=1)]
 [int]$LogsFileMaxN = 31,

 [Parameter(Position=2)]
 [ValidateNotNullOrEmpty()]
 [string]$LogsFileNamePattern = "service.????-??-??.log"
)


[string[]]$FileNamesToRemove = Get-ChildItem -Path $LogsDirPath -Filter $LogsFileNamePattern |
 Sort-Object -Property CreationTime -Descending |
 Select-Object -Skip $LogsFileMaxN |
 Select -ExpandProperty "Name"


$Shell = new-object -comobject "Shell.Application"
$LogsDir = $Shell.Namespace($LogsDirPath)


Foreach ($FileName in $FileNamesToRemove)
{
 $Item = $LogsDir.ParseName($FileName)
 $Item.InvokeVerb("delete")
}

邏輯非常簡單:

  1. 使用日誌獲取目錄的路徑。
  2. 獲取要保留的最大文件數。
  3. 採用文件模式來搜尋日誌。
  4. 查找日誌目錄中的所有文件,按創建日期反向排序並取舊文件的名稱。
  5. 刪除舊文件(如果有)。

呼叫範例:

./delete-old-service-logs.ps1 "path/to/logs/dir"

或者:

./delete-old-service-logs.ps1 -LogsDirPath "path/to/logs/dir"

由於通過 GUI 在 Windows 中創建計劃任務可能會很痛苦,因此可以使用一個腳本來自動執行此操作:

# create-task_delete-old-service-logs.ps1

[CmdletBinding()]


param(
 [Parameter(Position=0, Mandatory=$true)]
 [ValidateNotNullOrEmpty()]
 [string]$LogsDirPath,

 [Parameter(Position=1)]
 [int]$LogsFileMaxN = 31,

 [Parameter(Position=2)]
 [ValidateNotNullOrEmpty()]
 [string]$LogsFileNamePattern = "service.????-??-??.log",

 [Parameter(Position=3)]
 [ValidateNotNullOrEmpty()]
 [string]$TaskScriptFilePath = "delete-old-service-logs.ps1",

 [Parameter(Position=4)]
 [ValidateNotNullOrEmpty()]
 [string]$TaskName = "SERVICE NAME - Delete Old Logs",

 [Parameter(Position=5)]
 [ValidateNotNullOrEmpty()]
 [string]$TaskDescription = "Delete old logs of SERVICE NAME aggregated via Fluentd",

 [Parameter(Position=6)]
 [ValidateNotNullOrEmpty()]
 [string]$TaskTriggerTime = "5:00:00 AM"
)


try {
 $LogsDirPath = Resolve-Path -Path $LogsDirPath
}
catch {
 throw "Specified logs dir path does not exist (path='$LogsDirPath')"
}

if(-Not ($LogsDirPath | Test-Path -PathType Container) ){
 throw "Specified logs dir path does not point to a directory (path='$LogsDirPath')"
}


try {
 $TaskScriptFilePath = Resolve-Path -Path $TaskScriptFilePath
}
catch {
 throw "Specified task script file path does not exist (path='$TaskScriptFilePath')"
}

if( -Not ($TaskScriptFilePath | Test-Path -PathType Leaf) ){
 throw "Specified task script file path is not a file (path='$TaskScriptFilePath')"
}


$TaskAction = New-ScheduledTaskAction -Execute "C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" `
 -Argument "-NoProfile -WindowStyle Hidden -command ""$TaskScriptFilePath -LogsDirPath ""$LogsDirPath"" -LogsFileMaxN $LogsFileMaxN -LogsFileNamePattern ""$LogsFileNamePattern"""""


$TaskTrigger = New-ScheduledTaskTrigger -Daily -At $TaskTriggerTime


Register-ScheduledTask -TaskName $TaskName -Description $TaskDescription -Action $TaskAction -Trigger $TaskTrigger

實際的邏輯發生在最後 3 行,其中創建並註冊了一個動作、一個觸發器和一個任務。總的來說,工作流程如下:

  1. delete-old-service-logs.ps1.
  2. 採取delete-old-service-logs.ps1腳本路徑。
  3. 獲取任務名稱、描述和触發腳本的時間。
  4. 嘗試解析和驗證路徑。
  5. 使用 args 創建用於呼叫 PowerShell 的操作以執行delete-old-service-logs.ps1
  6. 創建每日觸發器。
  7. 註冊任務。

呼叫範例,假設兩個腳本位於同一目錄中:

./create-task_delete-old-service-logs.ps1 "path/to/logs/dir"

或者:

./create-task_delete-old-service-logs.ps1 -LogsDirPath "path/to/logs/dir" -TaskScriptFilePath "path/to/delete-old-service-logs.ps1"

引用自:https://serverfault.com/questions/620770