How to use logging to debug
a tutorial on using the logging package for debugging
- Basic Usage
- Logging to a file
- Logging from multiple modules
- Setting the logging level from a command-line option
- Reference
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.
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')
Since we set the level to INFO, messages with level lower than INFO won't be printed
import logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.warning('is when this event was logged.')
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
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')
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()
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.
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
You should find the following message in file.log:
INFO:root:test message 1