Logging in Python

Logging in Python

Python has a very powerful logging system. Adding logging to a Python program is as simple as:

In [1]:
import logging 
logger = logging.getLogger('__name__')
def function_that_logs(x, y):
    logger.info(f"I was called with x={x}, y={y}")

However, calling function_that_logs won't print the logged message anywhere yet:

In [4]:
function_that_logs(12, 13)

This is because unless explicitly enabled, python ignores logging calls. Otherwise calling codes would be inundated with logging messages from various libraries. There are a few steps to do before our logged message gets anywhere.

In [5]:
# Set the logger level. Anything logged with level lower than DEBUG will be ignored. 
logger.setLevel(logging.DEBUG)
# Now add a handler to our logger. StreamHandler prints to stderr
handler = logging.StreamHandler()
# Now the handler's level must be set too.
handler.setLevel(logging.DEBUG)
# Add the handler to the logger
logger.addHandler(handler)
In [7]:
function_that_logs(12, 13)
I was called with x=12, y=13

Finally, our logged message appears. A logger can have multiple handlers in Python: logging module contains many kinds of handlers, e.g. HTTPHandler (sends logs to a web server), SMTPHandler (sends the log via email) etc.

Custom formatters

We might sometimes want some additional information to be logged alongwith the message, for example the time the log was created. This can be achieved by setting the formatter of our log handler:

In [31]:
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s')
handler.setFormatter(formatter)
logger.handlers = []
logger.addHandler(handler)

Where do the properties like asctime or funcName come from? They are all attributes of the Logrecord objects. On each logging call, such a LogRecord object is created, and they contain a lot of information including the calling code's module name, filepath, line number etc.

In [12]:
class CustomFormatter(logging.Formatter):
    def format(self, record):
        return "Hello"
In [13]:
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
formatter = CustomFormatter()
handler.setFormatter(formatter)
logger.addHandler(handler)
In [14]:
function_that_logs(12, 13)
I was called with x=12, y=13
Hello
In [ ]: