Mario Stefanutti
Mario Stefanutti

Reputation: 262

Python handlers.TimedRotatingFileHandler rotation does not work as I expected

I need file rotation to happen exactly when the hour change, not after an hour from the program has started.

PS: I took care of writing logs at <hour>:59:58 and <hour+1>:00:02 to to avoid to wait next write.

Is it possible?

[handler_file_handler]
class=handlers.TimedRotatingFileHandler
level=INFO
formatter=formatter
delay=False
args=('logfile.log', 'H', 1, 0)

Upvotes: 1

Views: 3620

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121494

The TimedRotatingFileHandler documentation tells you that it uses an offset from the current time; the only exceptions are the weekday and daily options (W0-W6 and midnight), which use atTime as the point in time to roll over:

When computing the next rollover time for the first time (when the handler is created), the last modification time of an existing log file, or else the current time, is used to compute when the next rotation will occur.

[...]

If atTime is not None, it must be a datetime.time instance which specifies the time of day when rollover occurs, for the cases where rollover is set to happen “at midnight” or “on a particular weekday”. Note that in these cases, the atTime value is effectively used to compute the initial rollover, and subsequent rollovers would be calculated via the normal interval calculation.

If you must have roll-overs occur hourly, on the hour, then you have two options:

  • Create an initial logfile with a mtime modification value set to the most recent whole hour. This will then be used to write current entries to, and it'll be rotated a whole hour from the set modification time.
  • Provide an alternative TimedRotatingFileHandler.computeRollover() implementation which returns the timestamp for the next whole hour, rather than just current time + interval.

The latter is probably the better path here; rounding the current time to the nearest self.interval multiple is simple enough:

from logging.handlers import TimedRotatingFileHandler

class WholeIntervalRotatingFileHandler(TimedRotatingFileHandler):
    def computeRollover(self, currentTime):
        if self.when[0] == 'W' or self.when == 'MIDNIGHT':
            # use existing computation
            return super().computeRollover(currentTime)
        # round time up to nearest next multiple of the interval
        return ((currentTime // self.interval) + 1) * self.interval

This calculates the next rollover time to be an exact multiple of the interval. Take into account that if you set the interval argument to the class to a value other than 1 then the next whole interval time value could be different; interval=2 would pick the next whole hour divisible by 2, set it to 0.5 you'd see rotation at the whole and half hour.

To use the above in a fileConfig configuration file, just put the code in a module that lives on the module search path, then use class=modulename.WholeIntervalRotatingFileHandler in your handler section.

Demo to show that the next computed rollover time is indeed the next whole hour:

>>> from datetime import datetime
>>> print(datetime.now())   # current time
2018-11-17 16:48:08.793855
>>> handler = WholeIntervalRotatingFileHandler('/tmp/logfile.log', 'H', 1, 0)
>>> print(datetime.fromtimestamp(handler.rolloverAt))  # next rotation time
2018-11-17 17:00:00

Upvotes: 8

Related Questions