Python 101 - Creating a Logging Decorator
Logging boilerplate can make adding logging to your code more complex. One workaround is to wrap up your logging code in a decorator. Then, you can add a decorator to any functions that you want to add logging to.
This chapter will cover the following topics:
What is a decorator?
Creating an exception logging decorator
Passing a logger to a decorator
You will focus on adding an exception logging decorator. However, you can extend or modify the decorator to log more than exceptions. That part is left as an excercise for you.
Let’s get started!
What is a Decorator?
A decorator is a function that takes another function as its argument. The decorator function will then extend the functionality of the function that it is decorating in some way. In the case of logging, you will create a decorator that catches exceptions and logs them if they occur in the decorated function.
That sounds like double-talk. Writing a decorator helps you to see how they work.
Open up your Python editor and create a file named hello_decorator.py, then enter the following code:
The info() function above takes in a function as its sole argument. Python implicitly passes in the function’s arguments, so you see a *args in the nested wrapper() function inside of info(). The wrapper() function extends the functionality of the function being decorated.
In this case, you print out the function’s name and docstring, so it’s not an enhancement to the function. However, this example demonstrates how decorators work, and you can log the decorated function here if you want to.
When you run this code, you will see the following printed out:
Function name: doubler
Function docstring: Doubles the number passed to it
8
Note that decorators will replace the function’s name and docstring with the decorator’s name and function name. You can fix this behavior with Python’s functools.wraps.
To demonstrate this, replace your code’s print() function with the following: print(doubler.__name__). When you run this, you will see that it prints out “wrapper” instead of “doubler”.
Here’s how to fix that issue with functools:
All you need to do is import functools and add a decorator line to the nested wrapper() function inside info(). When you do this, you need to pass the func reference to @functools.wraps() to make it work.
Now let’s move on and learn how to create a logging decorator!
Creating an Exception Logging Decorator
Creating a decorator that logs an exception will use much of what you’ve learned throughout this book. You will use Python’s logging API to create a logger object and log to a file using a specific formatter.
To start, open up your Python IDE or text editor and create a new file named exception_decor.py. Then enter the following code:
The create_logger() method uses the logging API to create an “example_logger” object that writes out to test.log. You also make a formatter using a format string you have seen several times. The last line of the function returns the logger object.
The exception function is your decorator. This function wraps a call to the function you are decorating with an exception handler (i.e., try / except) to catch any exceptions the decorated function might throw.
Your decorator emits a log message that includes the decorated function’s name and the exception information.
Now, you need to test that the decorator works. In your Python IDE, create a second file named main.py and save it in the same location as exception_decor.py. Then add the following code:
The first step is to import your exception decorator. Then, you apply the decorator to the zero_divide() function, which foolishly divides an integer by zero. Python raises an exception when you divide by zero. The exception decorator should catch the exception and log it.
Try running the code in your terminal using python main.py, and you should see something like the following in your test.log file:
Nice! You caught the exception and logged it successfully.
Passing a Logger to a Decorator
Complex code might have multiple logger objects. If that’s the case, you will want to make your decorator be able to take in a logger object as an argument.
You will re-use much of the code from the previous example with a few small tweaks.
The first step is to open your Python IDE and create a new file named exception_logger.py. In that file, you will put all your logger API code.
Here’s the code:
This code is nearly identical to the previous logger API code you had earlier. The difference here is
The code is in a separate module and
The logger instance is created at the end
Now, create the exception_decor.py file and save it in the same location as your exception_logger.py file. The decorator code needs to be updated to take in an argument.
Here’s the code that will allow that:
The decorator now has two functions nested inside of it instead of one. The exception() function uses the logger object as its argument. The decorator() function inside of exception() takes in the implicit function that exception() is decorating. The wrapper() function takes in the args and kwargs for the function that was implicitly passed in.
Otherwise, the code is the same as it was before.
The last step is to create a new main.py file in your Python IDE or text editor. The code is slightly different, so make sure you check it over:
This main.py imports your logger object and custom exception decorator. You pass in your logger object to the exception() decorator to be able to use it. You could create a different logger object and use it with the exception() decorator on a different function, which makes this coding pattern handy.
It would help if you created other functions that raise different exceptions as practice. Then, write up a couple of logger objects and pass them to each decorated function to see how flexible this is.
Wrapping Up
Decorators are powerful tools for extending regular functions. In a logging context, they can add logging to functions and methods without needing to import or configure logging in the module you decorate. All you need is the decorator.
In this chapter, you covered the following topics related to decorators and logging:
What is a decorator?
Creating an exception logging decorator
Passing a logger to a decorator
Using decorators can make your code cleaner. The downside is that the tracebacks that occur can sometimes be more complex. However, decorators are a great design pattern to try. Just don’t overdo it!
Sponsor
Viktor is your new AI hire! It can help you grow your business by analyzing your Slack or Teams Channels. Give it work to do, have it respond to questions, create reports, and more









