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) |