Log rotation is supposed to be routine maintenance. But if your collector reads a file while another process renames, truncates, or compresses it, events can slip through the gap — and you often won’t notice until you go looking for a log that isn’t there. For a security team, that gap is a blind spot: a detection that never fired, an audit trail with a hole in it, a control you can’t prove was working. This gets worse on high-volume services and resource-constrained systems, where rotation happens more often and the collector has less slack to catch up.
The good news is that losing events during rotation is a configuration problem, not an inevitability. Get the source-side rotation policy and the collector’s behavior right, and you can rotate as often as you like without dropping a line. Here’s how rotation breaks, and how to set it up so it doesn’t.
What rotation does to your files
Most rotation falls into two patterns.
Rename and recreate.
A rotation tool renames the active file (say app.log) to app.log.1 and creates a fresh app.log.
On Unix, a process that already holds the file open keeps writing to the same inode — now app.log.1 — until it’s told to reopen.
That’s why applications need a HUP signal or a postrotate hook after rotation.
Copy and truncate. The tool copies the file to an archive, then truncates the original to zero bytes in place. The inode stays the same, so the writing process needs no signal. The catch is the gap between the copy finishing and the truncate: anything written in that window is gone before a collector can read it.
Rotated files are then often compressed, turning app.log.1 into app.log.1.gz.
Rotation changes the file out from under whatever is reading it.
Events get lost when the reader doesn’t expect the change.
Where events get lost
A few specific failure modes account for most missing events:
-
The collector abandons the old file too early. When
app.logis rotated away, the reader needs to finish the tail of the renamed file before switching to the new one. Jump straight to the new file and you drop whatever was still unread. -
The copy-truncate race. Events written between the copy and the truncate never reach the collector. This loss happens at the source, so no collector setting can recover it.
-
Rotation outruns the poll interval. If a high-volume file rotates several times between reads and the retention count is low, entire generations can be created and deleted before the collector opens them.
-
No saved position across restarts. If the collector forgets where it was, a restart either re-reads from the top (duplicates) or skips to the end (loss), depending on its defaults.
-
Inode reuse fools rename detection. When a file is deleted and a new one reuses its inode number, a collector tracking files by inode alone can mistake the new file for the old and skip its contents.
Tracking files by inode is necessary, but not enough on its own.
How to rotate without losing events
These principles hold regardless of which collector you run:
Let the reader finish before you rotate. The safest rotation waits until the collector has read a file to its end. Failing that, give it enough time and enough retained generations to catch up.
Persist the read position to disk. A position saved on shutdown means a restart resumes where it stopped, with no re-reads and no skips.
Match poll frequency and file size to your log volume. A file that rotates every few seconds needs a sub-second poll interval, a larger size threshold, or both. Size the rotation so the reader is never racing it.
Prefer rename-and-recreate over copy-truncate.
Renaming keeps the original bytes intact for the reader to finish.
If you have to use the copytruncate directive, accept that a small loss window comes with it.
Use delaycompress.
Compressing a rotated file one cycle later leaves it as plain text long enough for a reader to finish it before it becomes a .gz it can’t parse.
A typical loss-safe logrotate policy looks like this:
# /etc/logrotate.d/app
/var/log/app/*.log {
daily
rotate 14
missingok
notifempty
create 0640 app app
delaycompress
compress
sharedscripts
postrotate
systemctl reload app > /dev/null 2>&1 || true
endscript
}
This renames rather than truncates, keeps two weeks of history, delays compression by a cycle, and signals the application to reopen after each rotation.
How NXLog Agent handles log rotation
NXLog Agent’s File input module is built around these same principles, so you configure them rather than engineer them yourself.
It resumes cleanly after a restart.
With SavePos (on by default), the agent records its position before exiting and reads it back on startup.
Paired with ReadFromLast, a restarted agent continues from the saved offset instead of re-reading or skipping.
It finishes a rotated file, then follows the rotation. When the module can’t read more from a file, it checks whether the open descriptor still points to the original filename. If the inode no longer matches, it treats the file as moved and reopens its input, so it still reads the tail of the rotated file.
It tunes to your rotation rate.
PollInterval sets how often the module checks for new content (default 1 second, and you can go fractional).
ActiveFiles caps how many wildcard-matched files it watches at once (default 10), and DirCheckInterval controls how often it picks up new files.
It handles tricky rotation explicitly.
Set RenameCheck TRUE to recognize a rotated file by matching inode and size and skip re-reading it — useful, with the inode-reuse caveat in mind.
Or set DetectContentChanges TRUE to checksum files and catch in-place overwrites, reading from the start when the content changes.
Here’s a reader configuration for logs that something else rotates:
<Input app_log>
Module im_file
File '/var/log/app/*.log'
SavePos TRUE
ReadFromLast TRUE
PollInterval 0.5
ActiveFiles 20
CloseWhenIdle TRUE
</Input>
Name your rotated files so they fall outside the File wildcard, for example by rotating them into a separate archive directory.
Rotated files then drop off the watch list on their own, without relying on inode matching.
When NXLog Agent owns the rotation, the OnEOF block runs an action once the module has read the file to the end — the cleanest moment to rotate, since there’s nothing left to lose:
<Extension fileop>
Module xm_fileop
</Extension>
<Input app_log>
Module im_file
File '/var/log/app.log'
<OnEOF>
<Exec>
file_rename(file_name(),
file_name() + strftime(now(), '_%Y%m%dT%H%M%S'));
</Exec>
GraceTimeout 10
</OnEOF>
</Input>
The timestamped name keeps the rotated file clear of the File pattern, so the agent won’t pick it up again.
NXLog Agent in SecOps practice
This matters most where logs are high-volume and completeness isn’t optional. Firewalls, web servers, DNS, and EDR tooling all rotate frequently and at volume, and all feed detections and investigations. NXLog Agent collects from these sources across Windows, Linux, and macOS, then forwards to NXLog Platform or your SIEM for correlation and retention.
For regulated environments in finance, healthcare, and government, "we think we captured everything" doesn’t hold up in an audit. Saved positions, full-tail reads after rotation, and rotation tied to end-of-file give you a collection path you can stand behind.
The takeaway
Rotation doesn’t lose events — collectors that mishandle rotation do. Decide where reading should resume, give the reader time to finish each file, and prefer renaming over truncation. Then let your collector’s rotation handling do the rest. If you’d like a hand mapping this to your own log sources, we’re glad to help.