logging.config
The logging.config module provides configuration functions for the logging module. It supports several configuration methods including dictionary-based, file-based, and network-based configuration.
Basic Usage
Dictionary Configuration
import logging
import logging.config
# Simple dictionary configuration
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
'detailed': {
'format': '%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'standard',
'stream': 'ext://sys.stdout',
},
'file': {
'class': 'logging.FileHandler',
'level': 'DEBUG',
'formatter': 'detailed',
'filename': 'app.log',
},
},
'loggers': {
'my_app': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
'propagate': False,
},
},
'root': {
'level': 'WARNING',
'handlers': ['console'],
},
}
# Apply configuration
logging.config.dictConfig(LOGGING_CONFIG)
# Use the configured logger
logger = logging.getLogger('my_app')
logger.info("Application started")
logger.debug("Debug information")
Basic Configuration
import logging
# Simple basic configuration
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('debug.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
logger.info("Basic configuration example")
File-Based Configuration
import logging.config
# Load configuration from file
logging.config.fileConfig('logging.conf')
logger = logging.getLogger(__name__)
logger.info("Loaded from configuration file")
Configuration API Reference
Core Functions
| Function | Description | Parameters | Return Type |
|---|---|---|---|
dictConfig(config) | Configure logging from dictionary | config: Configuration dict | None |
fileConfig(fname, defaults=None, disable_existing_loggers=True) | Configure from file | fname: Config file path | None |
listen(port=DEFAULT_LOGGING_CONFIG_PORT) | Start config server | port: TCP port number | None |
stopListening() | Stop config server | None | None |
Basic Configuration
| Function | Description | Parameters | Return Type |
|---|---|---|---|
basicConfig(**kwargs) | Basic logging setup | Various keyword arguments | None |
Dictionary Configuration Schema
Complete Configuration Structure
COMPLETE_CONFIG = {
'version': 1, # Required: schema version
'disable_existing_loggers': False, # Optional: disable existing loggers
# Optional: Define incremental config
'incremental': False,
# Optional: Define formatters
'formatters': {
'formatter_name': {
'format': 'format_string',
'datefmt': 'date_format_string',
'style': '%', # or '{' or '$'
'class': 'logging.Formatter', # Custom formatter class
},
},
# Optional: Define filters
'filters': {
'filter_name': {
'name': 'logger_name', # For built-in Filter
'class': 'module.FilterClass', # Custom filter class
# Custom parameters for filter
},
},
# Optional: Define handlers
'handlers': {
'handler_name': {
'class': 'logging.StreamHandler', # Handler class
'level': 'INFO', # Optional: handler level
'formatter': 'formatter_name', # Optional: formatter reference
'filters': ['filter_name'], # Optional: list of filter references
# Handler-specific parameters
},
},
# Optional: Define loggers
'loggers': {
'logger_name': {
'level': 'DEBUG', # Optional: logger level
'handlers': ['handler_name'], # Optional: list of handler references
'filters': ['filter_name'], # Optional: list of filter references
'propagate': True, # Optional: propagation setting
},
},
# Optional: Configure root logger
'root': {
'level': 'WARNING',
'handlers': ['handler_name'],
'filters': ['filter_name'],
},
}
Real-World Configuration Examples
import logging.config
import sys
# Production configuration
PRODUCTION_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'production': {
'format': '%(asctime)s.%(msecs)03d [%(process)d:%(thread)d] '
'%(name)-20s %(levelname)-8s %(filename)s:%(lineno)d - %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
'json': {
'class': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(name)s %(levelname)s %(message)s %(filename)s %(lineno)d',
},
},
'filters': {
'rate_limit': {
'class': 'myapp.logging_filters.RateLimitFilter',
'max_per_minute': 100,
},
'sensitive_data': {
'class': 'myapp.logging_filters.SensitiveDataFilter',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'production',
'stream': 'ext://sys.stdout',
'filters': ['rate_limit'],
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'json',
'filename': '/var/log/myapp/app.log',
'maxBytes': 10485760, # 10MB
'backupCount': 5,
'filters': ['sensitive_data'],
},
'error_file': {
'class': 'logging.FileHandler',
'level': 'ERROR',
'formatter': 'production',
'filename': '/var/log/myapp/error.log',
},
'syslog': {
'class': 'logging.handlers.SysLogHandler',
'level': 'WARNING',
'formatter': 'production',
'address': '/dev/log',
},
},
'loggers': {
'myapp': {
'level': 'DEBUG',
'handlers': ['console', 'file', 'error_file'],
'propagate': False,
},
'myapp.database': {
'level': 'INFO',
'handlers': ['file'],
'propagate': True,
},
'requests': {
'level': 'WARNING',
'propagate': True,
},
},
'root': {
'level': 'WARNING',
'handlers': ['console', 'syslog'],
},
}
# Apply production configuration
logging.config.dictConfig(PRODUCTION_CONFIG)
File-Based Configuration
INI-Style Configuration File
# logging.conf
[loggers]
keys=root,myapp,database
[handlers]
keys=consoleHandler,fileHandler,rotatingHandler
[formatters]
keys=simpleFormatter,detailedFormatter
[logger_root]
level=WARNING
handlers=consoleHandler
[logger_myapp]
level=DEBUG
handlers=consoleHandler,fileHandler
qualname=myapp
propagate=0
[logger_database]
level=INFO
handlers=rotatingHandler
qualname=myapp.database
propagate=1
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=detailedFormatter
args=('app.log', 'a')
[handler_rotatingHandler]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=detailedFormatter
args=('database.log', 'a', 1048576, 3)
[formatter_simpleFormatter]
format=%(levelname)s - %(message)s
[formatter_detailedFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s
datefmt=%Y-%m-%d %H:%M:%S
Loading File Configuration
import logging.config
import os
# Check if config file exists
config_file = 'logging.conf'
if os.path.exists(config_file):
logging.config.fileConfig(config_file)
else:
# Fallback to basic configuration
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
logger.info("Logging configured from file")
Advanced Configuration Patterns
Environment-Based Configuration
import logging.config
import os
def get_logging_config():
"""Get logging configuration based on environment"""
env = os.getenv('ENVIRONMENT', 'development')
base_config = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
},
}
if env == 'production':
config = {
**base_config,
'handlers': {
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'INFO',
'formatter': 'standard',
'filename': '/var/log/app.log',
'maxBytes': 10485760,
'backupCount': 5,
},
'syslog': {
'class': 'logging.handlers.SysLogHandler',
'level': 'WARNING',
'formatter': 'standard',
},
},
'root': {
'level': 'INFO',
'handlers': ['file', 'syslog'],
},
}
elif env == 'development':
config = {
**base_config,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'formatter': 'standard',
},
'file': {
'class': 'logging.FileHandler',
'level': 'DEBUG',
'formatter': 'standard',
'filename': 'debug.log',
},
},
'root': {
'level': 'DEBUG',
'handlers': ['console', 'file'],
},
}
else: # testing
config = {
**base_config,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'CRITICAL',
'formatter': 'standard',
},
},
'root': {
'level': 'CRITICAL',
'handlers': ['console'],
},
}
return config
# Apply environment-specific configuration
logging.config.dictConfig(get_logging_config())
Dynamic Configuration with Validation
import logging.config
import json
import jsonschema
# JSON Schema for logging configuration validation
LOGGING_SCHEMA = {
"type": "object",
"properties": {
"version": {"type": "number", "minimum": 1},
"disable_existing_loggers": {"type": "boolean"},
"formatters": {
"type": "object",
"patternProperties": {
".*": {
"type": "object",
"properties": {
"format": {"type": "string"},
"datefmt": {"type": "string"},
"style": {"enum": ["%", "{", "$"]},
},
}
}
},
"handlers": {
"type": "object",
"patternProperties": {
".*": {
"type": "object",
"properties": {
"class": {"type": "string"},
"level": {"type": "string"},
"formatter": {"type": "string"},
},
"required": ["class"]
}
}
},
},
"required": ["version"]
}
def load_and_validate_config(config_path):
"""Load and validate logging configuration"""
try:
with open(config_path, 'r') as f:
config = json.load(f)
# Validate against schema
jsonschema.validate(config, LOGGING_SCHEMA)
# Apply configuration
logging.config.dictConfig(config)
print(f"Logging configuration loaded from {config_path}")
except FileNotFoundError:
print(f"Config file {config_path} not found, using defaults")
logging.basicConfig(level=logging.INFO)
except jsonschema.ValidationError as e:
print(f"Invalid configuration: {e.message}")
logging.basicConfig(level=logging.INFO)
except Exception as e:
print(f"Error loading configuration: {e}")
logging.basicConfig(level=logging.INFO)
# Usage
load_and_validate_config('logging_config.json')
Configuration with Custom Objects
import logging.config
class CustomFormatter(logging.Formatter):
"""Custom formatter with color support"""
COLORS = {
'DEBUG': '\033[36m',
'INFO': '\033[32m',
'WARNING': '\033[33m',
'ERROR': '\033[31m',
'CRITICAL': '\033[35m',
}
RESET = '\033[0m'
def format(self, record):
if record.levelname in self.COLORS:
record.levelname = f"{self.COLORS[record.levelname]}{record.levelname}{self.RESET}"
return super().format(record)
class CustomFilter(logging.Filter):
"""Custom filter for specific requirements"""
def __init__(self, exclude_patterns=None):
super().__init__()
self.exclude_patterns = exclude_patterns or []
def filter(self, record):
message = record.getMessage()
return not any(pattern in message for pattern in self.exclude_patterns)
# Configuration using custom objects
CUSTOM_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'colored': {
'()': CustomFormatter, # Use custom class
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s',
},
},
'filters': {
'exclude_noise': {
'()': CustomFilter, # Use custom class
'exclude_patterns': ['heartbeat', 'keepalive'],
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'colored',
'filters': ['exclude_noise'],
},
},
'root': {
'level': 'INFO',
'handlers': ['console'],
},
}
logging.config.dictConfig(CUSTOM_CONFIG)
Runtime Configuration Updates
Dynamic Reconfiguration
import logging.config
import threading
import time
class ConfigurableLogging:
"""Logging system that can be reconfigured at runtime"""
def __init__(self, initial_config):
self.current_config = initial_config
self.config_lock = threading.Lock()
self.apply_config(initial_config)
def apply_config(self, config):
"""Apply new logging configuration"""
with self.config_lock:
try:
logging.config.dictConfig(config)
self.current_config = config
print("Logging configuration updated")
except Exception as e:
print(f"Failed to update configuration: {e}")
def update_level(self, logger_name, new_level):
"""Update log level for specific logger"""
with self.config_lock:
if 'loggers' not in self.current_config:
self.current_config['loggers'] = {}
if logger_name not in self.current_config['loggers']:
self.current_config['loggers'][logger_name] = {}
self.current_config['loggers'][logger_name]['level'] = new_level
self.apply_config(self.current_config)
def add_handler(self, logger_name, handler_name):
"""Add handler to specific logger"""
with self.config_lock:
if 'loggers' not in self.current_config:
self.current_config['loggers'] = {}
if logger_name not in self.current_config['loggers']:
self.current_config['loggers'][logger_name] = {'handlers': []}
if 'handlers' not in self.current_config['loggers'][logger_name]:
self.current_config['loggers'][logger_name]['handlers'] = []
if handler_name not in self.current_config['loggers'][logger_name]['handlers']:
self.current_config['loggers'][logger_name]['handlers'].append(handler_name)
self.apply_config(self.current_config)
# Usage
initial_config = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
},
},
'root': {
'level': 'INFO',
'handlers': ['console'],
},
}
configurable_logging = ConfigurableLogging(initial_config)
# Runtime updates
configurable_logging.update_level('myapp', 'DEBUG')
Hot Configuration Reloading
import logging.config
import json
import os
import time
import threading
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class ConfigReloadHandler(FileSystemEventHandler):
"""Handle configuration file changes"""
def __init__(self, config_path):
self.config_path = config_path
self.last_modified = 0
def on_modified(self, event):
if event.src_path == self.config_path:
# Debounce rapid file changes
current_time = time.time()
if current_time - self.last_modified > 1:
self.reload_config()
self.last_modified = current_time
def reload_config(self):
"""Reload logging configuration from file"""
try:
with open(self.config_path, 'r') as f:
config = json.load(f)
logging.config.dictConfig(config)
print(f"Reloaded logging configuration from {self.config_path}")
except Exception as e:
print(f"Failed to reload configuration: {e}")
def setup_config_watcher(config_path):
"""Set up automatic configuration reloading"""
event_handler = ConfigReloadHandler(config_path)
observer = Observer()
observer.schedule(event_handler, os.path.dirname(config_path), recursive=False)
observer.start()
return observer
# Usage
config_path = 'logging_config.json'
observer = setup_config_watcher(config_path)
try:
# Your application code here
logger = logging.getLogger(__name__)
while True:
logger.info("Application running...")
time.sleep(10)
finally:
observer.stop()
observer.join()
Network Configuration
Remote Configuration Server
import logging.config
import socketserver
import struct
import pickle
import threading
class LoggingConfigHandler(socketserver.StreamRequestHandler):
"""Handle incoming logging configuration requests"""
def handle(self):
try:
# Read configuration data
chunk = self.rfile.read(4)
if len(chunk) < 4:
return
slen = struct.unpack('>L', chunk)[0]
chunk = self.rfile.read(slen)
# Deserialize and apply configuration
config = pickle.loads(chunk)
logging.config.dictConfig(config)
print(f"Applied configuration from {self.client_address}")
except Exception as e:
print(f"Error handling configuration: {e}")
def start_config_server(port=9020):
"""Start configuration server"""
server = socketserver.TCPServer(('localhost', port), LoggingConfigHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
print(f"Configuration server started on port {port}")
return server
# Client code to send configuration
def send_config(config, host='localhost', port=9020):
"""Send configuration to remote server"""
import socket
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
data = pickle.dumps(config)
sock.send(struct.pack('>L', len(data)))
sock.send(data)
sock.close()
print("Configuration sent successfully")
except Exception as e:
print(f"Failed to send configuration: {e}")
# Usage
if __name__ == "__main__":
# Start server
server = start_config_server()
# Example configuration to send
new_config = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
},
},
'root': {
'level': 'DEBUG',
'handlers': ['console'],
},
}
# Send configuration
time.sleep(1) # Let server start
send_config(new_config)
Common Configuration Issues
Configuration Validation
import logging.config
def validate_logging_config(config):
"""Validate logging configuration before applying"""
errors = []
# Check required fields
if 'version' not in config:
errors.append("Missing required 'version' field")
# Validate formatters
if 'formatters' in config:
for name, formatter in config['formatters'].items():
if 'format' not in formatter and 'class' not in formatter:
errors.append(f"Formatter '{name}' missing 'format' or 'class'")
# Validate handlers
if 'handlers' in config:
for name, handler in config['handlers'].items():
if 'class' not in handler:
errors.append(f"Handler '{name}' missing 'class' field")
# Check formatter references
if 'formatter' in handler:
if handler['formatter'] not in config.get('formatters', {}):
errors.append(f"Handler '{name}' references unknown formatter '{handler['formatter']}'")
# Validate loggers
if 'loggers' in config:
for name, logger in config['loggers'].items():
# Check handler references
if 'handlers' in logger:
for handler_name in logger['handlers']:
if handler_name not in config.get('handlers', {}):
errors.append(f"Logger '{name}' references unknown handler '{handler_name}'")
return errors
# Safe configuration application
def safe_apply_config(config):
"""Safely apply configuration with validation"""
errors = validate_logging_config(config)
if errors:
print("Configuration validation failed:")
for error in errors:
print(f" - {error}")
return False
try:
logging.config.dictConfig(config)
return True
except Exception as e:
print(f"Failed to apply configuration: {e}")
return False
Error Recovery
import logging.config
import json
def robust_config_loading(config_paths, fallback_config):
"""Try multiple configuration sources with fallback"""
for config_path in config_paths:
try:
with open(config_path, 'r') as f:
config = json.load(f)
logging.config.dictConfig(config)
print(f"Successfully loaded configuration from {config_path}")
return True
except FileNotFoundError:
print(f"Configuration file {config_path} not found")
continue
except json.JSONDecodeError as e:
print(f"Invalid JSON in {config_path}: {e}")
continue
except Exception as e:
print(f"Error loading {config_path}: {e}")
continue
# Apply fallback configuration
try:
logging.config.dictConfig(fallback_config)
print("Applied fallback configuration")
return True
except Exception as e:
print(f"Failed to apply fallback configuration: {e}")
# Last resort
logging.basicConfig(level=logging.INFO)
return False
# Usage
config_paths = [
'/etc/myapp/logging.json',
'./config/logging.json',
'./logging.json',
]
fallback_config = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
},
},
'root': {
'level': 'INFO',
'handlers': ['console'],
},
}
robust_config_loading(config_paths, fallback_config)
Related Components
- logging.Logger - Configured through this module
- logging.Handler - Handler configuration options
- logging.Formatter - Formatter configuration options
- logging.Filter - Filter configuration options