Logging
Configure logging for backoff retry events.
Default Logger
Backoff uses the 'backoff' logger by default. It's configured with a NullHandler, so nothing is output unless you configure it.
Basic Setup
import logging
# Enable backoff logging
logging.getLogger('backoff').addHandler(logging.StreamHandler())
logging.getLogger('backoff').setLevel(logging.INFO)
Log Levels
- INFO - Logs all retry attempts
- ERROR - Logs only when giving up
- WARNING - Custom level for important retries
- DEBUG - Detailed information
# Only log when giving up
logging.getLogger('backoff').setLevel(logging.ERROR)
# Log all retries
logging.getLogger('backoff').setLevel(logging.INFO)
Custom Logger
Specify a custom logger by name or instance.
Logger by Name
@backoff.on_exception(
backoff.expo,
Exception,
logger='my_custom_logger'
)
def my_function():
pass
Logger Instance
import logging
my_logger = logging.getLogger('my_app.retries')
my_logger.addHandler(logging.FileHandler('retries.log'))
my_logger.setLevel(logging.WARNING)
@backoff.on_exception(
backoff.expo,
Exception,
logger=my_logger
)
def my_function():
pass
Disable Logging
Pass logger=None to disable all default logging:
@backoff.on_exception(
backoff.expo,
Exception,
logger=None
)
def my_function():
pass
Use with custom event handlers for complete control:
def my_custom_log(details):
print(f"Custom log: {details}")
@backoff.on_exception(
backoff.expo,
Exception,
logger=None,
on_backoff=my_custom_log
)
def my_function():
pass
Formatting
Basic Format
import logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
logging.getLogger('backoff').addHandler(logging.StreamHandler())
Structured Logging (JSON)
import logging
import json
class JsonFormatter(logging.Formatter):
def format(self, record):
log_data = {
'timestamp': self.formatTime(record),
'level': record.levelname,
'logger': record.name,
'message': record.getMessage()
}
return json.dumps(log_data)
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
backoff_logger = logging.getLogger('backoff')
backoff_logger.addHandler(handler)
backoff_logger.setLevel(logging.INFO)
Multiple Handlers
Send logs to multiple destinations:
import logging
backoff_logger = logging.getLogger('backoff')
# Console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
# File handler
file_handler = logging.FileHandler('backoff.log')
file_handler.setLevel(logging.INFO)
# Add both handlers
backoff_logger.addHandler(console_handler)
backoff_logger.addHandler(file_handler)
backoff_logger.setLevel(logging.INFO)
Per-Function Logging
Use different loggers for different functions:
critical_logger = logging.getLogger('critical_ops')
routine_logger = logging.getLogger('routine_ops')
@backoff.on_exception(
backoff.expo,
Exception,
logger=critical_logger,
max_tries=10
)
def critical_operation():
pass
@backoff.on_exception(
backoff.expo,
Exception,
logger=routine_logger,
max_tries=3
)
def routine_operation():
pass
Complete Example
import logging
from logging.handlers import RotatingFileHandler
# Create custom logger
logger = logging.getLogger('myapp.backoff')
logger.setLevel(logging.INFO)
# Console handler with WARNING level
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
console_format = logging.Formatter(
'%(levelname)s: %(message)s'
)
console_handler.setFormatter(console_format)
# File handler with INFO level and rotation
file_handler = RotatingFileHandler(
'backoff.log',
maxBytes=10*1024*1024, # 10MB
backupCount=5
)
file_handler.setLevel(logging.INFO)
file_format = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(file_format)
# Add handlers
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# Use in decorator
@backoff.on_exception(
backoff.expo,
Exception,
logger=logger,
max_tries=5
)
def my_function():
pass