Назад | Перейти на главную страницу

Настройка Logrotate для файлов Fluentd. Нужно?

У меня есть следующий файл 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 справится с этим сам?

Плагин out_file от Fluentd автоматически разбивает выходные файлы по дням, поэтому вам НЕ нужно использовать logrotate.

Если вы хотите разделить по разной степени детализации, измените параметр time_slice_format (по умолчанию это% Y% m% d).

Однако это означает, что у выходного файла нет текущего канонического имени. Для этого вы можете использовать параметр "symlink_path" с "buffer_type file". Это не особенность out_file как таковая, но любой буферизованный вывод.

К сожалению, если out_file плагин является в настоящее время может разделить файлы журнала по времени (вещь logrotate может также), это не удаляет N старых файлов (который logrotate инвентарь).

Итак, используя logrotate кажется все еще необходимым если вам нужно контролировать количество и размер хранимых журналов (источник: https://github.com/fluent/fluentd/issues/2111 ).

На этом этапе вы можете отключить fluentd функция разделения файлов, связанная со временем, и обязательно используйте append true позволить logrotate делай это полноценно. Обратите внимание, что нет необходимости postrotate тонкости в logrotateконф, как fluentd повторно открывать файл при каждой очистке буфера ... и это долгожданный бонус использования fluentd.

fluentd останется полезным для фильтров / копирования потоков журнала, разделения файлов по тегам и буферизации.

Как указал @ kiyoto-tamura, fluentd может разбивать выходные файлы по дням, и это поведение по умолчанию.

И, как указывает @vaab, fluentd не может удалить старые файлы. Следовательно, очевидным решением было бы отключить разделение fluentd и разреши logrotate для обработки разбиения на разделы с отслеживанием количества файлов.

Однако это может создать ненужную сложность, особенно в простых случаях: нужно будет установить, настроить и контролировать дополнительную службу, которая logrotate.

Более того, получить аналог logrotate в Windows (если вам не нравится установка cygwin или используя 0.0.0.x версии в производстве).

Итак, другое возможное решение было бы позволить fluentd для разделения выходных файлов по дням как обычно, но периодически удаляйте старые файлы.

В UNIX-подобных системах это несложно и может быть достигнуто путем написания однострочного сценария оболочки. призывая 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"

Так как создание запланированной задачи в 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. Создайте действие для вызова 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"