Skip to content

fix(pflogsumm): Prevent incorrect stats when log retention is > 1 year - #4709

Merged
casperklein merged 2 commits into
docker-mailserver:masterfrom
piwai:pflogsumm-fix
Jun 3, 2026
Merged

fix(pflogsumm): Prevent incorrect stats when log retention is > 1 year#4709
casperklein merged 2 commits into
docker-mailserver:masterfrom
piwai:pflogsumm-fix

Conversation

@piwai

@piwai piwai commented May 25, 2026

Copy link
Copy Markdown
Contributor

Description

I have configured mail log retention to be several years instead of default. Recently, I started to notice unusually high daily volume in the report produced by pflogsumm. After investigation, it appears that the wrapper currently unextract all log files in log folder without considering their age.
So If the log retention for the mailserver is greater than one year, pflogsumm will process files from more 1 year ago, and since the log file only contains the day, it will consider these files as if they were from yesterday, producing incorrect reports.

With this modification, only log files < 180 days will be sent to pflogsumm for processing.

Fixes #

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (README.md or the documentation under docs/)
  • If necessary, I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have added information about changes made in this PR to CHANGELOG.md

Comment thread target/bin/report-pflogsumm-yesterday Outdated
@polarathene polarathene added this to the v16.0.0 milestone May 28, 2026
@polarathene polarathene added area/scripts kind/bug/fix A fix (PR) for a confirmed bug labels May 28, 2026
@piwai
piwai requested a review from casperklein June 2, 2026 20:25
@casperklein

Copy link
Copy Markdown
Member

[ ] I have added information about changes made in this PR to CHANGELOG.md

Please also update the changelog.

pflogsumm was processing all files from /var/log/mail, so
If the log retention for the mailserver is greater than one year,
it would process files from a year ago.

Since the log files only contains the day without the year,
it would consider logs from a year ago as if they were from yesterday,
and produce incorrect daily reports.

Fix this by limiting analyzed files to those less than 6 months.

Signed-off-by: Pierre-Yves Rofes <3604235+piwai@users.noreply.github.com>
@piwai

piwai commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

@casperklein done

@casperklein
casperklein merged commit 7290055 into docker-mailserver:master Jun 3, 2026
7 checks passed
@polarathene

Copy link
Copy Markdown
Member

Sorry I was bit too slow to chime in on review 😓

With this modification, only log files < 180 days will be sent to pflogsumm for processing.

Could I please have some context for the approx 6 month value? My findings from below suggest this task is only meant to send mail from a < 48 hours span of time?

I have configured mail log retention to be several years instead of default.

How did you go about that btw?

Did you set something like LOGROTATE_INTERVAL=monthly + LOGROTATE_COUNT=36?

I assume your config hasn't changed the default LOGWATCH_INTERVAL=none? It seems like logwatch would otherwise encounter a similar problem that this PR has not addressed?

Could you add context as to why you prefer retaining internal logs for that length of time? No external backups or collection through the host such as systemd-journald? (may need to change the Docker log driver for that)

UPDATE: Given that the bug was actually due to an old log timestamp format lacking an associated year for filtering by, was this something you missed when upgrading to DMS v14 or newer? Our changelog did point out logs would be formatted differently. When did you upgrade?


Overview of logging feature / integration in DMS

Looking over the following context:

I have this understanding:

This PR only made a change to the report-pflogsumm-yesterday script, which collects rotated logs to pipe into pflogsumm --problems_first -d yesterday? Why would the mtime filter be 180 days then?

The postfix-summary script only cares about the newly rotated log file (pflogsumm /var/log/mail/mail.log.1 --problems-first), so no concern there.

When we configure logwatch to use either daily/weekly intervals, these are apparently filtering by timestamps in the log files themselves (pflogsumm presumably does similar, both using Perl), with configs:

$ cat /etc/logwatch/conf/logfiles/maillog.conf

#
# Extensions for docker-mailserver /var/log/mail/. log path
#

# Mail log files
LogFile = mail/mail.log
LogFile = mail/mail.log.1

# Support Archives
Archive = mail/mail.log.*.gz
$ cat /etc/logwatch/conf/logfiles/clam-update.conf

LogFile = /var/log/mail/freshclam.log

Since logrotate is configured to compress logs, that Archive line means they'll all get parsed regardless, and then filtered by timestamp.


Startup scripts - Related logic that affects log files mtime

NOTE: We also have log fixes applied in this container startup function:

@polarathene

polarathene commented Jun 5, 2026

Copy link
Copy Markdown
Member

TL;DR: Actual source of failure identified (old DMS logs used a different timestamp format that omitted the year).

Change in timestamp format since DMS v14 (June 2024)

Also worth noting is in June 2024 with the DMS v14 release (upgrade to Debian 12), the changelog Rsyslog notes mention the timestamp format was changed by Debian:

  • From RSYSLOG_TraditionalFileFormat (similar to RFC 3164).
  • To RSYSLOG_FileFormat (similar to RFC 5424).
    • Timestamp format is standardized, uses RFC 3339 (a strict subset of ISO 8601).

I provided more insights here, but these two snippets below are worth pointing out.

EDIT:Another related PR

  • I provided a review comment noting there is another template with rsyslog RSYSLOG_SyslogRFC5424Format (beneficial if preferring that log format for forwarding/ingesting elsewhere).
  • That PR also contains my review comment detailing the change by Debian (since 2012) that was finally removed for Debian 12.

The logger command with actual RFC 3164 vs RFC 5424 format output:

# Old RFC 3164 format (lacks a year, assumes present year):
$ logger --rfc3164 --priority mail.error --id=1337 --tag dms-changedetector "This is an example error message!"
<19>May 26 11:45:27 mail dms-changedetector[1337]: This is an example error message!

# RFC 5424 format (notq omits the structured data regarding time, msgid is rfc5424 only):
$ logger --rfc5424=notq --priority mail.error --id=1337 --msgid=hello --tag dms-changedetector "This is an example error message!"
<19>1 2024-05-26T11:45:28.305480+00:00 mail.example.test dms-changedetector 1337 hello - This is an example error message!

While in DMS this is how the formats differ from the above two logger commands (indicated below as 1st line as RFC 3164 + 2nd line as RFC 5424) after being processed through rsyslog (which ingests logger syslog input, and then formats it slightly differently), comparing between DMS v13.3 vs DMS v14:

# DMS v13.3 (RSYSLOG_TraditionalFileFormat):
May 26 11:51:14 mail mail dms-changedetector[1337]: This is an example error message!
May 26 11:55:07 mail 1 2024-05-26T11:55:07.509942+00:00 mail.example.test dms-changedetector 1337 - - This is an example error message!

# DMS v14 (RSYSLOG_FileFormat):
2024-05-26T12:05:58.700336+00:00 mail mail dms-changedetector[1337]: This is an example error message!
2024-05-26T12:17:07.824094+00:00 mail 1 2024-05-26T12:17:07.824035+00:00 mail.example.test dms-changedetector 1337 hello - This is an example error message!

So rsyslog isn't outputting RFC 3164 or RFC 5424 syslog formats regardless, just ingesting either , displaing the message and differing by timestamp format. Notice the lack of year in logs for DMS v13.3 / RSYSLOG_TraditionalFileFormat? That's likely the culprit 😓


Reproduction - Verified assumptions

I've confirmed what might have been obvious by now, the RFC 3339 timestamps on logs are fine, it's the old DMS releases logs that were using RFC 3164 until Debian 12 / DMS 14 finally switched to using RFC 3339 timestamps.

verify-log-parsing.sh:

#!/bin/bash

# Prepend some timestamps for the log processors to filter by:
function _generate_mock_logs() {
  local LOG_MESSAGE="${1:?Expected a log message}"

  # RFC 3339 timestamp from yesterday, with offsets by year:
  RFC_3339_YMD0=$(date -d "yesterday" +"%Y-%m-%d")
  RFC_3339_YMD1=$(date -d "yesterday - 1 year" +"%Y-%m-%d")
  RFC_3339_YMD2=$(date -d "yesterday - 2 years" +"%Y-%m-%d")

  # RFC 3164 timestamps are like `Jan  1 12:34:56` (format lacks context of which year)
  RFC_3164_MD=$(date -d "yesterday - 2 years" +"%b %e")

  # RFC 3339 Format: <YYYY-MM-DD>T<HH:mm:ss>[.<fractional-seconds: 1-7 decimal places)>]<UTC offset: (Z|+HH:mm|-HH:mm)>
  RFC_3339_SUFFIX='00:00:00.100001+00:00'

  local LOG_LINES=(
    "${RFC_3339_YMD0}T${RFC_3339_SUFFIX} ${LOG_MESSAGE}" # 2026
    "${RFC_3339_YMD1}T${RFC_3339_SUFFIX} ${LOG_MESSAGE}" # 2025
    "${RFC_3339_YMD2}T${RFC_3339_SUFFIX} ${LOG_MESSAGE}" # 2024
    "${RFC_3164_MD} 00:00:00 ${LOG_MESSAGE}"             # 2026 (year could not be encoded)
  )

  # Output the array as a multi-line string
  printf "%s\n" "${LOG_LINES[@]}"
}

# Example log messages (neither message below was mutually reported across both commands, thus separate examples used):
JSON_DATA=$(printf '%s' '
[
  {
    "CMD": "pflogsumm",
    "LOG_MESSAGE": "mail postfix/master[762]: daemon started -- version 3.7.11, configuration /etc/postfix"
  },
  {
    "CMD": "logwatch",
    "LOG_MESSAGE": "mail postfix/postfix-script[761]: starting the Postfix mail system"
  }
]')

function _run_log_reporters() {
  local MAIL_LOG="${TMP_DIR}/mail.log"

  # Iterating over objects/structs in Bash is awkward..
  # JSON used with tab delimiter to split object properties into variable assignments.
  local CMD LOG_MESSAGE REPORT
  while IFS=$'\t' read -r CMD LOG_MESSAGE; do
    _generate_mock_logs "${LOG_MESSAGE}" > "${MAIL_LOG}"

    case "${CMD}" in
      # `pflogsumm` can accept log files as args or via stdin, otherwise hangs?:
      ('pflogsumm')
        REPORT=$(pflogsumm -d yesterday "${MAIL_LOG}" | grep -i -A2 master)
        ;;

      # `logwatch` can only process log files via resolved config files via `LogFile` setting.
      # - `--logfile` (a service group) or `--service` restricts what logs to process.
      # - `--logdir` can add additional logdirs (however `/var/log` + `/var/adm` are always implicit).
      # NOTE: To ignore `/var/log` as a logdir, ensure the generated logs and `--range` wouldn't overlap with `/var/log/mail.log`.
      ('logwatch')
        REPORT=$(TZ=UTC logwatch --service postfix --range yesterday --logdir "${TMP_DIR}" | grep -A5 'Postfix Begin')
       ;;
    esac

    echo -e "\n\nReport (filtered) from '${CMD}':"
    sed 's/^/\t/' <<< "${REPORT}"
  done < <(jaq -r '.[] | .CMD + "\t" + .LOG_MESSAGE' <<< "${JSON_DATA}")
}

TMP_DIR=$(mktemp -d)
_run_log_reporters
rm -rf "${TMP_DIR}"
# RFC 3164 log line (used prior to DMS v14) was counted:
$ bash verify-log-parsing.sh

Report (filtered) from 'pflogsumm':
        Master daemon messages
        ----------------------
              2   daemon started -- version 3.7.11, configuration /etc/postfix


Report (filtered) from 'logwatch':
         --------------------- Postfix Begin ------------------------

                2   Postfix start                                    2


         ---------------------- Postfix End -------------------------

@piwai

piwai commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Hey @polarathene, thanks for the detailled review! I will try to answer to everything.

With this modification, only log files < 180 days will be sent to pflogsumm for processing.

Could I please have some context for the approx 6 month value? My findings from below suggest this task is only meant to send mail from a < 48 hours span of time?

Actually you're right! 180 is definitely not necessary. Since the issue appeared after one year, I just reduced to half without thinking too much about it. My bad :-(

I have configured mail log retention to be several years instead of default.

How did you go about that btw?

I'm using a custom user-patches.sh script, with something like:

echo "Updating maillog to 10 years retention..."
sed -i -e 's/rotate 4/rotate 520/' /etc/logrotate.d/maillog

Did you set something like LOGROTATE_INTERVAL=monthly + LOGROTATE_COUNT=36?

Nope, I think this was introduced in a more recent version (I'm not on latest, see below).

I assume your config hasn't changed the default LOGWATCH_INTERVAL=none? It seems like logwatch would otherwise encounter a similar problem that this PR has not addressed?

Yes, I'm not using logwatch

Could you add context as to why you prefer retaining internal logs for that length of time? No external backups or collection through the host such as systemd-journald? (may need to change the Docker log driver for that)

Of course! I'm using DMS for my personal mail, so rather low traffic. Some time ago a friend of mine sent me an email, but I never received it. Some weeks later he told me about it, and I couldn't find anything in the logs since they had been rotated.
That's why I decided to increase log retention, to be able to investigate such cases (pretty rare, but I still have issues with some mail providers, despite having SPF+DKMS enabled)
Regarding external backups, it could be possible but I prefer to keep everything at the same place, to be able to launch a simple "grep".

UPDATE: Given that the bug was actually due to an old log timestamp format lacking an associated year for filtering by, was this something you missed when upgrading to DMS v14 or newer? Our changelog did point out logs would be formatted differently. When did you upgrade?

Actually I'm still using 12.1 😅 I remember I tried to upgrade some time ago and encountered some regressions, so I kept the working version. I will try to upgrade again to latest stable release

Overview of logging feature / integration in DMS

[...]

To conclude, this may not be necessary on newer versions, but for performance issue it still might be relevant, to avoid unzipping hundreds of log files.

@piwai

piwai commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

@polarathene I updated to restrict to 3 days only (2 + 1 to avoid edge cases)

@polarathene

Copy link
Copy Markdown
Member

@piwai it looks like you opened a PR to your own fork of DMS by accident? 😅

I have a suggestion below that might be a better approach (no mtime filtering needed).


You are correct about the LOGROTATE_COUNT ENV, that was also introduced in DMS v14.

Once you've upgraded you can drop the custom patch if you like 👍 (NOTE: LOGROTATE_COUNT affects log retention for other service logs too fail2ban and rspamd have logrotate configs configured by DMS with that ENV too)


To conclude, this may not be necessary on newer versions, but for performance issue it still might be relevant, to avoid unzipping hundreds of log files.

I agree that the previous logic wasn't ideal. Thanks for contributing to the project so we could resolve that ❤️

I don't know if we really need an mtime filter though? Since we're only concerned with processing logs for -d yesterday only two files matter? (mail.log + mail.log.1), the .gz archives will be older than that so never relevant.

I think this would be sufficient (No pipe with cat/gzip/find/sort -rV needed now):

# `pflogsumm` expects files in chronological order, while for `-d yesterday`
# the only relevant log files to process are `mail.log.1` (oldest) and `mail.log`:
BODY=$(/usr/sbin/pflogsumm --problems_first -d yesterday /var/log/mail/mail.log{.1,})

I included some inline commentary for context.

Additional note... I've noticed that logrotate can result in both of those files having the same mtime, so if we weren't able to rely on the filename convention for some reason, sorting by timestamps with ls -rt would not be reliable.


Response to other parts of your reply (collapsed as less relevant)

I have configured mail log retention to be several years instead of default.

How did you go about that btw?

I'm using a custom user-patches.sh script, with something like:

echo "Updating maillog to 10 years retention..."
sed -i -e 's/rotate 4/rotate 520/' /etc/logrotate.d/maillog

Did you set something like LOGROTATE_INTERVAL=monthly + LOGROTATE_COUNT=36?

Nope, I think this was introduced in a more recent version (I'm not on latest, see below).

Ah... yes it seems that LOGROTATE_COUNT was introduced in DMS v14 (June 2024):

- `LOGROTATE_COUNT` defines the number of files kept by logrotate ([#3907](https://github.com/docker-mailserver/docker-mailserver/pull/3907))


UPDATE: Given that the bug was actually due to an old log timestamp format lacking an associated year for filtering by, was this something you missed when upgrading to DMS v14 or newer? Our changelog did point out logs would be formatted differently. When did you upgrade?

Actually I'm still using 12.1 😅 I remember I tried to upgrade some time ago and encountered some regressions, so I kept the working version. I will try to upgrade again to latest stable release

Yeah, DMS v12.1 (May 2023) is quite old. You're going to want to comb through the changelog, at least the major version bumps to have a smoother upgrade experience.

You can try reach out about regressions you experience with an upgrade, but the older the version the less support that we can provide.

Could you add context as to why you prefer retaining internal logs for that length of time? No external backups or collection through the host such as systemd-journald? (may need to change the Docker log driver for that)

Of course! I'm using DMS for my personal mail, so rather low traffic.

Some time ago a friend of mine sent me an email, but I never received it. Some weeks later he told me about it, and I couldn't find anything in the logs since they had been rotated.

That's why I decided to increase log retention, to be able to investigate such cases (pretty rare, but I still have issues with some mail providers, despite having SPF+DKMS enabled)

Ah okay, that's fair :)

You perhaps would benefit from LOGROTATE_INTERVAL=monthly? And once you're on DMS v14 or newer, than you could use the LOGROTATE_COUNT=120 for 10 years.

Although for compatibility with your existing weekly logs, or perhaps that's a bad idea 😅 (you could also just archive all of that into a single file, given the log format will differ after upgrading, it may be better to have a clean separation?)

Regarding external backups, it could be possible but I prefer to keep everything at the same place, to be able to launch a simple "grep".

Sure, I can understand that :)

Some prefer the "same place" to be more centralized log collection, which the container logging drivers can be better for. There's benefits to doing such, but I totally get if you prefer retaining log history within the associated container, I'd still advise keeping a periodic backup of that data somewhere though just in case.

@polarathene

polarathene commented Jun 7, 2026

Copy link
Copy Markdown
Member

Below is just some notes addressing concerns I had raised earlier (like with logwatch). These are resolved now and can be ignored 😎

Mixed intervals

Summary: No issues to address. Can ignore.

DMS officially supports configuring logrotate intervals of daily, weekly (default), and monthly. Interaction with pflogsumm and logwatch reporters:

  • pflogsumm reports (PFLOGSUMM_TRIGGER with daily_cron / logrotate):
    • ✅: daily_cron paired with any LOGROTATE_INTERVAL. In all cases I think the only relevant log files are /var/log/mail/mail.log{.1,}? (that bash syntax {.1,} expands path stems to mail.log + mail.log.1)
    • ✅: logrotate trigger is always on /var/log/mail/mail.log.1.
  • logwatch reports (daily or weekly intervals).
    • ✅: When LOGWATCH_INTERVAL is matching LOGROTATE_INTERVAL (or this interval is higher), then similar to pflogsumm only two log files are relevant, archives won't matter.
    • ⚠️? (UPDATE: ✅): A LOGWATCH_INTERVAL=weekly + LOGROTATE_INTERVAL=daily combination would require setting at least LOGROTATE_COUNT=7 (thus archived logs will also be processed). If LOGROTATE_COUNT were notably higher, I'm not sure how we would limit the number of archives to process? (UPDATE: logwatch uses mtime itself on archives to filter, so not an issue).

logwatch internally filters archives by mtime (2 day buffer)

Summary: logwatch does not require a related change 😎

I have investigated source code of logwatch and verified that LogFile paths in maillog.conf config are checked regardless, but Archive entries are filtered by the requested --range and mtime check, with a 2 day buffer for timezones.

  • If logwatch were run with a --range 'between 7 days ago and yesterday' filter, while that does extend to 9 days, the buffer offset is in seconds (86_400 * 2), and AFAIK is from when the command is invoked, thus if the mtime of an archive is from earlier that day than the unix timestamp (even by a second), it will be excluded.
  • That buffer however should prevent any race condition concerns as a bonus (from separate cron tasks invoking logrotate and logwatch at roughly the same time). Both mail.log and mail.log.1 files get unconditionally processed, and then any subsequent mail.log.*.gz archives filtered conditionally based on the given range query (with approx 2 day buffer/padding) 👍

bocan pushed a commit to bocan/docker-mailserver that referenced this pull request Jun 18, 2026
docker-mailserver#4709)

pflogsumm was processing all files from /var/log/mail, so
If the log retention for the mailserver is greater than one year,
it would process files from a year ago.

Since the log files only contains the day without the year,
it would consider logs from a year ago as if they were from yesterday,
and produce incorrect daily reports.

Fix this by limiting analyzed files to those less than 6 months.

Signed-off-by: Pierre-Yves Rofes <3604235+piwai@users.noreply.github.com>
Co-authored-by: Casper <casperklein@users.noreply.github.com>
@piwai
piwai deleted the pflogsumm-fix branch June 21, 2026 16:58
@piwai

piwai commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

sorry for the delay. @polarathene I finally tested your suggestion to keep only mail.log{.1,} and it works as expected. I opened #4722
(and BTW I finally upgraded to DMS 15.1)

piwai added a commit to piwai/docker-mailserver that referenced this pull request Jul 4, 2026
This fixes commit d15b446 intronduced with PR docker-mailserver#4709 which added 6 months worth of log files when only 2 days are necessary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/scripts kind/bug/fix A fix (PR) for a confirmed bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants