mirror of https://github.com/python/cpython
bpo-42378: fixed log truncation on logging shutdown (GH-27310)
Automerge-Triggered-By: GH:vsajip
This commit is contained in:
parent
9751f85914
commit
96cf5a63d2
|
@ -117,6 +117,9 @@ sends logging output to a disk file. It inherits the output functionality from
|
||||||
|
|
||||||
Outputs the record to the file.
|
Outputs the record to the file.
|
||||||
|
|
||||||
|
Note that if the file was closed due to logging shutdown at exit and the file
|
||||||
|
mode is 'w', the record will not be emitted (see :issue:`42378`).
|
||||||
|
|
||||||
|
|
||||||
.. _null-handler:
|
.. _null-handler:
|
||||||
|
|
||||||
|
|
|
@ -882,6 +882,7 @@ class Handler(Filterer):
|
||||||
self._name = None
|
self._name = None
|
||||||
self.level = _checkLevel(level)
|
self.level = _checkLevel(level)
|
||||||
self.formatter = None
|
self.formatter = None
|
||||||
|
self._closed = False
|
||||||
# Add the handler to the global _handlerList (for cleanup on shutdown)
|
# Add the handler to the global _handlerList (for cleanup on shutdown)
|
||||||
_addHandlerRef(self)
|
_addHandlerRef(self)
|
||||||
self.createLock()
|
self.createLock()
|
||||||
|
@ -1000,6 +1001,7 @@ class Handler(Filterer):
|
||||||
#get the module data lock, as we're updating a shared structure.
|
#get the module data lock, as we're updating a shared structure.
|
||||||
_acquireLock()
|
_acquireLock()
|
||||||
try: #unlikely to raise an exception, but you never know...
|
try: #unlikely to raise an exception, but you never know...
|
||||||
|
self._closed = True
|
||||||
if self._name and self._name in _handlers:
|
if self._name and self._name in _handlers:
|
||||||
del _handlers[self._name]
|
del _handlers[self._name]
|
||||||
finally:
|
finally:
|
||||||
|
@ -1188,6 +1190,8 @@ class FileHandler(StreamHandler):
|
||||||
finally:
|
finally:
|
||||||
# Issue #19523: call unconditionally to
|
# Issue #19523: call unconditionally to
|
||||||
# prevent a handler leak when delay is set
|
# prevent a handler leak when delay is set
|
||||||
|
# Also see Issue #42378: we also rely on
|
||||||
|
# self._closed being set to True there
|
||||||
StreamHandler.close(self)
|
StreamHandler.close(self)
|
||||||
finally:
|
finally:
|
||||||
self.release()
|
self.release()
|
||||||
|
@ -1207,10 +1211,15 @@ class FileHandler(StreamHandler):
|
||||||
|
|
||||||
If the stream was not opened because 'delay' was specified in the
|
If the stream was not opened because 'delay' was specified in the
|
||||||
constructor, open it before calling the superclass's emit.
|
constructor, open it before calling the superclass's emit.
|
||||||
|
|
||||||
|
If stream is not open, current mode is 'w' and `_closed=True`, record
|
||||||
|
will not be emitted (see Issue #42378).
|
||||||
"""
|
"""
|
||||||
if self.stream is None:
|
if self.stream is None:
|
||||||
self.stream = self._open()
|
if self.mode != 'w' or not self._closed:
|
||||||
StreamHandler.emit(self, record)
|
self.stream = self._open()
|
||||||
|
if self.stream:
|
||||||
|
StreamHandler.emit(self, record)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
level = getLevelName(self.level)
|
level = getLevelName(self.level)
|
||||||
|
|
|
@ -5174,6 +5174,9 @@ class BaseFileTest(BaseTest):
|
||||||
msg="Log file %r does not exist" % filename)
|
msg="Log file %r does not exist" % filename)
|
||||||
self.rmfiles.append(filename)
|
self.rmfiles.append(filename)
|
||||||
|
|
||||||
|
def next_rec(self):
|
||||||
|
return logging.LogRecord('n', logging.DEBUG, 'p', 1,
|
||||||
|
self.next_message(), None, None, None)
|
||||||
|
|
||||||
class FileHandlerTest(BaseFileTest):
|
class FileHandlerTest(BaseFileTest):
|
||||||
def test_delay(self):
|
def test_delay(self):
|
||||||
|
@ -5186,11 +5189,18 @@ class FileHandlerTest(BaseFileTest):
|
||||||
self.assertTrue(os.path.exists(self.fn))
|
self.assertTrue(os.path.exists(self.fn))
|
||||||
fh.close()
|
fh.close()
|
||||||
|
|
||||||
class RotatingFileHandlerTest(BaseFileTest):
|
def test_emit_after_closing_in_write_mode(self):
|
||||||
def next_rec(self):
|
# Issue #42378
|
||||||
return logging.LogRecord('n', logging.DEBUG, 'p', 1,
|
os.unlink(self.fn)
|
||||||
self.next_message(), None, None, None)
|
fh = logging.FileHandler(self.fn, encoding='utf-8', mode='w')
|
||||||
|
fh.setFormatter(logging.Formatter('%(message)s'))
|
||||||
|
fh.emit(self.next_rec()) # '1'
|
||||||
|
fh.close()
|
||||||
|
fh.emit(self.next_rec()) # '2'
|
||||||
|
with open(self.fn) as fp:
|
||||||
|
self.assertEqual(fp.read().strip(), '1')
|
||||||
|
|
||||||
|
class RotatingFileHandlerTest(BaseFileTest):
|
||||||
def test_should_not_rollover(self):
|
def test_should_not_rollover(self):
|
||||||
# If maxbytes is zero rollover never occurs
|
# If maxbytes is zero rollover never occurs
|
||||||
rh = logging.handlers.RotatingFileHandler(
|
rh = logging.handlers.RotatingFileHandler(
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Fixes the issue with log file being overwritten when
|
||||||
|
:class:`logging.FileHandler` is used in :mod:`atexit` with *filemode* set to
|
||||||
|
``'w'``. Note this will cause the message in *atexit* not being logged if
|
||||||
|
the log stream is already closed due to shutdown of logging.
|
Loading…
Reference in New Issue