Files
virtual_board_member/app/core/logging.py
2025-08-07 16:11:14 -04:00

158 lines
4.7 KiB
Python

"""
Structured logging configuration for the Virtual Board Member AI System.
"""
import logging
import sys
from typing import Any, Dict
import structlog
from structlog.stdlib import LoggerFactory
from structlog.processors import (
TimeStamper,
JSONRenderer,
format_exc_info,
add_log_level,
StackInfoRenderer,
)
from structlog.types import Processor
from app.core.config import settings
def setup_logging() -> None:
"""Setup structured logging configuration."""
# Configure standard library logging
logging.basicConfig(
format="%(message)s",
stream=sys.stdout,
level=getattr(logging, settings.LOG_LEVEL.upper()),
)
# Configure structlog
structlog.configure(
processors=[
# Add timestamp
TimeStamper(fmt="iso"),
# Add log level
add_log_level,
# Add stack info
StackInfoRenderer(),
# Add exception info
format_exc_info,
# Add caller info
structlog.processors.CallsiteParameterAdder(
parameters={
structlog.processors.CallsiteParameter.FILENAME,
structlog.processors.CallsiteParameter.FUNC_NAME,
structlog.processors.CallsiteParameter.LINENO,
}
),
# Add process info
structlog.stdlib.add_log_level_number,
# Add service info
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
context_class=dict,
logger_factory=LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Configure formatter
formatter = structlog.stdlib.ProcessorFormatter(
processor=structlog.dev.ConsoleRenderer() if settings.DEBUG else JSONRenderer(),
foreign_pre_chain=[
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
],
)
# Configure root logger
root_logger = logging.getLogger()
root_logger.handlers.clear()
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
root_logger.addHandler(handler)
root_logger.setLevel(getattr(logging, settings.LOG_LEVEL.upper()))
def get_logger(name: str = None) -> structlog.BoundLogger:
"""Get a structured logger instance."""
return structlog.get_logger(name)
class AuditLogger:
"""Audit logging for compliance and security events."""
def __init__(self):
self.logger = get_logger("audit")
def log_user_login(self, user_id: str, ip_address: str, success: bool, **kwargs) -> None:
"""Log user login attempt."""
self.logger.info(
"User login attempt",
event_type="user_login",
user_id=user_id,
ip_address=ip_address,
success=success,
**kwargs
)
def log_document_access(self, user_id: str, document_id: str, action: str, **kwargs) -> None:
"""Log document access."""
self.logger.info(
"Document access",
event_type="document_access",
user_id=user_id,
document_id=document_id,
action=action,
**kwargs
)
def log_query_execution(self, user_id: str, query: str, response_time: float, **kwargs) -> None:
"""Log query execution."""
self.logger.info(
"Query execution",
event_type="query_execution",
user_id=user_id,
query=query,
response_time=response_time,
**kwargs
)
def log_commitment_extraction(self, document_id: str, commitments_count: int, **kwargs) -> None:
"""Log commitment extraction."""
self.logger.info(
"Commitment extraction",
event_type="commitment_extraction",
document_id=document_id,
commitments_count=commitments_count,
**kwargs
)
def log_security_event(self, event_type: str, severity: str, details: Dict[str, Any]) -> None:
"""Log security events."""
self.logger.warning(
"Security event",
event_type="security_event",
security_event_type=event_type,
severity=severity,
details=details
)
# Create audit logger instance
audit_logger = AuditLogger()