Logging involves printing code output to a file that can be reviewed later.
The logging
module contains tools for logging in Python.
logging.basicCongif()
is the setup code for logging in python.
In [1]:
import logging
logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s - %(levelname)s - %(message)s') # Format for basic logging
In [18]:
# Creating a buggy program for testing
def factorial(n):
"""
A function for finding factorials.
"""
total = 1
for i in range(n+1):
total *= i # Total = total multiplied by ever element i added to itself
return total
print(factorial(5))
The factorial program is returning a 0
output, which is invalid. We can use a log to find the error(s).
Build a log framerwork:
In [19]:
import logging
# For iPython/Jupyter, logging is handled at the root level. There are two ways to reacces logging
# Way 1: (Easy way)
import imp # Import the 'import' internals module
imp.reload(logging) # Reload the logging module for this specific instance
# Way 2: (Less Easy Way)
# Must define a logging object for this instance:
# logger = logging.getLogger() # Logging object
# logger.setLevel(logging.DEBUG) # Setting logging level to the level defined in the program
# This can be used by passing the normal logging.debug('message') as usual
logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s - %(levelname)s - %(message)s') # Format for basic logging
logging.debug('Start of Program') # Start of program in log
def factorial(n):
"""
A function for finding factorials.
"""
logging.debug('Start of the factorial(%s)' % (n)) # Start the program and pass the argument to the log
total = 1
for i in range(n+1):
total *= i # Total = total multiplied by ever element i added to itself
logging.debug('i is the %s, total is %s' % (i, total)) # Pass the actual function arguments to the log
logging.debug('Return value is %s' % (total)) # Return the total argument to the log
return total
print(factorial(5))
logging.debug('End of Program') # End of program in log
logging.debug()
function calls are a lot like print()
calls, but they can provide a lot more information:
%(asctime)s
.%(levelname)s
.logging.debug()
message via %(message)s
.From the log, it seems the issue is because the function starts at 0
and not 1
, multiplying every following iteration by total = 0
.
Fix this by passing the range()
function a start of 1
:
In [17]:
import logging
import imp # Import the 'import' internals module
imp.reload(logging) # Reload the logging module for this specific instance
logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s - %(levelname)s - %(message)s') # Format for basic logging
logging.debug('Start of Program') # Start of program in log
def factorial(n):
"""
A function for finding factorials.
"""
logging.debug('Start of the factorial(%s)' % (n)) # Start the program and pass the argument to the log
total = 1
for i in range(1, n+1):
total *= i # Total = total multiplied by ever element i added to itself
logging.debug('i is the %s, total is %s' % (i, total)) # Pass the actual function arguments to the log
logging.debug('Return value is %s' % (total)) # Return the total argument to the log
return total
print(factorial(5))
logging.debug('End of Program') # End of program in log
This is now returning the proper value.
logging.debug()
is used instead of print()
because when the program is complete, every print()
debug statement to be removed (and normal print()
methods have to be ignored.)
Meanwhile, removing the debug()
messages is as simple as changing the log level:
In [20]:
import logging
import imp
imp.reload(logging)
logging.disable(logging.CRITICAL) # Switch the log level from 'DEBUG' to 'CRITICAL'; only show 'critical' level debugs
logging.basicConfig(level=logging.DEBUG, format = '%(asctime)s - %(levelname)s - %(message)s') # Format for basic logging
logging.debug('Start of Program') # Start of program in log
def factorial(n):
"""
A function for finding factorials.
"""
logging.debug('Start of the factorial(%s)' % (n)) # Start the program and pass the argument to the log
total = 1
for i in range(1, n+1):
total *= i # Total = total multiplied by ever element i added to itself
logging.debug('i is the %s, total is %s' % (i, total)) # Pass the actual function arguments to the log
logging.debug('Return value is %s' % (total)) # Return the total argument to the log
return total
print(factorial(5))
logging.debug('End of Program') # End of program in log
There are 5 log levels defined in Python:
logging.debug()
.logging.info()
.logging.warnig()
.logging.error()
.logging.critical()
.logging.disable()
disables that level and every element below it.
logging.disable(logging.WARNING)
would therefore disable the Warning
, Info
and Debug
levels.
logging.disable(logging.CRITICAL)
would disable all levels except Critical
.
To put the log outputs in a text file, you can modify the logging.basicConfig()
to take a filename
parameter.
Formerly, the try
and except
statements were used to handle exceptions, but you can also create your own exceptions with raise
.
In [26]:
import logging
import os
import imp
imp.reload(logging)
#logging.disable(logging.CRITICAL) # Switch the log level from 'DEBUG' to 'CRITICAL'; only show 'critical' level debugs
logging.basicConfig(filename = os.path.abspath('files/Factoriallog.txt'), level=logging.DEBUG, format = '%(asctime)s - %(levelname)s - %(message)s') # Format for basic logging
logging.debug('Start of Program') # Start of program in log
def factorial(n):
"""
A function for finding factorials.
"""
logging.debug('Start of the factorial(%s)' % (n)) # Start the program and pass the argument to the log
total = 1
for i in range(1, n+1):
total *= i # Total = total multiplied by ever element i added to itself
logging.debug('i is the %s, total is %s' % (i, total)) # Pass the actual function arguments to the log
logging.debug('Return value is %s' % (total)) # Return the total argument to the log
return total
print(factorial(5))
logging.debug('End of Program') # End of program in log
In [27]:
print(open(os.path.abspath('files/Factoriallog.txt'), 'r').read()) # Open and read the created log file
basicConfig()
to set up logging, call logging.debug()
to create a log message.logging.disable(logging.CRITICAL)
(which disables every level below it.)prin()
for log messages; it's hard to remove them all when you're done debugging.DEBUG
, INFO
, WARNING
, ERROR
, and CRITICAL
.filename
paramater in the basicConfig()
function.