CalejoControl/src/core/logging.py

136 lines
4.1 KiB
Python
Raw Normal View History

"""
Structured logging setup for Calejo Control Adapter.
"""
import structlog
import logging
import sys
import uuid
from datetime import datetime
from typing import Dict, Any, Optional
from config.settings import settings
def setup_logging():
"""Setup structured logging with JSON formatting."""
# Configure standard logging
logging.basicConfig(
format="%(message)s",
stream=sys.stdout,
level=getattr(logging, settings.log_level)
)
# Configure structlog
processors = [
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
add_correlation_id,
add_application_info,
]
if settings.log_format == "json":
processors.append(structlog.processors.JSONRenderer())
else:
processors.append(structlog.dev.ConsoleRenderer())
structlog.configure(
processors=processors,
wrapper_class=structlog.stdlib.BoundLogger,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
logger = structlog.get_logger()
logger.info(
"logging_configured",
log_level=settings.log_level,
log_format=settings.log_format,
environment=settings.environment
)
return logger
def add_correlation_id(_, __, event_dict: Dict[str, Any]) -> Dict[str, Any]:
"""Add correlation ID to log entries for request tracing."""
if 'correlation_id' not in event_dict:
event_dict['correlation_id'] = str(uuid.uuid4())
return event_dict
def add_application_info(_, __, event_dict: Dict[str, Any]) -> Dict[str, Any]:
"""Add application information to log entries."""
event_dict['app_name'] = settings.app_name
event_dict['app_version'] = settings.app_version
event_dict['environment'] = settings.environment
return event_dict
class AuditLogger:
"""Audit logger for compliance requirements."""
def __init__(self, db_client):
self.db_client = db_client
self.logger = structlog.get_logger(__name__)
def log(
self,
event_type: str,
severity: str,
station_id: Optional[str] = None,
pump_id: Optional[str] = None,
user_id: Optional[str] = None,
ip_address: Optional[str] = None,
protocol: Optional[str] = None,
action: Optional[str] = None,
resource: Optional[str] = None,
result: Optional[str] = None,
event_data: Optional[Dict[str, Any]] = None
):
"""Log an audit event to both structured logs and database."""
# Log to structured logs
self.logger.info(
"audit_event",
event_type=event_type,
severity=severity,
station_id=station_id,
pump_id=pump_id,
user_id=user_id,
ip_address=ip_address,
protocol=protocol,
action=action,
resource=resource,
result=result,
event_data=event_data
)
# Log to database if audit logging is enabled
if settings.audit_log_enabled:
try:
query = """
INSERT INTO audit_log
(event_type, severity, station_id, pump_id, user_id, ip_address,
protocol, action, resource, result, event_data)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
self.db_client.execute(
query,
(
event_type, severity, station_id, pump_id, user_id, ip_address,
protocol, action, resource, result, event_data
)
)
except Exception as e:
self.logger.error("audit_log_database_failed", error=str(e))
# Global logger instance
logger = setup_logging()