Target-based Logging
Learn how to send logs to multiple targets simultaneously with different configurations for each destination.
🎯 Multi-Target Overview
Target-based logging allows you to send the same log messages to different destinations (console, files, remote services) with unique formatting and filtering for each target.
📤 Basic Multi-Target Setup
Console + File Targets
python
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
), create_logger
# Initialize base logger
create_logger(level="DEBUG", log_path="logs")
# Target 1: Console (INFO and above, colored)
logger.add(
sink=lambda msg: print(msg, end=""),
format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>",
level="INFO",
colorize=True
)
# Target 2: Debug file (everything)
logger.add(
"logs/debug.log",
format="{time} | {level} | {name}:{function}:{line} - {message}",
level="DEBUG",
rotation="20MB"
)
# Target 3: Error file (errors only)
logger.add(
"logs/errors.log",
format="{time} | {level} | {name}:{function}:{line} - {message} | {extra}",
level="ERROR",
rotation="10MB",
retention="90 days"
)
# Test different levels
logger.debug("Debug message - only in debug.log")
logger.info("Info message - console + debug.log")
logger.error("Error message - all three targets")
Level-based Target Separation
python
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
# Remove default handlers
logger.remove()
# Target 1: Debug console (development)
logger.add(
sink=lambda msg: print(f"[DEBUG] {msg}", end=""),
level="DEBUG",
filter=lambda record: record["level"].name == "DEBUG"
)
# Target 2: Info console (general output)
logger.add(
sink=lambda msg: print(f"[INFO] {msg}", end=""),
level="INFO",
filter=lambda record: record["level"].name in ["INFO", "SUCCESS"]
)
# Target 3: Warning console (attention needed)
logger.add(
sink=lambda msg: print(f"[WARN] {msg}", end=""),
level="WARNING",
filter=lambda record: record["level"].name == "WARNING"
)
# Target 4: Error console (problems)
logger.add(
sink=lambda msg: print(f"[ERROR] {msg}", end=""),
level="ERROR",
filter=lambda record: record["level"].name in ["ERROR", "CRITICAL"]
)
# Test level separation
logger.debug("Debug information")
logger.info("General information")
logger.success("Success message")
logger.warning("Warning message")
logger.error("Error message")
🏢 Enterprise Multi-Target Setup
Production Logging Architecture
python
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
), create_logger
import json
import socket
# Initialize base logger
create_logger(level="INFO", log_path="logs", component_name="api_server")
# Target 1: Application logs (structured JSON)
logger.add(
"logs/application.json",
format=lambda record: json.dumps({
"timestamp": record["time"].isoformat(),
"level": record["level"].name,
"service": "api_server",
"module": record["name"],
"function": record["function"],
"line": record["line"],
"message": record["message"],
"extra": record["extra"]
}),
level="INFO",
rotation="50MB",
retention="30 days"
)
# Target 2: Security audit log
logger.add(
"logs/security.log",
format="{time} | SECURITY | {message} | {extra}",
filter=lambda record: "security" in record["extra"],
level="INFO",
rotation="daily",
retention="365 days" # Keep security logs longer
)
# Target 3: Performance monitoring
logger.add(
"logs/performance.log",
format="{time} | PERF | {message} | duration: {extra[duration]} | {extra}",
filter=lambda record: "performance" in record["extra"],
level="INFO",
rotation="daily"
)
# Target 4: Critical alerts (immediate notification)
def send_alert(message):
"""Send critical alerts to monitoring system"""
try:
# Example: Send to monitoring service
alert_data = {
"service": "api_server",
"severity": "critical",
"message": message,
"timestamp": "now"
}
# sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# sock.sendto(json.dumps(alert_data).encode(), ('monitoring-server', 514))
print(f"ALERT SENT: {message}")
except Exception as e:
print(f"Failed to send alert: {e}")
logger.add(
send_alert,
format="{message}",
level="CRITICAL",
filter=lambda record: record["level"].name == "CRITICAL"
)
# Usage examples
logger.info("Server started")
# Security logging
logger.bind(security=True, user_id="user123", action="login").info("User authentication successful")
# Performance logging
logger.bind(performance=True, duration=0.245, endpoint="/api/users").info("API request processed")
# Critical alert
logger.critical("Database connection lost - immediate attention required")
🌐 Remote Target Integration
Syslog Integration
python
import socket
import json
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
def syslog_handler(message):
"""Send logs to syslog server"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# RFC3164 format: <priority>timestamp hostname tag: message
syslog_msg = f"<134>{message}" # 134 = facility 16 (local0) + severity 6 (info)
sock.sendto(syslog_msg.encode(), ('syslog-server', 514))
sock.close()
except Exception as e:
print(f"Syslog error: {e}")
# Add syslog target
logger.add(
syslog_handler,
format="{time:MMM DD HH:mm:ss} api-server pretty-loguru: {level} {message}",
level="WARNING" # Only warnings and above to syslog
)
logger.warning("This message goes to syslog")
ELK Stack Integration
python
import json
import requests
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
def elasticsearch_handler(record):
"""Send logs to Elasticsearch"""
try:
doc = {
"@timestamp": record["time"].isoformat(),
"level": record["level"].name,
"service": "api_server",
"module": record["name"],
"function": record["function"],
"line": record["line"],
"message": record["message"],
"extra": record["extra"]
}
# Send to Elasticsearch
response = requests.post(
'http://elasticsearch:9200/logs/_doc',
json=doc,
headers={'Content-Type': 'application/json'},
timeout=5
)
response.raise_for_status()
except Exception as e:
print(f"Elasticsearch error: {e}")
# Add Elasticsearch target
logger.add(
elasticsearch_handler,
format="{message}", # We handle formatting in the handler
level="INFO"
)
logger.info("Log sent to Elasticsearch")
🔄 Dynamic Target Management
Runtime Target Configuration
python
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
import os
from typing import Dict, Any
class TargetManager:
def __init__(self):
self.targets: Dict[str, int] = {}
def add_target(self, name: str, **config) -> bool:
"""Add a new logging target"""
try:
handler_id = logger.add(**config)
self.targets[name] = handler_id
logger.info(f"Added logging target: {name}")
return True
except Exception as e:
logger.error(f"Failed to add target {name}: {e}")
return False
def remove_target(self, name: str) -> bool:
"""Remove a logging target"""
if name in self.targets:
try:
logger.remove(self.targets[name])
del self.targets[name]
logger.info(f"Removed logging target: {name}")
return True
except Exception as e:
logger.error(f"Failed to remove target {name}: {e}")
return False
return False
def list_targets(self) -> list:
"""List active targets"""
return list(self.targets.keys())
# Usage
target_manager = TargetManager()
# Add targets based on environment
environment = os.getenv('ENVIRONMENT', 'development')
if environment == 'development':
target_manager.add_target(
"dev_console",
sink=lambda msg: print(f"[DEV] {msg}", end=""),
level="DEBUG",
colorize=True
)
if environment in ['staging', 'production']:
target_manager.add_target(
"app_file",
sink="logs/app.log",
level="INFO",
rotation="50MB",
retention="30 days"
)
target_manager.add_target(
"error_file",
sink="logs/errors.log",
level="ERROR",
rotation="10MB"
)
# Runtime target management
if os.getenv('ENABLE_DEBUG_LOG') == 'true':
target_manager.add_target(
"debug_file",
sink="logs/debug.log",
level="DEBUG",
rotation="daily"
)
logger.info(f"Active targets: {target_manager.list_targets()}")
Configuration-driven Targets
python
import json
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
def load_logging_config(config_file: str):
"""Load and apply logging configuration from JSON file"""
with open(config_file, 'r') as f:
config = json.load(f)
# Remove existing handlers
logger.remove()
# Add targets from configuration
for target_name, target_config in config.get('targets', {}).items():
try:
logger.add(**target_config)
logger.info(f"Configured target: {target_name}")
except Exception as e:
print(f"Failed to configure target {target_name}: {e}")
# Example config file (logging_config.json):
config_example = {
"targets": {
"console": {
"sink": "sys.stderr",
"format": "<green>{time:HH:mm:ss}</green> | <level>{level}</level> | <level>{message}</level>",
"level": "INFO",
"colorize": True
},
"application_log": {
"sink": "logs/app.log",
"format": "{time} | {level} | {name}:{function}:{line} - {message}",
"level": "DEBUG",
"rotation": "50MB",
"retention": "14 days"
},
"error_log": {
"sink": "logs/errors.log",
"format": "{time} | {level} | {name}:{function}:{line} - {message} | {extra}",
"level": "ERROR",
"rotation": "daily",
"retention": "90 days"
}
}
}
# Save example config
with open('logging_config.json', 'w') as f:
json.dump(config_example, f, indent=2)
# Load configuration
load_logging_config('logging_config.json')
logger.debug("Debug message")
logger.info("Info message")
logger.error("Error message")
📊 Target Performance Monitoring
Measuring Target Performance
python
import time
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
from contextlib import contextmanager
class PerformanceTarget:
def __init__(self, sink, **kwargs):
self.sink = sink
self.kwargs = kwargs
self.message_count = 0
self.total_time = 0
def __call__(self, message):
start_time = time.time()
if callable(self.sink):
result = self.sink(message)
else:
# Handle file sinks
with open(self.sink, 'a') as f:
f.write(str(message) + '\n')
result = None
self.total_time += time.time() - start_time
self.message_count += 1
return result
@property
def average_time(self):
return self.total_time / self.message_count if self.message_count > 0 else 0
# Create performance-monitored targets
console_target = PerformanceTarget(lambda msg: print(msg, end=""))
file_target = PerformanceTarget("logs/perf_test.log")
# Add targets
logger.add(console_target, level="INFO")
logger.add(file_target, level="DEBUG")
# Generate test logs
for i in range(100):
logger.info(f"Test message {i}")
print(f"Console average time: {console_target.average_time:.6f}s")
print(f"File average time: {file_target.average_time:.6f}s")
🎭 Conditional Target Routing
Content-based Routing
python
from pretty_loguru import create_logger
# Create logger instance
logger = create_logger(
name="demo",
log_path="logs",
level="INFO"
)
# Define routing functions
def route_database_logs(record):
"""Route database-related logs to specific file"""
return "database" in record["message"].lower() or "db" in record.get("extra", {})
def route_api_logs(record):
"""Route API-related logs to specific file"""
return "api" in record["message"].lower() or "endpoint" in record.get("extra", {})
def route_security_logs(record):
"""Route security-related logs to specific file"""
return any(keyword in record["message"].lower() for keyword in ["auth", "login", "security", "unauthorized"])
# Add conditional targets
logger.add(
"logs/database.log",
filter=route_database_logs,
level="DEBUG",
rotation="daily"
)
logger.add(
"logs/api.log",
filter=route_api_logs,
level="INFO",
rotation="50MB"
)
logger.add(
"logs/security.log",
filter=route_security_logs,
level="WARNING",
rotation="daily",
retention="365 days"
)
# Test routing
logger.info("Database connection established") # → database.log
logger.info("API endpoint /users called") # → api.log
logger.warning("Unauthorized access attempt") # → security.log
logger.info("General application message") # → general log (if configured)
Target-based logging provides maximum flexibility for complex applications requiring sophisticated log management strategies!