Bootstrap FreeKB - Python (Scripting) - Logging to a log file
Python (Scripting) - Logging to a log file

Updated:   |  Python (Scripting) articles

Here is a basic example of how to get started with logging.

#!/usr/bin/python3
import logging
logging.debug('debug message')       # <- will NOT appear in console output
logging.info('info message')         # <- will NOT appear in console output
logging.warning('warning message')   # <- will appear in console output
logging.error('error message')       # <- will appear in console output
logging.critical('critical message') # <- will appear in console output

 

Which should output the following.

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

 

Since logging is such a common task across nearly all of your Python scripts, it almost always makes sense to create a logging module that can be imported into your various Python scripts.

For example, if using Python version 2, here is what you could put into your logging module such as logger.py. This will always append events to a log file and optionally display the events on the console. This also includes os.path.dirname to get the absolute path the the directory that will contain the log file and os.makedirs to create the directory if it does not exit.

#!/usr/bin/python
import logging
from logging.handlers import RotatingFileHandler
import sys

def return_logger(log_file, console):

  logger = logging.getLogger()

  # Set Default Log Level
  #   DEBUG    - print/log critical, error, warning, info, debug events
  #   INFO     - print/log critical, error, warning, info events
  #   WARNING  - print/log critical, error, warning events
  #   ERROR    - print/log critical, error events
  #   CRITICAL - print/log critical events
  logger.setLevel(logging.INFO)

  format = logging.Formatter(fmt="[%(asctime)s %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")

  dirname = os.path.dirname(log_file)

  if not os.path.exists(dirname):
    os.makedirs(dirname)

  fileHandler = logging.FileHandler(log_file)
  fileHandler = RotatingFileHandler(log_file, maxBytes=10000000, backupCount=10)
  fileHandler.setFormatter(format)
  logger.addHandler(fileHandler)

  if console == True:
    consoleHandler = logging.StreamHandler(sys.stdout)
    consoleHandler.setFormatter(format)
    logger.addHandler(consoleHandler)

  return logger

def destroy_logger(logger):
  handlers = logger.handlers[:]
  for handler in handlers:
    logger.removeHandler(handler)
    handler.close()

 

And here is what you could have in your child Python scripts, where the logging module is imported, used and then destroyed.

#!/usr/bin/python
import os
import re
import sys

sys.path.append("/path/to/the/directory/that/has/your/logger/module")
from logger import return_logger

log_file = os.path.basename(__file__)
log_file = re.sub(".py", ".log", log_file)

# True is used so that the events are displayed on the console
logger = return_logger(log_file, True)

logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

destroy_logger(logger)

 

Here is how you can set different log formats.

#!/usr/bin/python3
import logging
import sys

log_file = "my.log"

def return_logger(log_file, frmt):
  logger = logging.getLogger()
  logger.setLevel(logging.INFO)

  try:
    frmt
  except NameError:
    format = logging.Formatter(fmt="[%(asctime)s %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
  else:
    if frmt == "minimal":
      format = logging.Formatter(fmt="%(message)s", datefmt="")
    else:
      format = logging.Formatter(fmt="[%(asctime)s %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")

  dirname = os.path.dirname(log_file)

  if not os.path.exists(dirname):
    os.makedirs(dirname)

  fileHandler = logging.FileHandler(log_file)
  fileHandler.setFormatter(format)
  logger.addHandler(fileHandler)

  consoleHandler = logging.StreamHandler(sys.stdout)
  consoleHandler.setFormatter(format)
  logger.addHandler(consoleHandler)

  return logger


def destroy_logger(logger):
  handlers = logger.handlers[:]
  for handler in handlers:
    logger.removeHandler(handler)
    handler.close()


logger = return_logger(log_file, 'standard')
logging.info("Hello")

destroy_logger(logger)

logger = return_logger(log_file, 'minimal')
logging.info("World")

destroy_logger(logger)

 

If using Python version 3, here is how to append to a log file. No output will appear on the console. The log level is set to DEBUG so that all log level events are appended to the log file.

#!/usr/bin/python
import logging
logging.basicConfig(filename='/path/to/example.log', encoding='utf-8', level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

 

Here is how you can control the format of the log file.

#!/usr/bin/python
import logging
logging.basicConfig(
        filename='/path/to/example.log',
        encoding='utf-8',
        format='[%(asctime)s %(levelname)s] %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S',
        level=logging.DEBUG)
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

 

Which should produce the following.

[2023-01-06 00:04:04 DEBUG] debug message
[2023-01-06 00:04:04 INFO] info message
[2023-01-06 00:04:04 WARNING] warning message
[2023-01-06 00:04:04 ERROR] error message
[2023-01-06 00:04:04 CRITICAL] critical message

 

The following can be used to print stdout to the console and to write to the log file.

logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter 44ab54 in the box below so that we can be sure you are a human.