Why use logging instead of print()?

Logging gives you the flexibility to

  • turn on and off the debugging message
  • recording logging events in a file

The flexibility is especially helpful when you're running a large program with multiple scripts.


Basic Usage

There are five standard logging levels (in increasing order of severity)

  • DEBUG
  • INFO
  • WARNING
  • ERROR
  • CRITICAL

The logging levels are used as thresholds for tracking. Once the logging level is set, messages with lower levels won't be printed.


We can use basicConfig to initiate a root logger with a severity level:

import logging
logging.basicConfig(level=logging.INFO)

The logging functions are named after the levels. The default message is in this format:

{logging level} : {logger name} {message}
logging.debug('debug message')  # will not print anything
logging.info('info message')  # will print a message to the console
logging.warning('warning message') 
logging.error('error message') 
logging.critical('critical message')
INFO:root:info message
WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message

Since we set the level to INFO, messages with level lower than INFO won't be printed

Important: The logging level of the root logger can only be set once. If you’d like to reset the logging level, you have to restart your runtime.


Display date and time in logging messages

We can specify the format argument to change the format of displayed messges.

import logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.warning('is when this event was logged.')


Logging to a file

To log to a file, we can specify the filename argument in basicConfig

import logging
logging.basicConfig(filename='example.log', level=logging.INFO)


logging.debug('debug message')  # will not print anything
logging.info('info message')  # will print a message to the console
logging.warning('warning message') 
logging.error('error message') 
logging.critical('critical message')

The example.log file should have the following messages:

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


Not remembering the messages from earlier runs

If we run the above code several times, the messages from successive runs will be appended to example.log.

If we want each run to start afresh, we can specify the filemode argument:

import logging
logging.basicConfig(filename='example.log', filemode='w', level=logging.INFO)


logging.debug('debug message')  # will not print anything
logging.info('info message')  # will print a message to the console
logging.warning('warning message') 
logging.error('error message') 
logging.critical('critical message')


Logging from multiple modules

If our program consists of multiple modules, we can create a logger in each module with:

logger = logging.getLogger(__name__)

and use logger.debug(), logger.info(), logger.warning(), logger.error(), or logger.critical() for logging messages.

Example

# mylib1.py
import logging
# create logger with name 'mylib1'
logger = logging.getLogger(__name__)

def do_something_1():
    logger.info('Doing something')
# mylib2.py
import logging
# create logger with name 'mylib1'
logger = logging.getLogger(__name__)

def do_something_2():
    logger.info('Doing something')
import logging
import mylib1, mylib2


def main():
    logging.basicConfig(level=logging.INFO)
    logging.info('Started')


    mylib1.do_something_1()
    mylib2.do_something_2()
    logging.info('Finished')

if __name__ == '__main__':
    main()
INFO:root:Started
INFO:mylib1:Doing something
INFO:mylib2:Doing something
INFO:root:Finished

By creating a logger in each module and setting a logger name, we can tell from the logging messages where in our application the messages came from.


Setting the logging level from a command-line option

If you want to set the logging level from a command-line option such as:

--log INFO

Here's an example script:

#logger.py
import argparse
import logging

parser = argparse.ArgumentParser( description='Process logging level')
parser.add_argument('--log', default='DEBUG', help='set logging level')
args = parser.parse_args()
print(args)

# get log level
loglevel = getattr(logging, args.log.upper())
logging.basicConfig(filename = 'file.log',level=loglevel,filemode='w' )

logging.info('test message 1')
logging.debug('test message 2')
%run logger.py --log INFO
Namespace(log='INFO')

You should find the following message in file.log:

INFO:root:test message 1