为 Fluentd 文件配置 Logrotate。有必要吗?

为 Fluentd 文件配置 Logrotate。有必要吗?

我有以下 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 可以自行处理?

答案1

Fluentd 的 out_file 插件会自动按天对输出文件进行分区,因此您不需要使用 logrotate。

如果要按不同的粒度进行分区,请更改“time_slice_format”参数(默认情况下为 %Y%m%d)。

但是,这意味着输出文件没有当前规范的名称。为此,您可以将参数“symlink_path”与“buffer_type file”一起使用。这不是 out_file 本身的功能,但任何缓冲输出

答案2

正如@kiyoto-tamura 所指出的,fluentd可以按天对输出文件进行分区,这是默认行为。

而且,正如@vaab 所指出的,fluentd无法删除旧文件。因此,显而易见的解决方案是禁用分区fluentd,并让其logrotate处理分区并监视文件数量。

然而,这可能会引入不必要的复杂性,特别是对于简单的情况:必须安装、配置和监控附加服务,即logrotate

此外,类似于logrotateWindows(除非有人愿意在生产中安装cygwin或使用0.0.0.x该版本)。

因此,另一个可行的解决方案是fluentd像往常一样按天对输出文件进行分区,但定期删除旧文件。

在类 UNIX 系统中,这很简单,只需编写一行 shell 脚本即可实现调用find并安排它通过cron

同样的逻辑也适用于 Windows 环境。例如,可以编写 PowerShell 脚本并通过系统任务计划程序 (参见要点为了便于阅读)。

下面的清单定义了这样的一个脚本作为示例:

# 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

实际逻辑发生在最后三行,其中创建并注册了一个动作、一个触发器和一个任务。总体而言,工作流程如下:

  1. 取参数delete-old-service-logs.ps1
  2. 采用脚本路径delete-old-service-logs.ps1
  3. 获取任务名称、描述和时间来触发脚本。
  4. 尝试解决并验证路径。
  5. 创建一个操作来调用 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"

答案3

不幸的是,如果out_file插件目前能够分割日志文件随着时间的推移(一件事logrotate也可以做到),它不删除 N 个旧文件logrotate实施)。

因此使用logrotate 似乎仍然有必要如果您需要控制保存日志的数量和大小(来源:https://github.com/fluent/fluentd/issues/2111)。

此时,您可以禁用与fluentd时间相关的文件分割功能,并确保使用append true让其完成全部工作。请注意,在的配置中logrotate不需要任何postrotate细节,因为每次刷新缓冲区时都会重新打开文件……这是使用 的一个受欢迎的优势。logrotatefluentdfluentd

fluentd对于其过滤器/日志流副本、按标签分割文件和缓冲来说仍然有用。

相关内容