Skip to content

Commit 3651c27

Browse files
[3.12] gh-93205: When rotating logs with no namer specified, match whole extension (GH-93224) (GH-115784)
(cherry picked from commit 113687a) Co-authored-by: Gabriele Catania <[email protected]>
1 parent 8e70dc8 commit 3651c27

File tree

3 files changed

+62
-19
lines changed

3 files changed

+62
-19
lines changed

Lib/logging/handlers.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -369,32 +369,37 @@ def getFilesToDelete(self):
369369
dirName, baseName = os.path.split(self.baseFilename)
370370
fileNames = os.listdir(dirName)
371371
result = []
372-
# See bpo-44753: Don't use the extension when computing the prefix.
373-
n, e = os.path.splitext(baseName)
374-
prefix = n + '.'
375-
plen = len(prefix)
376-
for fileName in fileNames:
377-
if self.namer is None:
378-
# Our files will always start with baseName
379-
if not fileName.startswith(baseName):
380-
continue
381-
else:
372+
if self.namer is None:
373+
prefix = baseName + '.'
374+
plen = len(prefix)
375+
for fileName in fileNames:
376+
if fileName[:plen] == prefix:
377+
suffix = fileName[plen:]
378+
if self.extMatch.match(suffix):
379+
result.append(os.path.join(dirName, fileName))
380+
else:
381+
# See bpo-44753: Don't use the extension when computing the prefix.
382+
n, e = os.path.splitext(baseName)
383+
prefix = n + '.'
384+
plen = len(prefix)
385+
for fileName in fileNames:
382386
# Our files could be just about anything after custom naming, but
383387
# likely candidates are of the form
384388
# foo.log.DATETIME_SUFFIX or foo.DATETIME_SUFFIX.log
385389
if (not fileName.startswith(baseName) and fileName.endswith(e) and
386390
len(fileName) > (plen + 1) and not fileName[plen+1].isdigit()):
387391
continue
388392

389-
if fileName[:plen] == prefix:
390-
suffix = fileName[plen:]
391-
# See bpo-45628: The date/time suffix could be anywhere in the
392-
# filename
393-
parts = suffix.split('.')
394-
for part in parts:
395-
if self.extMatch.match(part):
396-
result.append(os.path.join(dirName, fileName))
397-
break
393+
if fileName[:plen] == prefix:
394+
suffix = fileName[plen:]
395+
# See bpo-45628: The date/time suffix could be anywhere in the
396+
# filename
397+
398+
parts = suffix.split('.')
399+
for part in parts:
400+
if self.extMatch.match(part):
401+
result.append(os.path.join(dirName, fileName))
402+
break
398403
if len(result) < self.backupCount:
399404
result = []
400405
else:

Lib/test/test_logging.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6138,6 +6138,43 @@ def test_compute_files_to_delete(self):
61386138
self.assertTrue(fn.startswith(prefix + '.') and
61396139
fn[len(prefix) + 2].isdigit())
61406140

6141+
def test_compute_files_to_delete_same_filename_different_extensions(self):
6142+
# See GH-93205 for background
6143+
wd = pathlib.Path(tempfile.mkdtemp(prefix='test_logging_'))
6144+
self.addCleanup(shutil.rmtree, wd)
6145+
times = []
6146+
dt = datetime.datetime.now()
6147+
n_files = 10
6148+
for _ in range(n_files):
6149+
times.append(dt.strftime('%Y-%m-%d_%H-%M-%S'))
6150+
dt += datetime.timedelta(seconds=5)
6151+
prefixes = ('a.log', 'a.log.b')
6152+
files = []
6153+
rotators = []
6154+
for i, prefix in enumerate(prefixes):
6155+
backupCount = i+1
6156+
rotator = logging.handlers.TimedRotatingFileHandler(wd / prefix, when='s',
6157+
interval=5,
6158+
backupCount=backupCount,
6159+
delay=True)
6160+
rotators.append(rotator)
6161+
for t in times:
6162+
files.append('%s.%s' % (prefix, t))
6163+
# Create empty files
6164+
for f in files:
6165+
(wd / f).touch()
6166+
# Now the checks that only the correct files are offered up for deletion
6167+
for i, prefix in enumerate(prefixes):
6168+
backupCount = i+1
6169+
rotator = rotators[i]
6170+
candidates = rotator.getFilesToDelete()
6171+
self.assertEqual(len(candidates), n_files - backupCount)
6172+
matcher = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$")
6173+
for c in candidates:
6174+
d, fn = os.path.split(c)
6175+
self.assertTrue(fn.startswith(prefix))
6176+
suffix = fn[(len(prefix)+1):]
6177+
self.assertRegex(suffix, matcher)
61416178

61426179
def secs(**kw):
61436180
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in :class:`logging.handlers.TimedRotatingFileHandler` where multiple rotating handler instances pointing to files with the same name but different extensions would conflict and not delete the correct files.

0 commit comments

Comments
 (0)