One of my servers ran out of space recently. So I started looking into it. The nginx
logs took half of the partition. Also, I noticed a strange thing. For a lot of sites (60%) extra rotations where present (example.com-access.log.53.gz
when rotate 52
). And most of the biggest ones—but not all—had only two rotations:
example.com-access.log
example.com-access.log.53.gz
50% of the logs had only those two rotations. Sometimes there were just holes in the rotations (30%): one file or more. *.log.1
was often missing (25%). Sometimes there were both *.log.1
and *.log.1.gz
(2 out of 172).
Can you explain this missing/duplicate rotations? *.log
+ *.log.53.gz
case makes me think that at some point it was unable to rotate *.log.1
to *.log.2.gz
. But wouldn't it stop after unsuccessful gzip
? Then there must be no holes. Or at least there must be *.log.1
present if it wouldn't, mustn't there?
I'm running Debian server if anything.
/etc/logrotate.conf
:
# see "man logrotate" for details
# rotate log files weekly
weekly
# keep 4 weeks worth of backlogs
rotate 4
# create new (empty) log files after rotating old ones
create
# uncomment this if you want your log files compressed
#compress
# packages drop log rotation information into this directory
include /etc/logrotate.d
# no packages own wtmp, or btmp -- we'll rotate them here
/var/log/wtmp {
missingok
monthly
create 0664 root utmp
rotate 1
}
/var/log/btmp {
missingok
monthly
create 0660 root utmp
rotate 1
}
# system-specific logs may be configured here
/etc/logrotate.d/nginx
:
/var/log/nginx/*.log {
weekly
missingok
rotate 52
compress
delaycompress
notifempty
create 0640 www-data adm
size 50M
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
[ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
endscript
}
/etc/logrotate.d/httpd-prerotate
doesn't exist.
tl;dr Duplicates might be produced if compression (
gzip
) was interrupted. One such duplicate (ifsharedscripts
) makes it eventually leave only one gzipped rotation (#rotate + 1
).Here's a simplified version of what's going on under the hood (logset is an entry
/path/to/dir/*.log {...}
in the config):So, with
sharedscripts
normally an error, that occurs while handling a log file belonging to a logset, stops processing of the whole logset. Without it processing of just one log file is stopped.But nonexistent gzipped rotation or 1st rotation of a log file doesn't count as an error. As doesn't the case when log file itself doesn't exist if
missingok
(doesn't matter in case of a pattern). Also an error duringprerotateSingleLog()
phase withsharedscripts
doesn't break the loop.Do note, I made a lot of simplifications while compiling the code above. Consult the original when in doubt.
With this, the only cases I can see where there might be missing or extra files is when
logrotate
is interrupted. That might explainrotate + 1
rotations (gzipped rotations were renamed but the last one hasn't been removed). Also, whengzip
is interrupted, it leaves the target file behind. That explains having both*.log.1
and*.log.1.gz
rotations. Still no explanation for the holes in the rotations.UPD It appears duplicates (
*.log.1
+*.log.1.gz
) produce an error:That stops processing after
prerotateSingleLog()
phase. At that point all gzipped rotations were renamed. But renaming*.log -> *.log.1
and removing*.log.${rotate + 1}.gz
are skipped.*.log
grow bigger and bigger, and eventually you run out of space.That explains everything but missing
*.log.1
rotations. But probably is as good as it gets.So, beware of errors in log rotation. You can identify the issue by spotting "error:" line in
logrotate
(even non-verbose) output.As a bonus, a script that displays contents of a log dir in a nice way (natural sort, with sizes):
And another that let's you check if you've got the issues I ran into: