From a713079ed82b0bfd841562fd7ac043313feb8ef6 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 12 Apr 2013 17:04:23 +0100 Subject: [PATCH] Closed #9556: Allowed specifying a time-of-day for a TimedRotatingFileHandler to rotate. --- Doc/library/logging.handlers.rst | 8 +++++- Lib/logging/handlers.py | 28 +++++++++++++++------ Lib/test/test_logging.py | 42 ++++++++++++++++++++++++++++++++ Misc/NEWS | 3 +++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 30f9e03a64b..909a678d0dd 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -296,7 +296,7 @@ The :class:`TimedRotatingFileHandler` class, located in the timed intervals. -.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False) +.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None) Returns a new instance of the :class:`TimedRotatingFileHandler` class. The specified file is opened and used as the stream for logging. On rotating it also @@ -346,6 +346,12 @@ timed intervals. If *delay* is true, then file opening is deferred until the first call to :meth:`emit`. + 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". + + .. versionchanged:: 3.4 + *atTime* parameter was added. .. method:: doRollover() diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 8e7bb1b6df7..b5478d90217 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1,4 +1,4 @@ -# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Additional handlers for the logging package for Python. The core package is based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. To use, simply 'import logging.handlers' and log away! """ @@ -196,11 +196,12 @@ class TimedRotatingFileHandler(BaseRotatingHandler): If backupCount is > 0, when rollover is done, no more than backupCount files are kept - the oldest ones are deleted. """ - def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False): + def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None): BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay) self.when = when.upper() self.backupCount = backupCount self.utc = utc + self.atTime = atTime # Calculate the real rollover interval, which is just the number of # seconds between rollovers. Also set the filename suffix used when # a rollover occurs. Current 'when' events supported: @@ -270,9 +271,22 @@ class TimedRotatingFileHandler(BaseRotatingHandler): currentHour = t[3] currentMinute = t[4] currentSecond = t[5] - # r is the number of seconds left between now and midnight - r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 + - currentSecond) + currentDay = t[6] + # r is the number of seconds left between now and the next rotation + if self.atTime is None: + rotate_ts = _MIDNIGHT + else: + rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 + + self.atTime.second) + + r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 + + currentSecond) + if r < 0: + # Rotate time is before the current time (for example when + # self.rotateAt is 13:45 and it now 14:15), rotation is + # tomorrow. + r += _MIDNIGHT + currentDay = (currentDay + 1) % 7 result = currentTime + r # If we are rolling over on a certain day, add in the number of days until # the next rollover, but offset by 1 since we just calculated the time @@ -290,7 +304,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler): # This is because the above time calculation takes us to midnight on this # day, i.e. the start of the next day. if self.when.startswith('W'): - day = t[6] # 0 is Monday + day = currentDay # 0 is Monday if day != self.dayOfWeek: if day < self.dayOfWeek: daysToWait = self.dayOfWeek - day diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index fea2b00fe39..73adae5b08a 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3949,6 +3949,48 @@ class TimedRotatingFileHandlerTest(BaseFileTest): assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler, self.fn, 'W7', delay=True) + def test_compute_rollover_daily_attime(self): + currentTime = 0 + atTime = datetime.time(12, 0, 0) + rh = logging.handlers.TimedRotatingFileHandler( + self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True, + atTime=atTime) + + actual = rh.computeRollover(currentTime) + self.assertEqual(actual, currentTime + 12 * 60 * 60) + + actual = rh.computeRollover(currentTime + 13 * 60 * 60) + self.assertEqual(actual, currentTime + 36 * 60 * 60) + + rh.close() + + def test_compute_rollover_weekly_attime(self): + currentTime = 0 + atTime = datetime.time(12, 0, 0) + + wday = datetime.datetime.fromtimestamp(currentTime).weekday() + for day in range(7): + rh = logging.handlers.TimedRotatingFileHandler( + self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True, + atTime=atTime) + + if wday > day: + expected = (7 - wday + day) + else: + expected = (day - wday) + expected *= 24 * 60 * 60 + expected += 12 * 60 * 60 + actual = rh.computeRollover(currentTime) + self.assertEqual(actual, expected) + if day == wday: + # goes into following week + expected += 7 * 24 * 60 * 60 + actual = rh.computeRollover(currentTime + 13 * 60 * 60) + self.assertEqual(actual, expected) + + rh.close() + + def secs(**kw): return datetime.timedelta(**kw) // datetime.timedelta(seconds=1) diff --git a/Misc/NEWS b/Misc/NEWS index 17ded2da681..90c609e31df 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -34,6 +34,9 @@ Core and Builtins Library ------- +- Issue #9556: Allowed specifying a time-of-day for a TimedRotatingFileHandler + to rotate. + - Issue #14971: unittest test discovery no longer gets confused when a function has a different __name__ than its name in the TestCase class dictionary.