Skip to content

Commit a713079

Browse files
committed
Closed #9556: Allowed specifying a time-of-day for a TimedRotatingFileHandler to rotate.
1 parent 8a9e38e commit a713079

File tree

4 files changed

+73
-8
lines changed

4 files changed

+73
-8
lines changed

Doc/library/logging.handlers.rst

+7-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ The :class:`TimedRotatingFileHandler` class, located in the
296296
timed intervals.
297297

298298

299-
.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)
299+
.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
300300

301301
Returns a new instance of the :class:`TimedRotatingFileHandler` class. The
302302
specified file is opened and used as the stream for logging. On rotating it also
@@ -346,6 +346,12 @@ timed intervals.
346346
If *delay* is true, then file opening is deferred until the first call to
347347
:meth:`emit`.
348348

349+
If *atTime* is not ``None``, it must be a ``datetime.time`` instance which
350+
specifies the time of day when rollover occurs, for the cases where rollover
351+
is set to happen "at midnight" or "on a particular weekday".
352+
353+
.. versionchanged:: 3.4
354+
*atTime* parameter was added.
349355

350356
.. method:: doRollover()
351357

Lib/logging/handlers.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
1+
# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
22
#
33
# Permission to use, copy, modify, and distribute this software and its
44
# documentation for any purpose and without fee is hereby granted,
@@ -18,7 +18,7 @@
1818
Additional handlers for the logging package for Python. The core package is
1919
based on PEP 282 and comments thereto in comp.lang.python.
2020
21-
Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
21+
Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
2222
2323
To use, simply 'import logging.handlers' and log away!
2424
"""
@@ -196,11 +196,12 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
196196
If backupCount is > 0, when rollover is done, no more than backupCount
197197
files are kept - the oldest ones are deleted.
198198
"""
199-
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
199+
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
200200
BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
201201
self.when = when.upper()
202202
self.backupCount = backupCount
203203
self.utc = utc
204+
self.atTime = atTime
204205
# Calculate the real rollover interval, which is just the number of
205206
# seconds between rollovers. Also set the filename suffix used when
206207
# a rollover occurs. Current 'when' events supported:
@@ -270,9 +271,22 @@ def computeRollover(self, currentTime):
270271
currentHour = t[3]
271272
currentMinute = t[4]
272273
currentSecond = t[5]
273-
# r is the number of seconds left between now and midnight
274-
r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
275-
currentSecond)
274+
currentDay = t[6]
275+
# r is the number of seconds left between now and the next rotation
276+
if self.atTime is None:
277+
rotate_ts = _MIDNIGHT
278+
else:
279+
rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 +
280+
self.atTime.second)
281+
282+
r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 +
283+
currentSecond)
284+
if r < 0:
285+
# Rotate time is before the current time (for example when
286+
# self.rotateAt is 13:45 and it now 14:15), rotation is
287+
# tomorrow.
288+
r += _MIDNIGHT
289+
currentDay = (currentDay + 1) % 7
276290
result = currentTime + r
277291
# If we are rolling over on a certain day, add in the number of days until
278292
# the next rollover, but offset by 1 since we just calculated the time
@@ -290,7 +304,7 @@ def computeRollover(self, currentTime):
290304
# This is because the above time calculation takes us to midnight on this
291305
# day, i.e. the start of the next day.
292306
if self.when.startswith('W'):
293-
day = t[6] # 0 is Monday
307+
day = currentDay # 0 is Monday
294308
if day != self.dayOfWeek:
295309
if day < self.dayOfWeek:
296310
daysToWait = self.dayOfWeek - day

Lib/test/test_logging.py

+42
Original file line numberDiff line numberDiff line change
@@ -3949,6 +3949,48 @@ def test_invalid(self):
39493949
assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
39503950
self.fn, 'W7', delay=True)
39513951

3952+
def test_compute_rollover_daily_attime(self):
3953+
currentTime = 0
3954+
atTime = datetime.time(12, 0, 0)
3955+
rh = logging.handlers.TimedRotatingFileHandler(
3956+
self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
3957+
atTime=atTime)
3958+
3959+
actual = rh.computeRollover(currentTime)
3960+
self.assertEqual(actual, currentTime + 12 * 60 * 60)
3961+
3962+
actual = rh.computeRollover(currentTime + 13 * 60 * 60)
3963+
self.assertEqual(actual, currentTime + 36 * 60 * 60)
3964+
3965+
rh.close()
3966+
3967+
def test_compute_rollover_weekly_attime(self):
3968+
currentTime = 0
3969+
atTime = datetime.time(12, 0, 0)
3970+
3971+
wday = datetime.datetime.fromtimestamp(currentTime).weekday()
3972+
for day in range(7):
3973+
rh = logging.handlers.TimedRotatingFileHandler(
3974+
self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
3975+
atTime=atTime)
3976+
3977+
if wday > day:
3978+
expected = (7 - wday + day)
3979+
else:
3980+
expected = (day - wday)
3981+
expected *= 24 * 60 * 60
3982+
expected += 12 * 60 * 60
3983+
actual = rh.computeRollover(currentTime)
3984+
self.assertEqual(actual, expected)
3985+
if day == wday:
3986+
# goes into following week
3987+
expected += 7 * 24 * 60 * 60
3988+
actual = rh.computeRollover(currentTime + 13 * 60 * 60)
3989+
self.assertEqual(actual, expected)
3990+
3991+
rh.close()
3992+
3993+
39523994
def secs(**kw):
39533995
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
39543996

Misc/NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Core and Builtins
3434
Library
3535
-------
3636

37+
- Issue #9556: Allowed specifying a time-of-day for a TimedRotatingFileHandler
38+
to rotate.
39+
3740
- Issue #14971: unittest test discovery no longer gets confused when a function
3841
has a different __name__ than its name in the TestCase class dictionary.
3942

0 commit comments

Comments
 (0)