175 lines
6.3 KiB
Python
175 lines
6.3 KiB
Python
"""Centralized logging configuration for AlpineBits application.
|
|
|
|
This module sets up logging based on config and provides a function to get
|
|
loggers from anywhere in the application.
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from alpine_bits_python.email_monitoring import (
|
|
DailyReportScheduler,
|
|
EmailAlertHandler,
|
|
)
|
|
from alpine_bits_python.email_service import EmailService
|
|
from alpine_bits_python.pushover_service import PushoverService
|
|
|
|
|
|
def setup_logging(
|
|
config: dict | None = None,
|
|
email_service: "EmailService | None" = None,
|
|
pushover_service: "PushoverService | None" = None,
|
|
loop: asyncio.AbstractEventLoop | None = None,
|
|
enable_scheduler: bool = True,
|
|
) -> tuple[logging.Handler | None, object | None]:
|
|
"""Configure logging based on application config.
|
|
|
|
Args:
|
|
config: Application configuration dict with optional 'logger' section
|
|
email_service: Optional email service for email alerts
|
|
pushover_service: Optional pushover service for push notifications
|
|
loop: Optional asyncio event loop for email alerts
|
|
enable_scheduler: Whether to enable the daily report scheduler
|
|
(should be False for non-primary workers)
|
|
|
|
Returns:
|
|
Tuple of (alert_handler, daily_report_scheduler) if monitoring
|
|
is enabled, otherwise (None, None)
|
|
|
|
Logger config format:
|
|
logger:
|
|
level: "INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
file: "alpinebits.log" # Optional, logs to console if not provided
|
|
|
|
"""
|
|
if config is None:
|
|
config = {}
|
|
|
|
logger_config = config.get("logger", {})
|
|
level = logger_config.get("level", "INFO").upper()
|
|
log_file = logger_config.get("file")
|
|
|
|
# Convert string level to logging constant
|
|
numeric_level = getattr(logging, level, logging.INFO)
|
|
|
|
# Create formatter with timestamp
|
|
formatter = logging.Formatter(
|
|
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
|
|
# Get root logger
|
|
root_logger = logging.getLogger()
|
|
root_logger.setLevel(numeric_level)
|
|
|
|
# Remove existing handlers to avoid duplicates
|
|
root_logger.handlers.clear()
|
|
|
|
# Console handler (always add this)
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(numeric_level)
|
|
console_handler.setFormatter(formatter)
|
|
root_logger.addHandler(console_handler)
|
|
|
|
# File handler (optional)
|
|
if log_file:
|
|
log_path = Path(log_file)
|
|
|
|
# Create logs directory if it doesn't exist
|
|
if log_path.parent != Path():
|
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
file_handler.setLevel(numeric_level)
|
|
file_handler.setFormatter(formatter)
|
|
root_logger.addHandler(file_handler)
|
|
|
|
root_logger.info("Logging to file: %s", log_file)
|
|
|
|
root_logger.info("Logging configured at %s level", level)
|
|
|
|
# Setup unified notification monitoring if configured
|
|
alert_handler = None
|
|
report_scheduler = None
|
|
|
|
# Check if unified notifications are configured
|
|
notifications_config = config.get("notifications", {})
|
|
if notifications_config and (email_service or pushover_service):
|
|
try:
|
|
# Import here to avoid circular dependencies
|
|
from alpine_bits_python.notification_manager import (
|
|
get_notification_config,
|
|
setup_notification_service,
|
|
)
|
|
from alpine_bits_python.unified_monitoring import (
|
|
UnifiedAlertHandler,
|
|
UnifiedDailyReportScheduler,
|
|
)
|
|
|
|
# Setup unified notification service
|
|
notification_service = setup_notification_service(
|
|
config=config,
|
|
email_service=email_service,
|
|
pushover_service=pushover_service,
|
|
)
|
|
|
|
if notification_service:
|
|
# Setup error alert handler
|
|
error_alerts_config = get_notification_config("error_alerts", config)
|
|
if error_alerts_config.get("enabled", False):
|
|
try:
|
|
alert_handler = UnifiedAlertHandler(
|
|
notification_service=notification_service,
|
|
config=error_alerts_config,
|
|
loop=loop,
|
|
)
|
|
alert_handler.setLevel(logging.ERROR)
|
|
root_logger.addHandler(alert_handler)
|
|
root_logger.info("Unified alert handler enabled for error monitoring")
|
|
except Exception:
|
|
root_logger.exception("Failed to setup unified alert handler")
|
|
|
|
# Setup daily report scheduler (only if enabled and this is primary worker)
|
|
daily_report_config = get_notification_config("daily_report", config)
|
|
if daily_report_config.get("enabled", False) and enable_scheduler:
|
|
try:
|
|
report_scheduler = UnifiedDailyReportScheduler(
|
|
notification_service=notification_service,
|
|
config=daily_report_config,
|
|
)
|
|
root_logger.info("Unified daily report scheduler configured (primary worker)")
|
|
except Exception:
|
|
root_logger.exception("Failed to setup unified daily report scheduler")
|
|
elif daily_report_config.get("enabled", False) and not enable_scheduler:
|
|
root_logger.info(
|
|
"Unified daily report scheduler disabled (non-primary worker)"
|
|
)
|
|
|
|
except Exception:
|
|
root_logger.exception("Failed to setup unified notification monitoring")
|
|
|
|
return alert_handler, report_scheduler
|
|
|
|
|
|
def get_logger(name: str) -> logging.Logger:
|
|
"""Get a logger instance for the given module name.
|
|
|
|
Usage:
|
|
from alpine_bits_python.logging_config import get_logger
|
|
|
|
_LOGGER = get_logger(__name__)
|
|
_LOGGER.info("Something happened")
|
|
|
|
Args:
|
|
name: Usually __name__ from the calling module
|
|
|
|
Returns:
|
|
Configured logger instance
|
|
|
|
"""
|
|
return logging.getLogger(name)
|