Logging with Python

Import

import logging

Basic configuration for the logging system

logging.basicConfig()
Practical for simple scripts to do one-shot configuration of the logging package.
The default behaviour is to create a StreamHandler which writes to sys.stderr, set a formatter using the BASIC_FORMAT format string, and add the handler to the root logger.
But we could also specify parameters such as format, filename, filemode, level, handlers….

Example with a short format and the root level at INFO :

format = '%(asctime)-15s - %(levelname)s : %(message)s'
logging.basicConfig(level=logging.INFO, format=format)
logger = logging.root
logger.info('I trace something because I am a logger.')

Output:

2023-04-18 16:28:06,741 - INFO : I trace something because I am a logger.

Example with a detailed format (Output of the thread name, the module name and the function name) and the root level at INFO :

format = '%(asctime)-15s - %(threadName)s - %(levelname)s : %(name)s.%(funcName)s %(message)s'
logging.basicConfig(level=logging.INFO,
                    format=format,
                    force=True)
logger = logging.root
logger.info('I trace something because I am a logger.')

Output:

2023-04-18 16:28:08,754 - MainThread - INFO : root.basic_config_example_with_detailed_format  I trace something because I am a logger.

Example with a detailed format and 2 handlers (StreamHandler and RotatingFileHandler):
– we define a StreamHandler with the info level that writes in the standard out and a RotatingFileHandler that writes in a file with the debug level.
About the level of the two handlers : it means the first one doesn’t log below the info level while the second one doesn’t log below the debug level.
– Besides the root logger, we define two additional loggers, the Main and the foo loggers. Each one has its own level.

The main python file:

import logging
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
 
from mylogging.foo import do_foo
 
logging_format = \
    '%(asctime)-15s - %(threadName)s - %(levelname)s : %(name)s.%(funcName)s  %(message)s'
rotating_file_handler: logging.FileHandler = \
    RotatingFileHandler('{0}/{1}.log'.format(Path.cwd(), 'basic_logging_with_multiple_handlers'),
                        maxBytes=1000000,
                        backupCount=3)
stream_handler: logging.StreamHandler = logging.StreamHandler(sys.stdout)
logging.basicConfig(level=logging.INFO, format=logging_format,
                    handlers=[
                            stream_handler,
                            rotating_file_handler,
                    ])
# Set the level of the handlers
rotating_file_handler.setLevel(logging.DEBUG)
stream_handler.setLevel(logging.INFO)
 
# Set the level of the loggers
logger = logging.getLogger('Main')
logger.setLevel(logging.DEBUG)
logging.getLogger('foo').setLevel(logging.DEBUG)
 
# application start
logger.info('Start')
do_foo()

The foo python file that logs with the foo logger:

import logging
 
logger: logging.Logger = logging.getLogger('foo')
 
 
def do_foo():
    logger.info('The function starts.')
    logger.debug('we trace a detail of the function in debug level.')

Output in stdout :

2023-04-19 09:23:45,972 - MainThread - INFO : Main.<module>  Start
2023-04-19 09:23:45,973 - MainThread - INFO : foo.do_foo  The function starts.

Output in basic_logging_with_multiple_handlers.log:

2023-04-19 09:23:45,972 - MainThread - INFO : Main.<module>  Start
2023-04-19 09:23:45,973 - MainThread - INFO : foo.do_foo  The function starts.
2023-04-19 09:23:45,973 - MainThread - DEBUG : foo.do_foo  we trace a detail of the function in debug level.

Common use case with loggers

Logger and Root Logger :

# get root logger object, two ways :
logger = logging.getLogger()
logger = logging.root
 
# get a logger object :
logger = logging.getLogger("logger-name))

Write a log :

requests_logger = logging.getLogger('urllib3')
requests_logger.info('msg')
# or inline
logging.getLogger('foo-app').info('other msg')

Log with the root logger :
Best way : retrieve root logger as seen above and log with.
An alternative for quick and dirty app/tests is calling logging method wrapped by the logging package.
It performs basicConfig() for us if no handler are present and log with the root logger.

logging.info("I log without any explicit config")

Get current log level for a logger :

# returns the numeric level value
level: int = logger.level
# returns the textual level value
levelText: str = logging.getLevelName(logger.level)

Set the log level for a logger :
logger.setLevel(logging.DEBUG)

Logging numeric level meaning :

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

Force the reconfiguration of the logging.basicConfig() logging method:

logging.basicConfig(level=logging.INFO,...force=True)
Ce contenu a été publié dans Non classé. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *