Python provides a built-in library called logging to make it easy to add logging to any module or application in a unified way. Let's see how can we use it.

Simple logging

The most simple way to get started with logging is to import the logging module and then call the various logging methods on it. There are 5 primary levels of logging. Each one has a corresponding numeric value and a method to log on that level.

In addition we can call the log method passing to it the level as provided by the appropriate name in upper case (logging.WARNING in our example) or we can even pass the numerical value by ourself, though that makes of a much less readable code.

examples/python/simple_logging.py

import logging

logging.debug("debug") 
logging.info("info") 
logging.warning("warning") 
logging.error("error")
logging.critical("critical")

logging.log(logging.WARNING, "another warning")
logging.log(40, "another error")



If we run the above script we get the following output:

WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical
WARNING:root:another warning
ERROR:root:another error

  • By default the logging module only displays the levels WARNING and up.
  • By default it prints then to the Standard Error channel (STDERR).
  • The format is LEVEL followed by the name of the default logger (root), followed by the message

Basic Configuration: Set the log level for logging in Python

We can easily adjust the level of logging to be INFO and above by calling the basicConfig method:

examples/python/simple_logging_set_level.py

import logging

logging.basicConfig(level = logging.INFO)

logging.debug("debug") 
logging.info("info") 
logging.warning("warning") 
logging.error("error")
logging.critical("critical")

The output will be:

INFO:root:info
WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical

Send log to a file

We can also configure the logging module to save the log messages in a file, instead of the STDERR by adding the filename parameter to the basicConfig method call:

logging.basicConfig(level = logging.INFO, filename = "my.log")

This will create the log file if it does not exists and append the content of the file, if it already existed.

This means the file can grow in size quite fast so a better idea might be to use some kind of a log-file rotation tool or to create log files for limited time periods. E.g. One file per day. For this we can use the time.strftime method:

examples/python/simple_logging_to_file.py

import logging
import time

logging.basicConfig(level = logging.INFO, filename = time.strftime("my-%Y-%m-%d.log"))

logging.debug("debug") 
logging.info("info") 
logging.warning("warning") 
logging.error("error")
logging.critical("critical")

This will use files in the following format: my-2018-06-12.log. Please don't use British or American date formats here. Those are really confusing and break the sorting order.

Alternatively you could change it by adding filemode = 'w' that will change the mode of operation from "append" to "write" and will overwrite the file every time we run our application. This usually is not very useful as this mean we lose all the old logs.

Formatting the log message, including date

We can replace the default log message format using the format parameter and some keywords representing the various fields supplied by the logging module:

examples/python/simple_logging_format.py

import logging

logging.basicConfig( format = '%(asctime)s  %(levelname)-10s %(processName)s  %(name)s %(message)s')

logging.debug("debug") 
logging.info("info") 
logging.warning("warning") 
logging.error("error")
logging.critical("critical")

The output will look like this:

2018-06-12 08:46:22,942  WARNING    MainProcess  root warning
2018-06-12 08:46:22,942  ERROR      MainProcess  root error
2018-06-12 08:46:22,942  CRITICAL   MainProcess  root critical

Change the date format in the log

Though I don't really recommend to change the date format, you can do that by passing the datefmt parameter with a string using the strftime tags.

examples/python/simple_logging_format_date.py

import logging

logging.basicConfig( format = '%(asctime)s  %(levelname)-10s %(processName)s  %(name)s %(message)s', datefmt =  "%Y-%m-%d-%H-%M-%S")

logging.debug("debug") 
logging.info("info") 
logging.warning("warning") 
logging.error("error")
logging.critical("critical")

The output will then change to:

2018-06-12-08-51-30  WARNING    MainProcess  root warning
2018-06-12-08-51-30  ERROR      MainProcess  root error
2018-06-12-08-51-30  CRITICAL   MainProcess  root critical