Syslog / archive - add maxfilesize option to enforce a log rotate when files exceed their limit.

The combination of preserve logs and max file size help to guard the boundaries of the log storage being used, an archive action is already being performed hourly, which should be enough in normal situations (although that would be easy to change if needed).

In order to make room for the new additional files per day, we add a sequence to the file, for example the first rotate of a filter log exceeding its limit named /var/log/filter/filter_20231204.log  would be moved to /var/log/filter/filter_20231204.0001.log . The syslog-ng reload handles the flush to a new file, which automatically would result in a new filter_20231204.log file after rotate.
This commit is contained in:
Ad Schellevis 2023-12-04 11:20:41 +01:00
parent 042f71db12
commit 528b7df875
2 changed files with 49 additions and 8 deletions

View File

@ -2,7 +2,7 @@
<?php
/*
* Copyright (C) 2020-2021 Deciso B.V.
* Copyright (C) 2020-2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -34,6 +34,7 @@ require_once("interfaces.inc");
require_once("plugins.inc");
$preserve_logs = !empty($config['syslog']['preservelogs']) ? $config['syslog']['preservelogs'] : 31;
$maxfilesize = !empty($config['syslog']['maxfilesize']) ? $config['syslog']['maxfilesize'] : null;
// gather all syslog created local logs (filename starts with directory name_[8 digits].log)
$relevant_logs = [];
@ -53,8 +54,25 @@ foreach(new RecursiveIteratorIterator($it) as $file) {
}
// remove expired logs and set latest.log symlink
$rotate = false;
foreach ($relevant_logs as $log_subject => $items) {
rsort($items);
/**
* Force a manual rotate when maxfilesize is specified and the newest file exceeds the specified limit.
*/
if (!empty($maxfilesize) && (filesize($items[0]) / 1024 / 1024) > $maxfilesize) {
if (isset($items[1]) && preg_match('/.*_(\d){8}\.log$/', $items[0])) {
/* Extract rotate sequence number from filename in format XXX_YYYYMMDD.log or XXX_YYYYMMDD.SEQ.log */
$fparts = explode('_', $items[1]);
$parts = explode('.', $fparts[1]);
$seq = (count($parts) == 3 && ctype_digit($parts[1]) ? (int)$parts[1] : 0) + 1;
/* Suffix sequence number to last filename, rename and schedule a rotate (syslog-ng reload) */
$new_filename = sprintf('%s.%04d.log', explode('.', $items[0])[0], $seq);
array_unshift($items, array_shift($items), $new_filename);
rename($items[0], $new_filename);
$rotate = true;
}
}
if (count($items) > $preserve_logs) {
foreach (array_slice($items, $preserve_logs) as $filename) {
@unlink($filename);
@ -68,3 +86,7 @@ foreach ($relevant_logs as $log_subject => $items) {
@symlink($items[0], $link);
}
}
if ($rotate) {
system_syslog_start();
}

View File

@ -60,6 +60,7 @@ function is_valid_syslog_server($target) {
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$pconfig = array();
$pconfig['preservelogs'] = !empty($config['syslog']['preservelogs']) ? $config['syslog']['preservelogs'] : null;
$pconfig['maxfilesize'] = !empty($config['syslog']['maxfilesize']) ? $config['syslog']['maxfilesize'] : null;
$pconfig['logdefaultblock'] = empty($config['syslog']['nologdefaultblock']);
$pconfig['logdefaultpass'] = empty($config['syslog']['nologdefaultpass']);
$pconfig['logbogons'] = empty($config['syslog']['nologbogons']);
@ -82,15 +83,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$input_errors[] = gettext("Preserve logs must be a positive integer value.");
}
}
if (!empty($pconfig['maxfilesize']) && (strlen($pconfig['maxfilesize']) > 0)) {
if (!is_numeric($pconfig['maxfilesize'])) {
$input_errors[] = gettext("Max file size must be a positive integer value.");
}
}
if (count($input_errors) == 0) {
if (empty($config['syslog'])) {
$config['syslog'] = array();
$config['syslog'] = [];
}
if (isset($_POST['preservelogs']) && (strlen($pconfig['preservelogs']) > 0)) {
$config['syslog']['preservelogs'] = (int)$pconfig['preservelogs'];
} elseif (isset($config['syslog']['preservelogs'])) {
unset($config['syslog']['preservelogs']);
foreach (['preservelogs', 'maxfilesize'] as $fieldname) {
if (isset($pconfig[$fieldname]) && (strlen($pconfig[$fieldname]) > 0)) {
$config['syslog'][$fieldname] = (int)$pconfig[$fieldname];
} elseif (isset($config['syslog'][$fieldname])) {
unset($config['syslog'][$fieldname]);
}
}
$config['syslog']['disablelocallogging'] = !empty($pconfig['disablelocallogging']);
@ -188,11 +198,20 @@ $(document).ready(function() {
</td>
</tr>
<tr>
<td><a id="help_for_preservelogs" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext('Preserve logs (Days)') ?></td>
<td><a id="help_for_preservelogs" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext('Preserve logs') ?></td>
<td>
<input name="preservelogs" id="preservelogs" type="text" value="<?=$pconfig['preservelogs'];?>" />
<div class="hidden" data-for="help_for_preservelogs">
<?=gettext("Number of log to preserve. By default 31 logs are preserved.");?>
<?=gettext("Number of logs to preserve. By default 31 logs are preserved. When no max filesize is offered or the logs are smaller than the the size requested, this equals the number of days");?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_maxfilesize" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext('Max log filesize (MB)') ?></td>
<td>
<input name="maxfilesize" id="maxfilesize" type="text" value="<?=$pconfig['maxfilesize'];?>" />
<div class="hidden" data-for="help_for_maxfilesize">
<?=gettext("Maximum filesize per log file, when set and a logfile exceeds the amount specified, it will be rotated.");?>
</div>
</td>
</tr>