Logging is the process of tracking and recording key events that occur in our applications.
Components
Logger: entity responsible for emit the log messages from the application.
Handler: entity responsible for send log records to a specific location.
Formatter: entity responsible for format and style the log records.
Priority Levels
import logging
import sys
# Creates the logger
logging.basicConfig( stream = sys.stdout, level = logging. DEBUG )
# Logging levels (from lowest to highest priority)
logging.debug( "Your message for debugging the code." )
logging.info( "Messages for informative purpose." )
logging.warning( "There is something to be aware of." )
logging.error( "Something went wrong." )
logging.critical( "Something went very very wrong." )
On line 5 the class parameter level
sets the baseline level.
Setup
1. Log Location
# project_name/config.py
import sys
import logging
import logging.config
from pathlib import Path
LOGS_DIR = Path( BASE_DIR , "logs" )
LOGS_DIR .mkdir( parents = True , exist_ok = True )
# project_name/config.py
logging_config = {
"version" : 1 ,
"disable_existing_loggers" : False ,
"formatters" : {
"minimal" : { "format" : " %(message)s " },
"detailed" : {
"format" : " %(levelname)s %(asctime)s [ %(name)s : %(filename)s : %(funcName)s : %(lineno)d ] \n%(message)s\n "
},
},
3. Define Handlers
# project_name/config.py
"handlers" : {
"console" : {
"class" : "logging.StreamHandler" ,
"stream" : sys.stdout,
"formatter" : "minimal" ,
"level" : logging. DEBUG ,
},
"info" : {
"class" : "logging.handlers.RotatingFileHandler" ,
"filename" : Path( LOGS_DIR , "info.log" ),
"maxBytes" : 10485760 , # 1 MB
"backupCount" : 10 ,
"formatter" : "detailed" ,
"level" : logging. INFO ,
},
"error" : {
"class" : "logging.handlers.RotatingFileHandler" ,
"filename" : Path( LOGS_DIR , "error.log" ),
"maxBytes" : 10485760 , # 1 MB
"backupCount" : 10 ,
"formatter" : "detailed" ,
"level" : logging. ERROR ,
},
},
"root" : {
"handlers" : [ "console" , "info" , "error" ],
"level" : logging. INFO ,
"propagate" : True ,
},
}
4. Load Config
# project_name/config.py
# Logger
logging.config.dictConfig(logging_config)
logger = logging.getLogger()
# Using loaded logger
logger.debug( "Example message here." )
logger.info( "Example message here." )
logger.warning( "Example message here." )
logger.error( "Example message here." )
logger.critical( "Example message here." )
5. Usage
# project_name/app.py
from config import logger
for epoch in epochs:
logger.info( "Epoch completed !" )
References
There are different ways to configure your project’s logs, using python scripts or configuration files. Examples below. For more advanced use cases see the documentation or the well curated cookbook .
Dictionary Setup
# project_name/config.py
import sys
import logging
import logging.config
logging_config = {
"version" : 1 ,
"disable_existing_loggers" : False ,
"formatters" : {
"default" : {
"format" : " %(levelname)s %(asctime)s [ %(name)s : %(filename)s : %(funcName)s : %(lineno)d ] >> %(message)s " ,
"datefmt" : "%Y-%m- %d %H:%M:%S" ,
},
},
"handlers" : {
"default" : {
"class" : "logging.StreamHandler" ,
"stream" : sys.stdout,
"formatter" : "default" ,
"level" : logging. CRITICAL ,
},
"file_save" : {
"class" : "logging.handlers.RotatingFileHandler" ,
"filename" : "debug.log" ,
"maxBytes" : 10485760 , # 1 MB
"backupCount" : 3 ,
"formatter" : "default" ,
"level" : logging. ERROR ,
},
},
"root" : {
"handlers" : [ "default" , "file_save" ],
"level" : logging. DEBUG ,
"propagate" : True ,
},
}
logging.config.dictConfig(logging_config)
logger = logging.getLogger()
Configuration File
logging.config
file:
[formatters]
keys=minimal,detailed
[formatter_minimal]
format=%(message)s
[formatter_detailed]
format=
%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]
%(message)s
[handlers]
keys=console,info,error
[handler_console]
class=StreamHandler
level=DEBUG
formatter=minimal
args=(sys.stdout,)
[handler_info]
class=handlers.RotatingFileHandler
level=INFO
formatter=detailed
backupCount=10
maxBytes=10485760
args=("logs/info.log",)
[handler_error]
class=handlers.RotatingFileHandler
level=ERROR
formatter=detailed
backupCount=10
maxBytes=10485760
args=("logs/error.log",)
[loggers]
keys=root
[logger_root]
level=INFO
handlers=console,info,error
file.py
script:
import logging
import logging.config
from rich.logging import RichHandler
# Use config file to initialize logger
logging.config.fileConfig(Path( CONFIG_DIR , "logging.config" ))
logger = logging.getLogger()
logger.handlers[ 0 ] = RichHandler( markup = True ) # set rich handler
Python Script
import logging
from rich.logging import RichHandler
# Get root logger
logger = logging.getLogger()
logger.setLevel(logging. DEBUG )
# Create handlers
console_handler = RichHandler( markup = True )
console_handler.setLevel(logging. DEBUG )
info_handler = logging.handlers.RotatingFileHandler(
filename = Path( LOGS_DIR , "info.log" ),
maxBytes = 10485760 , # 1 MB
backupCount = 10 ,
)
info_handler.setLevel(logging. INFO )
error_handler = logging.handlers.RotatingFileHandler(
filename = Path( LOGS_DIR , "error.log" ),
maxBytes = 10485760 , # 1 MB
backupCount = 10 ,
)
error_handler.setLevel(logging. ERROR )
# Create formatters
minimal_formatter = logging.Formatter( fmt = " %(message)s " )
detailed_formatter = logging.Formatter(
fmt = " %(levelname)s %(asctime)s [ %(name)s : %(filename)s : %(funcName)s : %(lineno)d ] \n%(message)s\n "
)
# Hook it all up
console_handler.setFormatter( fmt = minimal_formatter)
info_handler.setFormatter( fmt = detailed_formatter)
error_handler.setFormatter( fmt = detailed_formatter)
logger.addHandler( hdlr = console_handler)
logger.addHandler( hdlr = info_handler)
logger.addHandler( hdlr = error_handler)