No matter what we do in computer science, sooner or later we will need logs. Every system that has a certain size produces errors or conditions in which specific people should be warned or informed. Nowadays, everything gets logged or recorded. Bank transactions, flights, networking activities, operating systems and much more. Log files help us to find problems and to get information about the state of our systems. They are an essential tool for avoiding and understanding errors. Up until now, we have always printed some message onto the console screen when we encountered an error. But when our applications grow, this becomes confusing and we need to categorize and outsource our logs. In addition, not every message is equally relevant. Some messages are urgent because a critical component fails and some just provide nice information.
In Python, we have got five security levels. A higher level means higher importance or urgency.
DEBUG INFO WARNING ERROR CRITICAL
Notice that when we choose a certain security level, we also get all the messages of the levels above. So for example, INFO also prints the messages of WARNING, ERROR and CRITICAL but not of DEBUG . DEBUG is mainly used for tests, experiments or in order to check something. We typically use this mode, when we are looking for errors (troubleshooting). We use INFO when we want to log all the important events that inform us about what is happening. This might be something like “User A logged in successfully!” or “Now we have 17 users online!” WARNING messages are messages that inform us about irregularities and things that might go wrong and become a problem. For example messages like “Only 247 MB of RAM left!” An ERROR message gets logged or printed when something didn’t go according to the plan. When we get an exception this is a classical error. CRITICAL messages tell us that critical for the whole system or application happened. This might be the case when a crucial component fails and we have to immediately stop all operations.
In order to create a logger in Python, we need to import the logging module.
Now we can just log messages by directly using the respective functions of the logging module.
logging.info( 'First informational message!' ) logging.critical( 'This is serious!' )
This works because we are using the root logger. We haven’t created our own loggers yet. The output looks like this:
CRITICAL:root:This is serious!
INFO:root:Logger successfully created!
So let’s create our own logger now. This is done by either using the constructor of the Logger class or by using the method getLogger .
logger = logging.getLogger() logger = logging.Logger( 'MYLOGGER' )
Notice that we need to specify a name for our logger, if we use the constructor. Now we can log our messages.
logger.info( 'Logger successfully created!' ) logger.log(logging.INFO, 'Successful!' ) logger.critical( 'Critical Message!' ) logger.log(logging.CRITICAL, 'Critical!' )
Here we also have two different options for logging messages. We can either directly call the function of the respective security level or we can use the method log and specify the security level in the parameters.
But when you now execute the script, you will notice that it will only print the critical messages. This has two reasons. First of all, we need to adjust the level of the logger and second of all, we need to remove all of the handlers from the default root logger.
for handler in logging.root.handlers:
logging.root.removeHandler(handler) logging.basicConfig( level =logging.INFO)
Here we just use a for loop in order to remove all the handlers from the root logger. Then we use the basicConfig method, in order to set our logging level to INFO . When we now run our code again, the output is the following:
LOGGING INTO FILES
What we are mainly interested in is logging into files. For this, we need a so-called FileHandler . It is an object that we add to our logger, in order to make it log everything into a specific file.
import logging logger = logging.getLogger( 'MYLOGGER' ) logger.setLevel(logging.INFO) handler = logging.FileHandler( 'logfile.log' ) handler.setLevel(logging.INFO) logger.addHandler(handler) logger.info( 'Log this into the file!' ) logger.critical( 'This is critical!' )
We start again by defining a logger. Then we set the security level to INFO by using the function setLevel . After that, we create a FileHandler that logs into the file logfile.log . Here we also need to set the security level. Finally, we add the handler to our logger using the addHandler function and start logging messages.
One thing that you will notice is that we don’t have any format in our logs. We don’t know which logger was used or which security level our message has. For this, we can use a so-called formatter .
import logging logger = logging.getLogger() logger.setLevel(logging.INFO) handler = logging.FileHandler( 'logfile.log' ) handler.setLevel(logging.INFO) formatter = logging.Formatter( '%(asctime)s: %(levelname)s - %(message)s' ) handler.setFormatter(formatter) logger.addHandler(handler) logger.info( 'This will get into the file!' )
We create a formatter by using the constructor of the respective class. Then we use the keywords for the timestamp, the security level name and the message. Last but not least, we assign the formatter to our handler and start logging again. When we now look into our file, we will find a more detailed message. 2019-06-25 15:41:43,523: INFO - This will get into the file! These log messages can be very helpful, if they are used wisely. Place them wherever something important or alarming happens in your code