Logs
The milter and settings services write all their log files to var/log inside the installation directory, e.g. /opt/msh-pm-milter/var/log. All files are owned by the service user (msh by default). A typical listing looks like this:
-rw-r--r-- 1 msh msh 1535 Jul 15 05:32 20260715053216038+info+7+accept+UGCCALUDUQ1C,E6516436222A
-rw-r--r-- 1 msh msh 6773 Jul 15 05:35 20260715053550770+erro+97+accept+ULUEDVSHWWBW,66E764364A09
-rw-r----- 1 msh msh 0 Apr 11 2025 .empty
-rw-r--r-- 1 msh msh 94 Jul 15 04:36 milter.out
-rw-r--r-- 1 msh msh 160 Jul 15 05:35 milter.stats
-rw-r--r-- 1 msh msh 4492 Jul 15 05:35 msh-pm-milter.log
-rw-r--r-- 1 msh msh 16106 Jul 15 05:56 msh-pm-settings.log
-rw-r--r-- 1 msh msh 94 Jul 15 04:58 settings.out
Launcher console output: milter.out and settings.out
milter.out (milter service) and settings.out (settings service) capture everything the process prints to standard output and standard error. The file is overwritten each time the service starts, so it only ever contains the output of the current (or most recent) run.
Look here for problems that occur before the application logging is up: launcher errors (missing or too old Java), JVM startup failures, and early messages such as Cannot read settings! when the milter cannot reach the settings service.
Application logs: msh-pm-milter.log and msh-pm-settings.log
These are the main application logs, written at info level with the format:
HH:mm:ss.SSS [thread] LEVEL logger - message
msh-pm-milter.log- the milter service: startup, connections from Postfix, message processing errors.msh-pm-settings.log- the settings service: RMI requests from the Administrator Panel, configuration changes, and the directory service synchronization trace (see Directory service).
Both files roll over daily. Rotated files are named msh-pm-milter.YYYY-MM-DD.log / msh-pm-settings.YYYY-MM-DD.log, the last 7 days are kept, and the total size of the rotated history is capped at 10 MB.
Worker pool statistics: milter.stats
A small snapshot of the milter's worker thread pool, rewritten at most once per stats update interval (STATS_UPDATE_TIME, default 60 seconds). It always contains the current values, one per line:
ActiveCount: 0
PoolSize: 10
CorePoolSize: 10
LargestPoolSize: 10
MaximumPoolSize: 10
TaskCount: 42
CompletedTaskCount: 42
CurrentQueueSize: 0
MaximumQueueSize: 20
ActiveCount- threads currently processing a message.PoolSize/CorePoolSize/MaximumPoolSize/LargestPoolSize- current, configured minimum, configured maximum and historical peak thread counts (POOL_SIZEin the configuration).TaskCount/CompletedTaskCount- messages submitted and finished since the service started.CurrentQueueSize/MaximumQueueSize- messages waiting for a free thread and the configured queue capacity (QUEUE_SIZE).
A CurrentQueueSize that keeps growing toward MaximumQueueSize means the milter cannot keep up with the mail volume - consider increasing the pool size.
Message trace files
When diagnostic logging is enabled, the milter writes one trace file per processed message. These files are what the Events History panel in the Administrator Panel reads. The file name encodes the outcome, with fields separated by +:
20260715053550770+erro+97+accept+ULUEDVSHWWBW,66E764364A09
| | | | | |
timestamp type | action MSH ID Postfix queue ID
processing time (ms)
- timestamp - when the trace was written, format
yyyyMMddHHmmssSSS. - type - the most severe entry in the trace:
info(clean run),warn(at least one warning),erro(at least one exception). To find failed messages, list*+erro+*files. - processing time - total milter processing time for the message, in milliseconds.
- action - the final answer returned to Postfix, e.g.
accept,continue,reject. - MSH ID, queue ID - the application's message ID (also added to the message as the
X-MSH-Idheader) and the Postfix queue ID, so a trace can be correlated with/var/log/mail.logon the Postfix side.
Inside, the file contains the full processing report: message metadata, envelope sender and recipients, all headers, the attachment count, the step-by-step rule evaluation trace with timestamps, and the final status.
Which messages get a trace file is controlled by the Diagnostic Logging panel: with Store error logs regardless of sender enabled only error traces are kept, and the sender/subject filters restrict tracing to specific test messages.
Message body dumps: *-before.eml and *-after.eml
When Persist message body is enabled in the Diagnostic Logging panel (and the message matches the configured sender/subject filters), the milter also stores the complete MIME message twice:
<MSH-ID>-before.eml- the message exactly as it arrived, before any rules were applied.<MSH-ID>-after.eml- the message after processing, as it was handed back to Postfix.
The <MSH-ID> matches the ID in the corresponding trace file name, and the trace's processing section logs the path of each dump. Comparing the two files shows precisely what the rules changed. The Administrator Panel displays them in the Events History message details.
Retention
The application log files (msh-pm-milter.log, msh-pm-settings.log) are rotated by the services themselves as described above. Message trace files and .eml body dumps are deleted by the milter once they are older than the Retention period (hours) configured in the Diagnostic Logging panel. The other files (milter.out, settings.out, milter.stats) are single files that are overwritten in place and never grow beyond a trivial size.
.empty
A zero-byte placeholder file shipped in the installation archive so that var/log exists after unpacking. It is never written to and can be ignored.