我有以下 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
。
此外,类似于logrotate
Windows(除非有人愿意在生产中安装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")
}
逻辑非常简单:
- 采用包含日志的目录路径。
- 保留最大文件数。
- 采用文件模式来搜索日志。
- 查找日志目录中的所有文件,按创建日期反向排序并获取旧文件的名称。
- 如果有的话,删除旧文件。
调用示例:
./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
实际逻辑发生在最后三行,其中创建并注册了一个动作、一个触发器和一个任务。总体而言,工作流程如下:
- 取参数
delete-old-service-logs.ps1
。 - 采用脚本路径
delete-old-service-logs.ps1
。 - 获取任务名称、描述和时间来触发脚本。
- 尝试解决并验证路径。
- 创建一个操作来调用 PowerShell,并使用要运行的参数
delete-old-service-logs.ps1
。 - 创建每日触发器。
- 注册任务。
调用示例,假设两个脚本都在同一个目录中:
./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
细节,因为每次刷新缓冲区时都会重新打开文件……这是使用 的一个受欢迎的优势。logrotate
fluentd
fluentd
fluentd
对于其过滤器/日志流副本、按标签分割文件和缓冲来说仍然有用。