Skip to content

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