Initial commit: Virtual Board Member AI System foundation

This commit is contained in:
Jonathan Pressnell
2025-08-07 16:11:14 -04:00
commit fbfe940a45
47 changed files with 7332 additions and 0 deletions

157
app/core/logging.py Normal file
View File

@@ -0,0 +1,157 @@
"""
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()