Thu 11 June 2015
The logrotate tool has a mode called copytruncate which copies the log file and truncates the original, rather than renaming the original, so that the daemon doesn't need to reopen the log file.
I've long wondered how this is implemented without the obvious race condition (i.e. log is copied, data is written, log is truncated -- the data written in the middle would get lost with a naive implementation). Today I wanted to implement a similar feature in a project at work, so I set about finding out how logrotate implements it.
I couldn't find any information online about avoiding the race condition so I read the source, and was surprised to find no obvious tricks. Logrotate just copies the log to the new file, then truncates the original file. It simply ignores the race condition. In fact, on closer inspection, this is even documented in the man page:
Truncate the original log file to zero size in place after creating a copy, instead of moving the old log file and optionally creating a new one. It can be used when some program cannot be told to close its logfile and thus might continue writing (appending) to the previous log file forever. Note that there is a very small time slice between copying the file and truncating it, so some logging data might be lost. When this option is used, the create option will have no effect, as the old log file stays in place.
I wrote a small C program to test it (outputting increasing numbers as fast as it could) and, indeed, 4 million lines were lost during the course of the log rotation.
Much like with my previous post about Digital Ocean private networking, this won't be a surprise to anyone who thoroughly reads the documentation, but I, for one, was surprised by logrotate's behaviour.If you like my blog, please consider subscribing to the RSS feed or the mailing list: