Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b2bc4df

Browse files
committed
Closes #18345: Merged documentation update from 3.3.
2 parents c1bd2fe + 2c1adcb commit b2bc4df

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

Doc/howto/logging-cookbook.rst

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,3 +1692,138 @@ When the above script is run, it prints::
16921692
Note that the order of items might be different according to the version of
16931693
Python used.
16941694

1695+
.. currentmodule:: logging.config
1696+
1697+
Customising handlers with :func:`dictConfig`
1698+
--------------------------------------------
1699+
1700+
There are times when you want to customise logging handlers in particular ways,
1701+
and if you use :func:`dictConfig` you may be able to do this without
1702+
subclassing. As an example, consider that you may want to set the ownership of a
1703+
log file. On POSIX, this is easily done using :func:`shutil.chown`, but the file
1704+
handlers in the stdlib don't offer built-in support. You can customise handler
1705+
creation using a plain function such as::
1706+
1707+
def owned_file_handler(filename, mode='a', encoding=None, owner=None):
1708+
if owner:
1709+
if not os.path.exists(filename):
1710+
open(filename, 'a').close()
1711+
shutil.chown(filename, *owner)
1712+
return logging.FileHandler(filename, mode, encoding)
1713+
1714+
You can then specify, in a logging configuration passed to :func:`dictConfig`,
1715+
that a logging handler be created by calling this function::
1716+
1717+
LOGGING = {
1718+
'version': 1,
1719+
'disable_existing_loggers': False,
1720+
'formatters': {
1721+
'default': {
1722+
'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
1723+
},
1724+
},
1725+
'handlers': {
1726+
'file':{
1727+
# The values below are popped from this dictionary and
1728+
# used to create the handler, set the handler's level and
1729+
# its formatter.
1730+
'()': owned_file_handler,
1731+
'level':'DEBUG',
1732+
'formatter': 'default',
1733+
# The values below are passed to the handler creator callable
1734+
# as keyword arguments.
1735+
'owner': ['pulse', 'pulse'],
1736+
'filename': 'chowntest.log',
1737+
'mode': 'w',
1738+
'encoding': 'utf-8',
1739+
},
1740+
},
1741+
'root': {
1742+
'handlers': ['file'],
1743+
'level': 'DEBUG',
1744+
},
1745+
}
1746+
1747+
In this example I am setting the ownership using the ``pulse`` user and group,
1748+
just for the purposes of illustration. Putting it together into a working
1749+
script, ``chowntest.py``::
1750+
1751+
import logging, logging.config, os, shutil
1752+
1753+
def owned_file_handler(filename, mode='a', encoding=None, owner=None):
1754+
if owner:
1755+
if not os.path.exists(filename):
1756+
open(filename, 'a').close()
1757+
shutil.chown(filename, *owner)
1758+
return logging.FileHandler(filename, mode, encoding)
1759+
1760+
LOGGING = {
1761+
'version': 1,
1762+
'disable_existing_loggers': False,
1763+
'formatters': {
1764+
'default': {
1765+
'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
1766+
},
1767+
},
1768+
'handlers': {
1769+
'file':{
1770+
# The values below are popped from this dictionary and
1771+
# used to create the handler, set the handler's level and
1772+
# its formatter.
1773+
'()': owned_file_handler,
1774+
'level':'DEBUG',
1775+
'formatter': 'default',
1776+
# The values below are passed to the handler creator callable
1777+
# as keyword arguments.
1778+
'owner': ['pulse', 'pulse'],
1779+
'filename': 'chowntest.log',
1780+
'mode': 'w',
1781+
'encoding': 'utf-8',
1782+
},
1783+
},
1784+
'root': {
1785+
'handlers': ['file'],
1786+
'level': 'DEBUG',
1787+
},
1788+
}
1789+
1790+
logging.config.dictConfig(LOGGING)
1791+
logger = logging.getLogger('mylogger')
1792+
logger.debug('A debug message')
1793+
1794+
To run this, you will probably need to run as ``root``::
1795+
1796+
$ sudo python3.3 chowntest.py
1797+
$ cat chowntest.log
1798+
2013-11-05 09:34:51,128 DEBUG mylogger A debug message
1799+
$ ls -l chowntest.log
1800+
-rw-r--r-- 1 pulse pulse 55 2013-11-05 09:34 chowntest.log
1801+
1802+
Note that this example uses Python 3.3 because that's where :func:`shutil.chown`
1803+
makes an appearance. This approach should work with any Python version that
1804+
supports :func:`dictConfig` - namely, Python 2.7, 3.2 or later. With pre-3.3
1805+
versions, you would need to implement the actual ownership change using e.g.
1806+
:func:`os.chown`.
1807+
1808+
In practice, the handler-creating function may be in a utility module somewhere
1809+
in your project. Instead of the line in the configuration::
1810+
1811+
'()': owned_file_handler,
1812+
1813+
you could use e.g.::
1814+
1815+
'()': 'ext://project.util.owned_file_handler',
1816+
1817+
where ``project.util`` can be replaced with the actual name of the package
1818+
where the function resides. In the above working script, using
1819+
``'ext://__main__.owned_file_handler'`` should work. Here, the actual callable
1820+
is resolved by :func:`dictConfig` from the ``ext://`` specification.
1821+
1822+
This example hopefully also points the way to how you could implement other
1823+
types of file change - e.g. setting specific POSIX permission bits - in the
1824+
same way, using :func:`os.chmod`.
1825+
1826+
Of course, the approach could also be extended to types of handler other than a
1827+
:class:`~logging.FileHandler` - for example, one of the rotating file handlers,
1828+
or a different type of handler altogether.
1829+

0 commit comments

Comments
 (0)