|
""" |
|
Centralized logging configuration for AgentPress. |
|
|
|
This module provides a unified logging interface with: |
|
- Structured JSON logging for better parsing |
|
- Log levels for different environments |
|
- Correlation IDs for request tracing |
|
- Contextual information for debugging |
|
""" |
|
|
|
import logging |
|
import json |
|
import sys |
|
import os |
|
from datetime import datetime |
|
from typing import Any, Dict, Optional |
|
from contextvars import ContextVar |
|
from functools import wraps |
|
import traceback |
|
from logging.handlers import RotatingFileHandler |
|
|
|
from utils.config import config, EnvMode |
|
|
|
|
|
request_id: ContextVar[str] = ContextVar('request_id', default='') |
|
|
|
class JSONFormatter(logging.Formatter): |
|
"""Custom JSON formatter for structured logging.""" |
|
|
|
def format(self, record: logging.LogRecord) -> str: |
|
"""Format log record as JSON with contextual information.""" |
|
log_data = { |
|
'timestamp': datetime.utcnow().isoformat(), |
|
'level': record.levelname, |
|
'message': record.getMessage(), |
|
'module': record.module, |
|
'function': record.funcName, |
|
'line': record.lineno, |
|
'request_id': request_id.get(), |
|
'thread_id': getattr(record, 'thread_id', None), |
|
'correlation_id': getattr(record, 'correlation_id', None) |
|
} |
|
|
|
|
|
if hasattr(record, 'extra'): |
|
log_data.update(record.extra) |
|
|
|
|
|
if record.exc_info: |
|
log_data['exception'] = { |
|
'type': str(record.exc_info[0].__name__), |
|
'message': str(record.exc_info[1]), |
|
'traceback': traceback.format_exception(*record.exc_info) |
|
} |
|
|
|
return json.dumps(log_data) |
|
|
|
def setup_logger(name: str = 'agentpress') -> logging.Logger: |
|
""" |
|
Set up a centralized logger with both file and console handlers. |
|
|
|
Args: |
|
name: The name of the logger |
|
|
|
Returns: |
|
logging.Logger: Configured logger instance |
|
""" |
|
logger = logging.getLogger(name) |
|
logger.setLevel(logging.DEBUG) |
|
|
|
|
|
log_dir = os.path.join(os.getcwd(), 'logs') |
|
try: |
|
if not os.path.exists(log_dir): |
|
os.makedirs(log_dir) |
|
print(f"Created log directory at: {log_dir}") |
|
except Exception as e: |
|
print(f"Error creating log directory: {e}") |
|
return logger |
|
|
|
|
|
try: |
|
log_file = os.path.join(log_dir, f'{name}_{datetime.now().strftime("%Y%m%d")}.log') |
|
file_handler = RotatingFileHandler( |
|
log_file, |
|
maxBytes=10*1024*1024, |
|
backupCount=5, |
|
encoding='utf-8' |
|
) |
|
file_handler.setLevel(logging.DEBUG) |
|
|
|
|
|
file_formatter = logging.Formatter( |
|
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s' |
|
) |
|
file_handler.setFormatter(file_formatter) |
|
|
|
|
|
logger.addHandler(file_handler) |
|
print(f"Added file handler for: {log_file}") |
|
except Exception as e: |
|
print(f"Error setting up file handler: {e}") |
|
|
|
|
|
try: |
|
console_handler = logging.StreamHandler(sys.stdout) |
|
if config.ENV_MODE == EnvMode.PRODUCTION: |
|
console_handler.setLevel(logging.WARNING) |
|
else: |
|
console_handler.setLevel(logging.INFO) |
|
|
|
console_formatter = logging.Formatter( |
|
'%(asctime)s - %(levelname)s - %(message)s' |
|
) |
|
console_handler.setFormatter(console_formatter) |
|
|
|
|
|
logger.addHandler(console_handler) |
|
print(f"Added console handler with level: {console_handler.level}") |
|
except Exception as e: |
|
print(f"Error setting up console handler: {e}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
return logger |
|
|
|
|
|
logger = setup_logger() |