Python logging

why logging?

  • Logging is essential to understand the behaviour of the application
  • Logging helps in debugging the unexpected issues or for simply tracking events.
  • Logs can used to get insights, set monitoring alerts, etc.

logging levels

  • Critical - used when error occured but execution can't be continued
  • Error - used when error occured but execution can be continued
  • Warning - used when something unexpected happened in the application, a problem, or a situation that might disturb one of the processes.
  • Info - used to when debugging the app with less details
  • Debug - used to when debug the app with specific details.

logging module

  • python has a built-in module to handle the logging.
  • it can be extended to write custom loggers based on the dev needs.
  • let's look at the below code
import logging

# config logging
logger = logging.getLogger(name='mylogger')
logging.basicConfig(level=logging.DEBUG)

def calculate_simple_interest(p, t, r):
    logger.info(f'principal={p} time={t} rate of interest={r}')
    amount = p * (1 + r * t)
    return amount

if __name__ == '__main__':
    interest = calculate_simple_interest(1000, 12, 0.2)
  • Try out the above code, we can see the below log in the console.
INFO:mylogger:principal=1000 time=12 rate of interest=0.2

logging handlers

  • Handler is an object responsible for dispatching the appropriate log messages (based on the log messages' severity) to the handler's specified destination.
  • Handlers are propagated like levels. If the logger has no handler set, its chain of ancestors is search for a handler.
import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

fileHandler = logging.FileHandler('test.log')
fileHandler.setLevel(logging.INFO)

consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

logger.addHandler(fileHandler)
logger.addHandler(consoleHandler)

logger.info('information message')
  • In the above code we have setup the two handlers.

  • file handler - writes the logs to the file

  • stream handler - writes the logs to the stream if specified otherwise to sys.stderr is used.

logging formatters

  • Formatter is an object which configures the final order, structure, and contents of the log record.
  • In addition to the message string, log records also include date and time, log names, and log level severity.
import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

logger.addHandler(consoleHandler)

formatter = logging.Formatter('%(asctime)s  %(name)s  %(levelname)s: %(message)s')
consoleHandler.setFormatter(formatter)

logger.info('information message')
# output: 2022-12-25 10:25:12,080  dev  INFO: information message

logging basicConfig

  • The basicConfig configures the root logger.
  • It does basic configuration for the logging system by creating a stream handler with a default formatter.
  • The debug, info, warning, error and critical call basicConfig automatically if no handlers are defined for the root logger.
import logging

logging.basicConfig(
    filename='/tmp/test.log',
    format='%(filename)s: %(message)s',
    level=logging.DEBUG
)

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
  • when we run the above code, it writes the logs to the file /tmp/test.log.
  • when we do cat /tmp/test.log then it shows the below text
testlog.py: This is a debug message
testlog.py: This is an info message
testlog.py: This is a warning message
testlog.py: This is an error message
testlog.py: This is a critical message

References: