Python Logging - Under The Hood
Python Logging - Under The Hood
Understanding Python's
logging library
Agenda
This post aims at making you aware about the internals of the
Python logging module.
I often find myself struggling to use the logging module and have to
refer the documentation. It's either my ignorance towards logging or
probably logging isn't documented as well and crisply as it should
be.
the hood,
UrbanPiper Blog there is a higher likelihood that you don't haveShare
to refer
this the
Logging components
Python logging module can be categorized into four broad
components:
Formatter
Filter
Handler
Logger
LogRecord
https://blog.urbanpiper.com/understanding-python-logging-library/ 2/21
27/11/2020 Python logging - under the hood
Python
UrbanPiper Blog logging module has a class called LogRecord . It can bethis
Share
https://blog.urbanpiper.com/understanding-python-logging-library/ 3/21
27/11/2020 Python logging - under the hood
In [45]: type(record)
Out[45]: logging.LogRecord
In [46]: record.name
Out[46]: 'first record'
In [47]: record.msg
Out[47]: 'User registered'
In [48]: record.args
Out[48]: ()
In [49]: record.levelname
Out[49]: 20
Severity levels
Let's talk a bit on the severity levels and the constants used to
represent them.
logging.DEBUG
logging.INFO
logging.WARNING
makeLogRecord
Coming back to how we created a LogRecord using makeLogRecord .
In [55]: record.msg
Out[55]: 'User registered with username %s'
In [56]: record.args
Out[56]: ('steve',)
In [57]: record.getMessage()
Out[57]: 'User registered with username steve'
https://blog.urbanpiper.com/understanding-python-logging-library/ 5/21
27/11/2020 Python logging - under the hood
Let'sBlog
UrbanPiper create another record. Share this
In [63]: record.getMessage()
Out[63]: 'User registered with username steve. Assigned id is 5'
Formatter
You would expect a control over the format of your event logs.
Similarly, sometimes you might want the event time to precede the
severity level while sometime you might want time info to show after
the severity level.
In [73]: formatter.format(record)
Out[73]: 'Name: user.username Level: INFO Message: User registered with u
https://blog.urbanpiper.com/understanding-python-logging-library/ 6/21
27/11/2020 Python logging - under the hood
Let's create another Formatter and log using the formatter. Let's use
levelno instead of levelname this time.
In [77]: formatter.format(record)
Out[77]: 'Name: user.username Level: 20 Message: User registered with use
name
args
levelname
levelno
pathname
filename
module
lineno
funcName
thread
https://blog.urbanpiper.com/understanding-python-logging-library/ 7/21
27/11/2020 Python logging - under the hood
UrbanPiperthreadName
Blog Share this
processName
In [82]: formatter.format(record)
Out[82]: 'Time: 2020-03-05 01:45:10,295 Name: user.username Level: 20 Mes
In [90]: formatter.format(record)
Out[90]: 'Time: March 05th 01:45 AM Name: user.username Level: 20 Message
https://blog.urbanpiper.com/understanding-python-logging-library/ 8/21
27/11/2020 Python logging - under the hood
Handler
UrbanPiper Blog Share this
A handler decides how the output of a particular logrecord should be
handled. A logrecord could be handled in multiple ways. Some
possible ways are:
In [93]: handler.handle(record)
User registered with username steve. Assigned id is 5
Out[93]: 1
https://blog.urbanpiper.com/understanding-python-logging-library/ 9/21
27/11/2020 Python logging - under the hood
In [96]: handler.handle(record)
Time: March 05th 01:45 AM Name: user.username Level: 20 Message: User reg
Out[96]: 1
Handlers can also have multiple filters associated with them. Let's
create a custom filter which can dictate to handler to log only events
with severity equal to higher than logging.INFO .
In [109]: handler.addFilter(severity_filter)
In [110]: handler.handle(record)
Out[110]: 0
The record wasn't logged here because the severity of this record is
DEBUG and our filter does not allow the handler to log events with a
severity less than INFO .
https://blog.urbanpiper.com/understanding-python-logging-library/ 10/21
27/11/2020 Python logging - under the hood
Let'sBlog
UrbanPiper create another record with severity of INFO and handle
Shareit using
this
the handler.
In [112]: handler.handle(record)
User registered with username steve. Assigned id is 5
Out[112]: 1
Logger
The final component of the logging is Logger . Docstring of Logger
defines it as:
Severity level
Multiple filters
Multiple handlers
https://blog.urbanpiper.com/understanding-python-logging-library/ 11/21
27/11/2020 Python logging - under the hood
UrbanPiper
If anyBlog
of Share this
the associated filters of the logger return False, then the
record is ignored.
Let's first create a simple logger without any explicit level and log
few messages.
In [7]: logger.addHandler(handler)
Internally, a LogRecord was created with msg as This code flow kicks
https://blog.urbanpiper.com/understanding-python-logging-library/ 12/21
27/11/2020 Python logging - under the hood
in . The
UrbanPiper Blogcreated logrecord was passed to . Since
handler.handle()Share this we
did not associate a formatter with handler, so the handler used the
default formatter and the logrecord was logged.
In [13]: logger.addHandler(handler)
In this case, the severity level of logger is INFO while we are trying to
log a record with severity level DEBUG . Since level DEBUG is less than
INFO , so it wasn't logged.
https://blog.urbanpiper.com/understanding-python-logging-library/ 13/21
27/11/2020 Python logging - under the hood
In [20]: logger.addHandler(StreamHandler())
In [21]: logger.addFilter(filter)
Our filter doesn't allow a logrecord with severity less than WARNING to
be logged.
As expected, trying to log a record with severity level less than WARNI
NG were ignored.
We can add multiple handlers to a logger too. eg: We can add a Strea
mHandler as well as a FileHandler to the logger.
https://blog.urbanpiper.com/understanding-python-logging-library/ 14/21
27/11/2020 Python logging - under the hood
In [20]: logger.addHandler(StreamHandler())
UrbanPiper Blog Share this
In [28]: logger.addHandler(handler)
A file called log.txt should have been created and you should be
able to see the record message written to that file.
In [30]: handler.close()
Logger Heirarchy
In last section, we explicitly created a Logger to understand what
happens under the hood.
https://blog.urbanpiper.com/understanding-python-logging-library/ 15/21
27/11/2020 Python logging - under the hood
In [11]: urbanpiper_logger.setLevel(logging.DEBUG)
In [16]: handler.setFormatter(formatter)
In [17]: urbanpiper_logger.addHandler(handler)
Without setting any handler on this child logger, let's try to log a
record.
https://blog.urbanpiper.com/understanding-python-logging-library/ 16/21
27/11/2020 Python logging - under the hood
Despite not associating any handler with the child logger, logging
system didn't complain with No handlers could be found for logger
"urbanpiper.tasks" . The handler of logger urbanpiper was invoked
instead.
This pattern comes very handy when you have a large project and
setting up the logger in each module would become cumbersome.
Consider the following project structure.
settings.py
urls.py
polls
|- __init__.py
|- views.py
|- tasks.py
|- models.py
people
|- __init__.py
|- views.py
|- tasks.py
|- models.py
Your project has two packages namely polls and people . Each
package has 3 modules each. polls has views.py , tasks.py and mod
els.py . Similarly people has 3 modules.
https://blog.urbanpiper.com/understanding-python-logging-library/ 17/21
27/11/2020 Python logging - under the hood
# polls/views.py
logger = logging.getLogger('polls.views')
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
...more code..
# polls/tasks.py
logger = logging.getLogger('polls.tasks')
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
Same way you would need to add similar boilerplate logging code in
other moduled.But with nested loggers, we can set severity level and
handler at the package level and all modules of that package will get
the level and handler for free.
# polls/__init__.py
logger = logging.getLogger('polls')
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
And only define the logger in the modules, there is no need to set the
level and handler.
https://blog.urbanpiper.com/understanding-python-logging-library/ 18/21
27/11/2020 Python logging - under the hood
# polls/tasks.py
logger = logging.getLogger('polls.tasks')
Infact we can remove the hardcoded logger names and instead use
the module name.
# polls/views.py
logger = logging.getLogger(__name__)
# polls/tasks.py
logger = logging.getLogger(__name__)
Hope you found this unraveling of the logging system insightful and
it helps you understand what goes under the hood when you use the
logging library.
https://blog.urbanpiper.com/understanding-python-logging-library/ 19/21
27/11/2020 Python logging - under the hood
— UrbanPiper Blog —
engineering
ENGINEERING
Queues have become an essential part of any high-performance web-based application. Here
UrbanPiper Blog Share this
we share some basic considerations to keep in mind while working with queues.
9 MIN READ
https://blog.urbanpiper.com/understanding-python-logging-library/ 21/21