DEV Community

Favour George
Favour George

Posted on • Originally published at psycode.hashnode.dev on

Harness the Potential of Logging: Create a Python Logger from Scratch

Imagine building an application for a client and it worked successfully on your computer and also worked successfully for the client initially. Then on a fateful morning, you get a message from the client saying the application is bugging out. To debug the error on the application, you have to replicate the error and that wont be an easy job if you dont know the steps the client had taken that led to this error occurring. This is where logging and loggers come in.

What is Logging?

Logging is the process of keeping track of events occurring in your software to keep an eye out for possible bugs that survived the development phase and snuck themselves into production. Not just bugs can be tracked using logging but also how a user is using the program.

What is a Logger?

A logger is a program that creates a logging file (.log) that enables developers to write to the file errors caught during development and production. Not only error messages could be written to the file, but warnings and information could also be written to the file to help developers keep track of how their programs are being used.

Why Loggers?

Loggers are quite useful when code has been pushed into production and you cant keep track of the events occurring by simply printing to the console as you would do during development. In such a case, loggers are useful for keeping track of events occurring in the application and you can easily access the log file and see whats going on. Summarily, instead of littering your code with loads of print statements, you can simply write the message to the log file. This achieves two things: first, it keeps your codebase clean and second, it provides persistency ( i.e unlike the console that gets cleared every time the program is run, the log file is persistent. It's like the difference between storing things in a list and a database )

Now Python has a default logger module, but for this article, well build a rudimentary version of the logger. If you would like a complete tutorial on the Python Logger module, indicate in the comments. Alright, lets get started.

Building a Logger

Our logger is going to be able to log messages of 5 levels - Error, Warning, Critical, Info and Debug.

import datetime

def error(msg):
    date = datetime.datetime.now()
    with open('./logger.log', 'a') as logger:
        logger.write(f'\n [ERROR] {msg} ---- {date}'.format())

Enter fullscreen mode Exit fullscreen mode

In the code above, we create a logger.py file where well be writing the code for our logger. In our logger.py file, we first import the datetime module to help us keep track of the time in which each log is made to our log file. We then create a function error, that takes the message we wish to log to our log file as an argument. In the function, we set up the date, which is the time the user calls the function and then we open a logger.log file in append mode, meaning we write to the end of the file without altering its previous contents. We then write to the file the message, which was passed in as an argument as well as the date which was created earlier.

We then copy the function and paste it four times, changing its name to the different levels we specified at the beginning of this section as shown


def critical(msg):
    date = datetime.datetime.now()
    with open('./logger.log', 'a') as logger:
        logger.write(f'\n [CRITICAL] {msg} ---- {date}'.format())

def warning(msg):
    date = datetime.datetime.now()
    with open('./logger.log', 'a') as logger:
        logger.write(f'\n [WARNING] {msg} ---- {date}'.format())

def info(msg):
    date = datetime.datetime.now()
    with open('./logger.log', 'a') as logger:
        logger.write(f'\n [INFO] {msg} ---- {date}'.format())

def debug(msg):
    date = datetime.datetime.now()
    with open('./logger.log', 'a') as logger:
        logger.write(f'\n [DEBUG] {msg} ---- {date}'.format())

Enter fullscreen mode Exit fullscreen mode

Now to use our logger, we create a new Python file - main.py and import our logger file into it. Now for testing purposes, well write a basic script to divide a number by zero and try catching the error (ZeroDivisionError) that comes up and then log it to our logger as shown below.:

import logger

try:
    calc = 5 / 0
except ZeroDivisionError as err:
    logger.error(err)
finally:
    print('done')

Enter fullscreen mode Exit fullscreen mode

Now to see our logged message, we open the logger.log file and you should see this


 [ERROR] division by zero ---- 2023-08-06 08:45:42.013959

Enter fullscreen mode Exit fullscreen mode

You can try the other functions we defined in the logger.py file such as critical at your leisure.

Simplifying Our Code

In programming, theres a principle known as DRY ( Dont Repeat Yourself ) and its pretty... DRY.

If you are a geek for code efficiency and minimalism, youd notice that our logger.py file is quite repetitive. We can create one function that takes the level, message and logger as arguments as shown below:

import datetime

def logger(level, msg, log_file):
    date = datetime.datetime.now()
    with open(f'./{log_file}.log', 'a') as logger:
        logger.write(f'\n [{level}] {msg} ---- {date}'.format())

Enter fullscreen mode Exit fullscreen mode

The problem with this design is that every time we call the function, we have to explicitly provide the name of the log file we intend to use and it could become quite messy as we could be logging into multiple log files.

To make this project even better we could implement the singleton design pattern to our logger, now thats for another article.

So for now, weve built and used a basic Python logger, all praise to Mad Max.

Thats it for now, Ill be uploading a new tutorial on how to implement the singleton design pattern to our logger later on. Till then, dont break production.

Top comments (0)