mirror of https://github.com/python/cpython
bpo-37111: Add 'encoding' and 'errors' parameters to logging.basicCon… (GH-14008)
This commit is contained in:
parent
00f6493084
commit
ca7b504a4d
|
@ -128,10 +128,18 @@ look at that next. Be sure to try the following in a newly-started Python
|
||||||
interpreter, and don't just continue from the session described above::
|
interpreter, and don't just continue from the session described above::
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logging.basicConfig(filename='example.log',level=logging.DEBUG)
|
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
|
||||||
logging.debug('This message should go to the log file')
|
logging.debug('This message should go to the log file')
|
||||||
logging.info('So should this')
|
logging.info('So should this')
|
||||||
logging.warning('And this, too')
|
logging.warning('And this, too')
|
||||||
|
logging.error('And non-ASCII stuff, too, like Øresund and Malmö')
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *encoding* argument was added. In earlier Python versions, or if not
|
||||||
|
specified, the encoding used is the default value used by :func:`open`. While
|
||||||
|
not shown in the above example, an *errors* argument can also now be passed,
|
||||||
|
which determines how encoding errors are handled. For available values and
|
||||||
|
the default, see the documentation for :func:`open`.
|
||||||
|
|
||||||
And now if we open the file and look at what we have, we should find the log
|
And now if we open the file and look at what we have, we should find the log
|
||||||
messages:
|
messages:
|
||||||
|
@ -141,6 +149,7 @@ messages:
|
||||||
DEBUG:root:This message should go to the log file
|
DEBUG:root:This message should go to the log file
|
||||||
INFO:root:So should this
|
INFO:root:So should this
|
||||||
WARNING:root:And this, too
|
WARNING:root:And this, too
|
||||||
|
ERROR:root:And non-ASCII stuff, too, like Øresund and Malmö
|
||||||
|
|
||||||
This example also shows how you can set the logging level which acts as the
|
This example also shows how you can set the logging level which acts as the
|
||||||
threshold for tracking. In this case, because we set the threshold to
|
threshold for tracking. In this case, because we set the threshold to
|
||||||
|
|
|
@ -89,18 +89,22 @@ sends logging output to a disk file. It inherits the output functionality from
|
||||||
:class:`StreamHandler`.
|
:class:`StreamHandler`.
|
||||||
|
|
||||||
|
|
||||||
.. class:: FileHandler(filename, mode='a', encoding=None, delay=False)
|
.. class:: FileHandler(filename, mode='a', encoding=None, delay=False, errors=None)
|
||||||
|
|
||||||
Returns a new instance of the :class:`FileHandler` class. The specified file is
|
Returns a new instance of the :class:`FileHandler` class. The specified file is
|
||||||
opened and used as the stream for logging. If *mode* is not specified,
|
opened and used as the stream for logging. If *mode* is not specified,
|
||||||
:const:`'a'` is used. If *encoding* is not ``None``, it is used to open the file
|
:const:`'a'` is used. If *encoding* is not ``None``, it is used to open the file
|
||||||
with that encoding. If *delay* is true, then file opening is deferred until the
|
with that encoding. If *delay* is true, then file opening is deferred until the
|
||||||
first call to :meth:`emit`. By default, the file grows indefinitely.
|
first call to :meth:`emit`. By default, the file grows indefinitely. If
|
||||||
|
*errors* is specified, it's used to determine how encoding errors are handled.
|
||||||
|
|
||||||
.. versionchanged:: 3.6
|
.. versionchanged:: 3.6
|
||||||
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
||||||
for the *filename* argument.
|
for the *filename* argument.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *errors* parameter was added.
|
||||||
|
|
||||||
.. method:: close()
|
.. method:: close()
|
||||||
|
|
||||||
Closes the file.
|
Closes the file.
|
||||||
|
@ -168,18 +172,22 @@ exclusive locks - and so there is no need for such a handler. Furthermore,
|
||||||
for this value.
|
for this value.
|
||||||
|
|
||||||
|
|
||||||
.. class:: WatchedFileHandler(filename, mode='a', encoding=None, delay=False)
|
.. class:: WatchedFileHandler(filename, mode='a', encoding=None, delay=False, errors=None)
|
||||||
|
|
||||||
Returns a new instance of the :class:`WatchedFileHandler` class. The specified
|
Returns a new instance of the :class:`WatchedFileHandler` class. The specified
|
||||||
file is opened and used as the stream for logging. If *mode* is not specified,
|
file is opened and used as the stream for logging. If *mode* is not specified,
|
||||||
:const:`'a'` is used. If *encoding* is not ``None``, it is used to open the file
|
:const:`'a'` is used. If *encoding* is not ``None``, it is used to open the file
|
||||||
with that encoding. If *delay* is true, then file opening is deferred until the
|
with that encoding. If *delay* is true, then file opening is deferred until the
|
||||||
first call to :meth:`emit`. By default, the file grows indefinitely.
|
first call to :meth:`emit`. By default, the file grows indefinitely. If
|
||||||
|
*errors* is provided, it determines how encoding errors are handled.
|
||||||
|
|
||||||
.. versionchanged:: 3.6
|
.. versionchanged:: 3.6
|
||||||
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
||||||
for the *filename* argument.
|
for the *filename* argument.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *errors* parameter was added.
|
||||||
|
|
||||||
.. method:: reopenIfNeeded()
|
.. method:: reopenIfNeeded()
|
||||||
|
|
||||||
Checks to see if the file has changed. If it has, the existing stream is
|
Checks to see if the file has changed. If it has, the existing stream is
|
||||||
|
@ -205,7 +213,7 @@ module, is the base class for the rotating file handlers,
|
||||||
not need to instantiate this class, but it has attributes and methods you may
|
not need to instantiate this class, but it has attributes and methods you may
|
||||||
need to override.
|
need to override.
|
||||||
|
|
||||||
.. class:: BaseRotatingHandler(filename, mode, encoding=None, delay=False)
|
.. class:: BaseRotatingHandler(filename, mode, encoding=None, delay=False, errors=None)
|
||||||
|
|
||||||
The parameters are as for :class:`FileHandler`. The attributes are:
|
The parameters are as for :class:`FileHandler`. The attributes are:
|
||||||
|
|
||||||
|
@ -284,13 +292,14 @@ The :class:`RotatingFileHandler` class, located in the :mod:`logging.handlers`
|
||||||
module, supports rotation of disk log files.
|
module, supports rotation of disk log files.
|
||||||
|
|
||||||
|
|
||||||
.. class:: RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)
|
.. class:: RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None)
|
||||||
|
|
||||||
Returns a new instance of the :class:`RotatingFileHandler` class. The specified
|
Returns a new instance of the :class:`RotatingFileHandler` class. The specified
|
||||||
file is opened and used as the stream for logging. If *mode* is not specified,
|
file is opened and used as the stream for logging. If *mode* is not specified,
|
||||||
``'a'`` is used. If *encoding* is not ``None``, it is used to open the file
|
``'a'`` is used. If *encoding* is not ``None``, it is used to open the file
|
||||||
with that encoding. If *delay* is true, then file opening is deferred until the
|
with that encoding. If *delay* is true, then file opening is deferred until the
|
||||||
first call to :meth:`emit`. By default, the file grows indefinitely.
|
first call to :meth:`emit`. By default, the file grows indefinitely. If
|
||||||
|
*errors* is provided, it determines how encoding errors are handled.
|
||||||
|
|
||||||
You can use the *maxBytes* and *backupCount* values to allow the file to
|
You can use the *maxBytes* and *backupCount* values to allow the file to
|
||||||
:dfn:`rollover` at a predetermined size. When the size is about to be exceeded,
|
:dfn:`rollover` at a predetermined size. When the size is about to be exceeded,
|
||||||
|
@ -311,6 +320,9 @@ module, supports rotation of disk log files.
|
||||||
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
||||||
for the *filename* argument.
|
for the *filename* argument.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *errors* parameter was added.
|
||||||
|
|
||||||
.. method:: doRollover()
|
.. method:: doRollover()
|
||||||
|
|
||||||
Does a rollover, as described above.
|
Does a rollover, as described above.
|
||||||
|
@ -331,7 +343,7 @@ The :class:`TimedRotatingFileHandler` class, located in the
|
||||||
timed intervals.
|
timed intervals.
|
||||||
|
|
||||||
|
|
||||||
.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
|
.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None, errors=None)
|
||||||
|
|
||||||
Returns a new instance of the :class:`TimedRotatingFileHandler` class. The
|
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
|
specified file is opened and used as the stream for logging. On rotating it also
|
||||||
|
@ -391,6 +403,9 @@ timed intervals.
|
||||||
rollover, and subsequent rollovers would be calculated via the normal
|
rollover, and subsequent rollovers would be calculated via the normal
|
||||||
interval calculation.
|
interval calculation.
|
||||||
|
|
||||||
|
If *errors* is specified, it's used to determine how encoding errors are
|
||||||
|
handled.
|
||||||
|
|
||||||
.. note:: Calculation of the initial rollover time is done when the handler
|
.. note:: Calculation of the initial rollover time is done when the handler
|
||||||
is initialised. Calculation of subsequent rollover times is done only
|
is initialised. Calculation of subsequent rollover times is done only
|
||||||
when rollover occurs, and rollover occurs only when emitting output. If
|
when rollover occurs, and rollover occurs only when emitting output. If
|
||||||
|
@ -411,6 +426,9 @@ timed intervals.
|
||||||
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
As well as string values, :class:`~pathlib.Path` objects are also accepted
|
||||||
for the *filename* argument.
|
for the *filename* argument.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *errors* parameter was added.
|
||||||
|
|
||||||
.. method:: doRollover()
|
.. method:: doRollover()
|
||||||
|
|
||||||
Does a rollover, as described above.
|
Does a rollover, as described above.
|
||||||
|
|
|
@ -1196,6 +1196,21 @@ functions.
|
||||||
| | carrying out the configuration as specified |
|
| | carrying out the configuration as specified |
|
||||||
| | by the other arguments. |
|
| | by the other arguments. |
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
|
| *encoding* | If this keyword argument is specified along |
|
||||||
|
| | with *filename*, its value is used when the |
|
||||||
|
| | FileHandler is created, and thus used when |
|
||||||
|
| | opening the output file. |
|
||||||
|
+--------------+---------------------------------------------+
|
||||||
|
| *errors* | If this keyword argument is specified along |
|
||||||
|
| | with *filename*, its value is used when the |
|
||||||
|
| | FileHandler is created, and thus used when |
|
||||||
|
| | opening the output file. If not specified, |
|
||||||
|
| | the value 'backslashreplace' is used. Note |
|
||||||
|
| | that if ``None`` is specified, it will be |
|
||||||
|
| | passed as such to func:`open`, which means |
|
||||||
|
| | that it will be treated the same as passing |
|
||||||
|
| | 'errors'. |
|
||||||
|
+--------------+---------------------------------------------+
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
The *style* argument was added.
|
The *style* argument was added.
|
||||||
|
@ -1209,6 +1224,9 @@ functions.
|
||||||
.. versionchanged:: 3.8
|
.. versionchanged:: 3.8
|
||||||
The *force* argument was added.
|
The *force* argument was added.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
The *encoding* and *errors* arguments were added.
|
||||||
|
|
||||||
.. function:: shutdown()
|
.. function:: shutdown()
|
||||||
|
|
||||||
Informs the logging system to perform an orderly shutdown by flushing and
|
Informs the logging system to perform an orderly shutdown by flushing and
|
||||||
|
|
|
@ -81,6 +81,7 @@ howto/ipaddress,,::,IPv6Address('2001:db8::ffff:ffff')
|
||||||
howto/ipaddress,,:ffff,IPv6Address('2001:db8::ffff:ffff')
|
howto/ipaddress,,:ffff,IPv6Address('2001:db8::ffff:ffff')
|
||||||
howto/logging,,:And,"WARNING:And this, too"
|
howto/logging,,:And,"WARNING:And this, too"
|
||||||
howto/logging,,:And,"WARNING:root:And this, too"
|
howto/logging,,:And,"WARNING:root:And this, too"
|
||||||
|
howto/logging,,:And,"ERROR:root:And non-ASCII stuff, too, like "
|
||||||
howto/logging,,:Doing,INFO:root:Doing something
|
howto/logging,,:Doing,INFO:root:Doing something
|
||||||
howto/logging,,:Finished,INFO:root:Finished
|
howto/logging,,:Finished,INFO:root:Finished
|
||||||
howto/logging,,:logger,severity:logger name:message
|
howto/logging,,:logger,severity:logger name:message
|
||||||
|
@ -90,6 +91,7 @@ howto/logging,,:root,DEBUG:root:This message should go to the log file
|
||||||
howto/logging,,:root,INFO:root:Doing something
|
howto/logging,,:root,INFO:root:Doing something
|
||||||
howto/logging,,:root,INFO:root:Finished
|
howto/logging,,:root,INFO:root:Finished
|
||||||
howto/logging,,:root,INFO:root:So should this
|
howto/logging,,:root,INFO:root:So should this
|
||||||
|
howto/logging,,:root,"ERROR:root:And non-ASCII stuff, too, like "
|
||||||
howto/logging,,:root,INFO:root:Started
|
howto/logging,,:root,INFO:root:Started
|
||||||
howto/logging,,:root,"WARNING:root:And this, too"
|
howto/logging,,:root,"WARNING:root:And this, too"
|
||||||
howto/logging,,:root,WARNING:root:Look before you leap!
|
howto/logging,,:root,WARNING:root:Look before you leap!
|
||||||
|
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2001-2017 by Vinay Sajip. All Rights Reserved.
|
# Copyright 2001-2019 by Vinay Sajip. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose and without fee is hereby granted,
|
# documentation for any purpose and without fee is hereby granted,
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
Logging package for Python. Based on PEP 282 and comments thereto in
|
Logging package for Python. Based on PEP 282 and comments thereto in
|
||||||
comp.lang.python.
|
comp.lang.python.
|
||||||
|
|
||||||
Copyright (C) 2001-2017 Vinay Sajip. All Rights Reserved.
|
Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.
|
||||||
|
|
||||||
To use, simply 'import logging' and log away!
|
To use, simply 'import logging' and log away!
|
||||||
"""
|
"""
|
||||||
|
@ -1122,7 +1122,7 @@ class FileHandler(StreamHandler):
|
||||||
"""
|
"""
|
||||||
A handler class which writes formatted logging records to disk files.
|
A handler class which writes formatted logging records to disk files.
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, mode='a', encoding=None, delay=False):
|
def __init__(self, filename, mode='a', encoding=None, delay=False, errors=None):
|
||||||
"""
|
"""
|
||||||
Open the specified file and use it as the stream for logging.
|
Open the specified file and use it as the stream for logging.
|
||||||
"""
|
"""
|
||||||
|
@ -1133,6 +1133,7 @@ class FileHandler(StreamHandler):
|
||||||
self.baseFilename = os.path.abspath(filename)
|
self.baseFilename = os.path.abspath(filename)
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
self.errors = errors
|
||||||
self.delay = delay
|
self.delay = delay
|
||||||
if delay:
|
if delay:
|
||||||
#We don't open the stream, but we still need to call the
|
#We don't open the stream, but we still need to call the
|
||||||
|
@ -1169,7 +1170,8 @@ class FileHandler(StreamHandler):
|
||||||
Open the current base file with the (original) mode and encoding.
|
Open the current base file with the (original) mode and encoding.
|
||||||
Return the resulting stream.
|
Return the resulting stream.
|
||||||
"""
|
"""
|
||||||
return open(self.baseFilename, self.mode, encoding=self.encoding)
|
return open(self.baseFilename, self.mode, encoding=self.encoding,
|
||||||
|
errors=self.errors)
|
||||||
|
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
"""
|
"""
|
||||||
|
@ -1928,15 +1930,20 @@ def basicConfig(**kwargs):
|
||||||
attached to the root logger are removed and closed, before
|
attached to the root logger are removed and closed, before
|
||||||
carrying out the configuration as specified by the other
|
carrying out the configuration as specified by the other
|
||||||
arguments.
|
arguments.
|
||||||
|
encoding If specified together with a filename, this encoding is passed to
|
||||||
|
the created FileHandler, causing it to be used when the file is
|
||||||
|
opened.
|
||||||
|
errors If specified together with a filename, this value is passed to the
|
||||||
|
created FileHandler, causing it to be used when the file is
|
||||||
|
opened in text mode. If not specified, the default value is
|
||||||
|
`backslashreplace`.
|
||||||
|
|
||||||
Note that you could specify a stream created using open(filename, mode)
|
Note that you could specify a stream created using open(filename, mode)
|
||||||
rather than passing the filename and mode in. However, it should be
|
rather than passing the filename and mode in. However, it should be
|
||||||
remembered that StreamHandler does not close its stream (since it may be
|
remembered that StreamHandler does not close its stream (since it may be
|
||||||
using sys.stdout or sys.stderr), whereas FileHandler closes its stream
|
using sys.stdout or sys.stderr), whereas FileHandler closes its stream
|
||||||
when the handler is closed.
|
when the handler is closed.
|
||||||
|
|
||||||
.. versionchanged:: 3.8
|
|
||||||
Added the ``force`` parameter.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
Added the ``style`` parameter.
|
Added the ``style`` parameter.
|
||||||
|
|
||||||
|
@ -1946,12 +1953,20 @@ def basicConfig(**kwargs):
|
||||||
``filename``/``filemode``, or ``filename``/``filemode`` specified
|
``filename``/``filemode``, or ``filename``/``filemode`` specified
|
||||||
together with ``stream``, or ``handlers`` specified together with
|
together with ``stream``, or ``handlers`` specified together with
|
||||||
``stream``.
|
``stream``.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.8
|
||||||
|
Added the ``force`` parameter.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
Added the ``encoding`` and ``errors`` parameters.
|
||||||
"""
|
"""
|
||||||
# Add thread safety in case someone mistakenly calls
|
# Add thread safety in case someone mistakenly calls
|
||||||
# basicConfig() from multiple threads
|
# basicConfig() from multiple threads
|
||||||
_acquireLock()
|
_acquireLock()
|
||||||
try:
|
try:
|
||||||
force = kwargs.pop('force', False)
|
force = kwargs.pop('force', False)
|
||||||
|
encoding = kwargs.pop('encoding', None)
|
||||||
|
errors = kwargs.pop('errors', 'backslashreplace')
|
||||||
if force:
|
if force:
|
||||||
for h in root.handlers[:]:
|
for h in root.handlers[:]:
|
||||||
root.removeHandler(h)
|
root.removeHandler(h)
|
||||||
|
@ -1970,7 +1985,10 @@ def basicConfig(**kwargs):
|
||||||
filename = kwargs.pop("filename", None)
|
filename = kwargs.pop("filename", None)
|
||||||
mode = kwargs.pop("filemode", 'a')
|
mode = kwargs.pop("filemode", 'a')
|
||||||
if filename:
|
if filename:
|
||||||
h = FileHandler(filename, mode)
|
if 'b'in mode:
|
||||||
|
errors = None
|
||||||
|
h = FileHandler(filename, mode,
|
||||||
|
encoding=encoding, errors=errors)
|
||||||
else:
|
else:
|
||||||
stream = kwargs.pop("stream", None)
|
stream = kwargs.pop("stream", None)
|
||||||
h = StreamHandler(stream)
|
h = StreamHandler(stream)
|
||||||
|
|
|
@ -48,13 +48,16 @@ class BaseRotatingHandler(logging.FileHandler):
|
||||||
Not meant to be instantiated directly. Instead, use RotatingFileHandler
|
Not meant to be instantiated directly. Instead, use RotatingFileHandler
|
||||||
or TimedRotatingFileHandler.
|
or TimedRotatingFileHandler.
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, mode, encoding=None, delay=False):
|
def __init__(self, filename, mode, encoding=None, delay=False, errors=None):
|
||||||
"""
|
"""
|
||||||
Use the specified filename for streamed logging
|
Use the specified filename for streamed logging
|
||||||
"""
|
"""
|
||||||
logging.FileHandler.__init__(self, filename, mode, encoding, delay)
|
logging.FileHandler.__init__(self, filename, mode=mode,
|
||||||
|
encoding=encoding, delay=delay,
|
||||||
|
errors=errors)
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
self.errors = errors
|
||||||
self.namer = None
|
self.namer = None
|
||||||
self.rotator = None
|
self.rotator = None
|
||||||
|
|
||||||
|
@ -117,7 +120,8 @@ class RotatingFileHandler(BaseRotatingHandler):
|
||||||
Handler for logging to a set of files, which switches from one file
|
Handler for logging to a set of files, which switches from one file
|
||||||
to the next when the current file reaches a certain size.
|
to the next when the current file reaches a certain size.
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False):
|
def __init__(self, filename, mode='a', maxBytes=0, backupCount=0,
|
||||||
|
encoding=None, delay=False, errors=None):
|
||||||
"""
|
"""
|
||||||
Open the specified file and use it as the stream for logging.
|
Open the specified file and use it as the stream for logging.
|
||||||
|
|
||||||
|
@ -145,7 +149,8 @@ class RotatingFileHandler(BaseRotatingHandler):
|
||||||
# on each run.
|
# on each run.
|
||||||
if maxBytes > 0:
|
if maxBytes > 0:
|
||||||
mode = 'a'
|
mode = 'a'
|
||||||
BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
|
BaseRotatingHandler.__init__(self, filename, mode, encoding=encoding,
|
||||||
|
delay=delay, errors=errors)
|
||||||
self.maxBytes = maxBytes
|
self.maxBytes = maxBytes
|
||||||
self.backupCount = backupCount
|
self.backupCount = backupCount
|
||||||
|
|
||||||
|
@ -196,8 +201,11 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
|
||||||
If backupCount is > 0, when rollover is done, no more than backupCount
|
If backupCount is > 0, when rollover is done, no more than backupCount
|
||||||
files are kept - the oldest ones are deleted.
|
files are kept - the oldest ones are deleted.
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
|
def __init__(self, filename, when='h', interval=1, backupCount=0,
|
||||||
BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
|
encoding=None, delay=False, utc=False, atTime=None,
|
||||||
|
errors=None):
|
||||||
|
BaseRotatingHandler.__init__(self, filename, 'a', encoding=encoding,
|
||||||
|
delay=delay, errors=errors)
|
||||||
self.when = when.upper()
|
self.when = when.upper()
|
||||||
self.backupCount = backupCount
|
self.backupCount = backupCount
|
||||||
self.utc = utc
|
self.utc = utc
|
||||||
|
@ -431,8 +439,11 @@ class WatchedFileHandler(logging.FileHandler):
|
||||||
This handler is based on a suggestion and patch by Chad J.
|
This handler is based on a suggestion and patch by Chad J.
|
||||||
Schroeder.
|
Schroeder.
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, mode='a', encoding=None, delay=False):
|
def __init__(self, filename, mode='a', encoding=None, delay=False,
|
||||||
logging.FileHandler.__init__(self, filename, mode, encoding, delay)
|
errors=None):
|
||||||
|
logging.FileHandler.__init__(self, filename, mode=mode,
|
||||||
|
encoding=encoding, delay=delay,
|
||||||
|
errors=errors)
|
||||||
self.dev, self.ino = -1, -1
|
self.dev, self.ino = -1, -1
|
||||||
self._statstream()
|
self._statstream()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2001-2017 by Vinay Sajip. All Rights Reserved.
|
# Copyright 2001-2019 by Vinay Sajip. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose and without fee is hereby granted,
|
# documentation for any purpose and without fee is hereby granted,
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
"""Test harness for the logging module. Run all tests.
|
"""Test harness for the logging module. Run all tests.
|
||||||
|
|
||||||
Copyright (C) 2001-2017 Vinay Sajip. All Rights Reserved.
|
Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -4445,6 +4445,99 @@ class BasicConfigTest(unittest.TestCase):
|
||||||
self.assertEqual(new_string_io.getvalue().strip(),
|
self.assertEqual(new_string_io.getvalue().strip(),
|
||||||
'WARNING:root:warn\nINFO:root:info')
|
'WARNING:root:warn\nINFO:root:info')
|
||||||
|
|
||||||
|
def test_encoding(self):
|
||||||
|
try:
|
||||||
|
encoding = 'utf-8'
|
||||||
|
logging.basicConfig(filename='test.log', encoding=encoding,
|
||||||
|
errors='strict',
|
||||||
|
format='%(message)s', level=logging.DEBUG)
|
||||||
|
|
||||||
|
self.assertEqual(len(logging.root.handlers), 1)
|
||||||
|
handler = logging.root.handlers[0]
|
||||||
|
self.assertIsInstance(handler, logging.FileHandler)
|
||||||
|
self.assertEqual(handler.encoding, encoding)
|
||||||
|
logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
|
||||||
|
finally:
|
||||||
|
handler.close()
|
||||||
|
with open('test.log', encoding='utf-8') as f:
|
||||||
|
data = f.read().strip()
|
||||||
|
os.remove('test.log')
|
||||||
|
self.assertEqual(data,
|
||||||
|
'The Øresund Bridge joins Copenhagen to Malmö')
|
||||||
|
|
||||||
|
def test_encoding_errors(self):
|
||||||
|
try:
|
||||||
|
encoding = 'ascii'
|
||||||
|
logging.basicConfig(filename='test.log', encoding=encoding,
|
||||||
|
errors='ignore',
|
||||||
|
format='%(message)s', level=logging.DEBUG)
|
||||||
|
|
||||||
|
self.assertEqual(len(logging.root.handlers), 1)
|
||||||
|
handler = logging.root.handlers[0]
|
||||||
|
self.assertIsInstance(handler, logging.FileHandler)
|
||||||
|
self.assertEqual(handler.encoding, encoding)
|
||||||
|
logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
|
||||||
|
finally:
|
||||||
|
handler.close()
|
||||||
|
with open('test.log', encoding='utf-8') as f:
|
||||||
|
data = f.read().strip()
|
||||||
|
os.remove('test.log')
|
||||||
|
self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
|
||||||
|
|
||||||
|
def test_encoding_errors_default(self):
|
||||||
|
try:
|
||||||
|
encoding = 'ascii'
|
||||||
|
logging.basicConfig(filename='test.log', encoding=encoding,
|
||||||
|
format='%(message)s', level=logging.DEBUG)
|
||||||
|
|
||||||
|
self.assertEqual(len(logging.root.handlers), 1)
|
||||||
|
handler = logging.root.handlers[0]
|
||||||
|
self.assertIsInstance(handler, logging.FileHandler)
|
||||||
|
self.assertEqual(handler.encoding, encoding)
|
||||||
|
self.assertEqual(handler.errors, 'backslashreplace')
|
||||||
|
logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
|
||||||
|
finally:
|
||||||
|
handler.close()
|
||||||
|
with open('test.log', encoding='utf-8') as f:
|
||||||
|
data = f.read().strip()
|
||||||
|
os.remove('test.log')
|
||||||
|
self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
|
||||||
|
r'Bridge joins Copenhagen to Malm\xf6')
|
||||||
|
|
||||||
|
def test_encoding_errors_none(self):
|
||||||
|
# Specifying None should behave as 'strict'
|
||||||
|
try:
|
||||||
|
encoding = 'ascii'
|
||||||
|
logging.basicConfig(filename='test.log', encoding=encoding,
|
||||||
|
errors=None,
|
||||||
|
format='%(message)s', level=logging.DEBUG)
|
||||||
|
|
||||||
|
self.assertEqual(len(logging.root.handlers), 1)
|
||||||
|
handler = logging.root.handlers[0]
|
||||||
|
self.assertIsInstance(handler, logging.FileHandler)
|
||||||
|
self.assertEqual(handler.encoding, encoding)
|
||||||
|
self.assertIsNone(handler.errors)
|
||||||
|
|
||||||
|
message = []
|
||||||
|
|
||||||
|
def dummy_handle_error(record):
|
||||||
|
_, v, _ = sys.exc_info()
|
||||||
|
message.append(str(v))
|
||||||
|
|
||||||
|
handler.handleError = dummy_handle_error
|
||||||
|
logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
|
||||||
|
self.assertTrue(message)
|
||||||
|
self.assertIn("'ascii' codec can't encode "
|
||||||
|
"character '\\xd8' in position 4:", message[0])
|
||||||
|
finally:
|
||||||
|
handler.close()
|
||||||
|
with open('test.log', encoding='utf-8') as f:
|
||||||
|
data = f.read().strip()
|
||||||
|
os.remove('test.log')
|
||||||
|
# didn't write anything due to the encoding error
|
||||||
|
self.assertEqual(data, r'')
|
||||||
|
|
||||||
|
|
||||||
def _test_log(self, method, level=None):
|
def _test_log(self, method, level=None):
|
||||||
# logging.root has no handlers so basicConfig should be called
|
# logging.root has no handlers so basicConfig should be called
|
||||||
called = []
|
called = []
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Added ``encoding`` and ``errors`` keyword parameters to ``logging.basicConfig``.
|
Loading…
Reference in New Issue