Link Search Menu Expand Document

Step 6 Logging

Table of contents


In the first assignment, we stressed the importance of Debugging. Access to information about the program’s execution is critical in this process. This step introduces the concept of logging to improve the output algorithm with convenient ways to tune how, where, and when to inform.

What is logging

Logging is a way to communicate information when the code is running. It is a more elaborate version of the Output. In step 4 of the first assignment, we defined the output algorithm as follows:

Output Algorithm

1
Output the text [message]

We used print(message) to ‘tell’ the user in Python syntax. However, it is limited to outputting text to the Terminal. More info would help us determine what the code is doing, what went wrong, and where the unexpected behaviour occurs. And as your code grows, it becomes even harder to keep track of this information (even for a designer’s prototype!).

Suppose we want extra information, such as the incidence time or line number from the code emitting this message. In that case, we need to do that ourselves. Next, when switching from debugging to regular execution, we need to go through the code to remove or comment on each print() statement. Finally, we often want information in a file or somewhere else, instead of showing it in the Terminal. A logger can provide all these functionalities.

In short, logging is an improved version of the output algorithm. With proper logging, we can create code that is easier to debug and understand.

For a logging algorithm, we need the following elements:

For each output:

  • message what information to output
  • type when to output this information

For our program:

  • level what are we currently interested in (e.g. detailed information for debbuging, error information only)
  • format how to output the information, in addition to your message (e.g. time, line of code)
  • target where to output the information (e.g. in the Terminal, in a file)

Logging Algorithm

In the logging algorithm, we retrieve the distinction between the elements set for the whole programme and the elements used for each logs.

1
2
3
4
5
For the whole programme,
Set once the [required_level], [format] and [target] of logs

For every log, if [type] greater or equal to [level]
then write [message] as [format] to [target]

Logging Python Syntax

1
2
3
4
5
6
7
8
# Import the built-in logging module
import logging

# Set once at the top of the file
logging.basicConfig(level=logging.DEBUG)

# For every log
logging.debug("Some debugging information.")

Python has a built-in module for logging. Built-in modules provide functionalities that are typical while programming, such as logging. To use these functionalities in our code, we use the import keyword followed by the built-in module we want to use. Then we can use logging in our programme like a variable.

For instance, we want to set logging options for the whole programme, i.e. for all logs. With the logging module we do so with logging.basicConfig(). In our example, we set the level of log (hat we are interested in) to DEBUG.

Finally, we show a debug message to the user with logging.debug().

Task 6.1 Experimenting with Logging

It is a lot of new Python syntax. So let’s create a new Replit project, paste the above lines of Python and see what happens.

embed code in discourse

The result shows the message ‘Some debugging information.’. Note that it starts with ‘DEBUG:root:’, meaning that it is a log of type ‘DEBUG’ from the root file of our programme (indeed, we only have one file in our programme).

Let’s switch the logging level to ‘ERROR’ by changing the following code.

1
2
3
4
5
6
...

# Set once at the top of the file
logging.basicConfig(level=logging.ERROR)

...

Then, execute the code again.

embed code in discourse

Now, nothing happens anymore. With this setting, we tell the computer to only show logs of type ERROR or CRITICAL: the computer ignores the log of type DEBUG.

Use info() and error() to Add logs of type INFO and ERROR in the same way we wrote the debug() log, then execute your code again.

embed code in discourse

This time, we can see the error message but not the two others.

In Python, the logging levels are DEBUG, INFO, WARNING, ERROR, and CRITICAL. With DEBUG being the lowest level and CRITICAL the highest level. Experiment with the level of logs. You will notice that logs appear if they are of greater or equal importance as the log level.

Task 6.2 Logging Vending Machine

Let’s go back to our vending machine and apply the logs. To do so, you need to import the built-in logging module at the top of your code, followed by the basicConfig() statement with the log level of your choice. Then, finally, transform all print() statements with the appropriate logging equivalent.

Check the code on Replit

Task 6.3 Logging into a file

As we introduced the logging algorithm, we mentioned the target element to output the log. By default, the output appears in the Terminal as the print() statement. We can also log into a file, often helpful to keep logs over time. To change the targeted output to a specific file, we update the basicConfig() statement as follows:

1
logging.basicConfig(filename='vending_machine.log', level=logging.DEBUG)

We can see the extra parameter filename to which we assign the value ‘vending_machine.log’: the name of the targeted file.

Execute your programme again with this change. You will notice that nothing appear anymore in the Terminal (apart from input() statements). However, a file ‘vending_machine.log’ appeared in the left panel. This file should contain your logs.

embed code in discourse

Check the code on Replit

Task 6.4 Format log messages

We can also format the log message to show information differently or other information, as we mentioned in the algorithm with format. It can also be done with the basicConfig() statement by providing the format parameter. We assign a string to this format parameter.

This string can contain some placeholders. The logging module replaces them automatically with the relevant values. Let’s take the following example:

1
2
logging.basicConfig(format='%(lineno)d %(levelname)s:%(message)s',
                    level=logging.DEBUG)

Here, the output format will contain the line number (%(lineno)d), the logger level (%(levelname)s), and the logger message (%(message)s). The logging module automatically replaces these three placeholders by the relevant line number, log level and message for each log.

Everything could fit on one single line, including the format and level parameters. By convention, we write parameters on separated lines when it gets too long.

Check the code on Replit

Next: Step 7 - Recap and More