Stack traces are a record of errors and exceptions caused by a program. Capturing stack trace helps in debugging the code, whereas Handlers help in extending the logging capabilities such as log customization, redirection etc. Before reading this post it is recommended to read logging in python part-1 of the Python Logging series.
Clone demo from github
The logging module provides an inbuilt function which is logging.exception. This automatically logs the message with an ‘error’ log level. For demo purpose, let’s try to create an exception by printing an element out of range of an array x.
import logging x=[1,2,3] try: print x except: logging.exception("some exception occured", exc_info=True)
ERROR:root:some exception occured Traceback (most recent call last): File "
", line 2, in IndexError: list index out of range
Note that, the exception has been logged with log level ‘error’ and the logger is by default root. exc_info=True ensures that exception is logged.
We can make this more meaningful by, let’s say, printing the name of the module that caused the log, in place of ‘root’. However this is not possible when “logging.basicConfig()” is used. This is made possible by using Handlers, which not only allows you to define a custom logger but also much more such as, to redirect logs to different targets such as stdout, files, http etc.
Handlers are classes available in the logging module that can be used to customize logging. Let’s look at the below code snippet to understand handlers in Python Logging module.
#!/usr/bin/python import logging # Create a logger # the logger name is set to the name of the current module # __name__ is a built-in variable which evaluates to the name of the current module. logger = logging.getLogger(__name__) # Create handlers for stdout with custom log level, log format stdout_handler = logging.StreamHandler() stdout_handler.setLevel(logging.INFO) stdout_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s') stdout_handler.setFormatter(stdout_format) # Create handlers for logging to files file_handler = logging.FileHandler('application.log') file_handler.setLevel(logging.ERROR) file_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(file_format) # Add handlers to the logger logger.addHandler(stdout_handler) logger.addHandler(file_handler) # set default to logger as well, not just for handlers logger.setLevel(logging.DEBUG) logger.error('Some error occured') logger.info('Information')
[admin@unixutils python_logging]# ./custom_logging.py __main__ - ERROR - Some error occured __main__ - INFO - Information [admin@unixutils python_logging]# cat application.log 2019-01-07 06:45:34,702 - __main__ - ERROR - Some error occured
(line #7 of the code snippet), “getLogger()” function ensures that logger is the name of the module that is currently being run.
From our output, we see that the module loaded is ‘__main__’, that caused this log entry.
Also, The output shows that the log entries are sent to stdout and also application.log, however only entries of severity ‘info’ and above are logged to stdout and entries of severity ‘error’ is logged to application.log as defined in line #11 “stdout_handler.setLevel(logging.INFO)“ and line #17 “file_handler.setLevel(logging.ERROR)“ respectively, of the above code snippet. Also, note that in line # 26 “logger.setLevel(logging.DEBUG)“ we set the overall log level for the logger which sets the default log level, whereas the handler log levels are specific to its targets where it is sending the logs to. Also, we see that the format of the log is different for both the handlers “stdout_handler” and “file_handler”, as defined in line #12 “stdout_format = logging.Formatter(‘%(name)s – %(levelname)s – %(message)s’)“and line #18 “file_format = logging.Formatter(‘%(asctime)s – %(name)s – %(levelname)s – %(message)s’)“ respectively.