Skip to content

Commit 7ff1d00

Browse files
First attemt at a section on logging.
1 parent 4db76aa commit 7ff1d00

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

docs/writing/logging.rst

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
Logging
2+
=======
3+
4+
The :mod:`logging` module has been a part of Python's Standard Library since
5+
version 2.3. It is succinctly described in :pep:`282`. The documentation
6+
is notoriously hard to read, except for the `basic logging tutorial`_,
7+
and often less useful than simply reading the source code.
8+
9+
Logging serves two purposes:
10+
11+
- **Diagnostic logging** records events related to the application's
12+
operation. If a user calls in to report an error, for example, the logs
13+
can be searched for context.
14+
- **Audit logging** records events for business analysis. A user's
15+
transactions can be extracted and combined with other user details for
16+
reports or to optimize a business goal.
17+
18+
19+
... or Print Statements?
20+
------------------------
21+
22+
The only time that ``print`` is a better option than logging is when
23+
the goal is to display a help statement for a command line application.
24+
Other reasons why logging is better than ``print``:
25+
26+
- The `log record`_, which is created with every logging event, contains
27+
readily available diagnostic information such as the file name,
28+
full path, function, and line number of the logging event.
29+
- Events logged in included modules are automatically accessible via the
30+
root logger
31+
to your application's logging stream, unless you filter them out.
32+
- Logging can be selectively silenced or disabled by using the method
33+
:meth:`logging.Logger.setLevel` or setting the attribute
34+
:attr:`logging.Logger.disabled` to ``True``.
35+
36+
37+
Logging in a Library
38+
--------------------
39+
40+
Notes for `configuring logging for a library`_ are in the
41+
`basic logging tutorial`_. Because the *user*, not the library, should
42+
dictate what happens when a logging event occurs, One admonition bears
43+
repeating:
44+
45+
.. note::
46+
It is strongly advised that you do not add any handlers other than
47+
NullHandler to your library’s loggers.
48+
49+
50+
Best practice when instantiating loggers in a library is to only create them
51+
using the ``__name__`` global variable: the :mod:`logging` module creates a
52+
hierarchy of loggers using dot notation, so using ``__name__`` ensures
53+
no name collisions.
54+
55+
Here is an example of best practice from the `requests source`_ -- place
56+
this in your ``__init__.py``
57+
58+
.. code-block:: python
59+
60+
# Set default logging handler to avoid "No handler found" warnings.
61+
import logging
62+
try: # Python 2.7+
63+
from logging import NullHandler
64+
except ImportError:
65+
class NullHandler(logging.Handler):
66+
def emit(self, record):
67+
pass
68+
69+
logging.getLogger(__name__).addHandler(NullHandler())
70+
71+
72+
73+
Logging in an Application
74+
-------------------------
75+
76+
The `twelve factor app's <http://12factor.net>`_, an authoritative reference
77+
for good practice in application development, contains a section on
78+
`logging best practice <http://12factor.net/logs>`_. It emphatically
79+
advocates for treating log events as an event stream, and for
80+
sending that event stream to standard output to be handled by the
81+
application environment. Do that.
82+
83+
84+
There are at least three ways to configure a logger:
85+
86+
- using a file (recommended)
87+
- using a dictionary
88+
- using code
89+
90+
Here is how with a file -- let us say it is named ``logging_config.txt``:
91+
92+
.. code-block:: none
93+
94+
[loggers]
95+
keys=root
96+
97+
[handlers]
98+
keys=stream_handler
99+
100+
[formatters]
101+
keys=formatter
102+
103+
[logger_root]
104+
level=DEBUG
105+
handlers=stream_handler
106+
107+
[handler_stream_handler]
108+
class=StreamHandler
109+
level=DEBUG
110+
formatter=formatter
111+
args=(sys.stderr,)
112+
113+
[formatter_formatter]
114+
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
115+
116+
117+
Then use :meth:`logging.config.fileConfig` in the code:
118+
119+
.. code-block:: python
120+
121+
import logging
122+
from logging.config import fileConfig
123+
124+
fileConfig('logging_config.txt')
125+
logger = logging.getLogger()
126+
logger.debug('often makes a very good meal of %s', 'visiting tourists')
127+
128+
129+
..
130+
As of Python 2.7, you can use a dictionary with configuration details:
131+
132+
.. code-block:: python
133+
134+
import logging
135+
from logging.config import dictConfig
136+
137+
logging_config = dict(
138+
version = 1,
139+
formatters = {
140+
'f': {'format':
141+
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
142+
},
143+
handlers = {
144+
'h': {'class': 'logging.StreamHandler',
145+
'formatter': 'f',
146+
'level': logging.DEBUG}
147+
},
148+
loggers = {
149+
root : {'handlers': ['h'],
150+
'level': logging.DEBUG}
151+
}
152+
)
153+
154+
dictConfig(logging_config)
155+
156+
logger = logging.getLogger()
157+
logger.debug('often makes a very good meal of %s', 'visiting tourists')
158+
159+
160+
Or instantiate the logger directly in code:
161+
162+
.. code-block:: python
163+
164+
import logging
165+
166+
logger = logging.getLogger()
167+
handler = logging.StreamHandler()
168+
formatter = logging.Formatter(
169+
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
170+
handler.setFormatter(formatter)
171+
logger.addHandler(handler)
172+
logger.setLevel(logging.DEBUG)
173+
174+
logger.debug('often makes a very good meal of %s', 'visiting tourists')
175+
176+
177+
.. _basic logging tutorial: http://docs.python.org/howto/logging.html#logging-basic-tutorial
178+
.. _configuring logging for a library: https://docs.python.org/howto/logging.html#configuring-logging-for-a-library
179+
.. _log record: https://docs.python.org/library/logging.html#logrecord-attributes
180+
.. _requests source: https://github.com/kennethreitz/requests

0 commit comments

Comments
 (0)