Skip to main content
Glama

MaverickMCP

by wshobson
MIT License
165
  • Apple
logging_settings.py14.6 kB
""" Structured logging configuration settings for the backtesting system. This module provides centralized configuration for all logging-related settings including debug mode, log levels, output formats, and performance monitoring. """ import os from dataclasses import dataclass from pathlib import Path from typing import Any @dataclass class LoggingSettings: """Comprehensive logging configuration settings.""" # Basic logging configuration log_level: str = "INFO" log_format: str = "json" # json or text enable_async_logging: bool = True console_output: str = "stderr" # stdout or stderr # File logging configuration enable_file_logging: bool = True log_file_path: str = "logs/backtesting.log" enable_log_rotation: bool = True max_log_size_mb: int = 10 backup_count: int = 5 # Debug mode configuration debug_enabled: bool = False verbose_modules: list[str] = None log_request_response: bool = False max_payload_length: int = 1000 # Performance monitoring enable_performance_logging: bool = True performance_log_threshold_ms: float = 1000.0 enable_resource_tracking: bool = True enable_business_metrics: bool = True # Async logging configuration async_log_queue_size: int = 10000 async_log_flush_interval: float = 1.0 # Sensitive data handling mask_sensitive_data: bool = True sensitive_field_patterns: list[str] = None # Remote logging (for future log aggregation) enable_remote_logging: bool = False remote_endpoint: str | None = None remote_api_key: str | None = None # Correlation and tracing enable_correlation_tracking: bool = True correlation_id_header: str = "X-Correlation-ID" enable_request_tracing: bool = True def __post_init__(self): """Initialize default values for mutable fields.""" if self.verbose_modules is None: self.verbose_modules = [] if self.sensitive_field_patterns is None: self.sensitive_field_patterns = [ "password", "token", "key", "secret", "auth", "credential", "bearer", "session", "cookie", "api_key", "access_token", "refresh_token", "private", "confidential", ] @classmethod def from_env(cls) -> "LoggingSettings": """Create logging settings from environment variables.""" return cls( log_level=os.getenv("MAVERICK_LOG_LEVEL", "INFO").upper(), log_format=os.getenv("MAVERICK_LOG_FORMAT", "json").lower(), enable_async_logging=os.getenv("MAVERICK_ASYNC_LOGGING", "true").lower() == "true", console_output=os.getenv("MAVERICK_CONSOLE_OUTPUT", "stderr").lower(), # File logging enable_file_logging=os.getenv("MAVERICK_FILE_LOGGING", "true").lower() == "true", log_file_path=os.getenv("MAVERICK_LOG_FILE", "logs/backtesting.log"), enable_log_rotation=os.getenv("MAVERICK_LOG_ROTATION", "true").lower() == "true", max_log_size_mb=int(os.getenv("MAVERICK_LOG_SIZE_MB", "10")), backup_count=int(os.getenv("MAVERICK_LOG_BACKUPS", "5")), # Debug configuration debug_enabled=os.getenv("MAVERICK_DEBUG", "false").lower() == "true", log_request_response=os.getenv("MAVERICK_LOG_REQUESTS", "false").lower() == "true", max_payload_length=int(os.getenv("MAVERICK_MAX_PAYLOAD", "1000")), # Performance monitoring enable_performance_logging=os.getenv( "MAVERICK_PERF_LOGGING", "true" ).lower() == "true", performance_log_threshold_ms=float( os.getenv("MAVERICK_PERF_THRESHOLD", "1000.0") ), enable_resource_tracking=os.getenv( "MAVERICK_RESOURCE_TRACKING", "true" ).lower() == "true", enable_business_metrics=os.getenv( "MAVERICK_BUSINESS_METRICS", "true" ).lower() == "true", # Async logging async_log_queue_size=int(os.getenv("MAVERICK_LOG_QUEUE_SIZE", "10000")), async_log_flush_interval=float( os.getenv("MAVERICK_LOG_FLUSH_INTERVAL", "1.0") ), # Sensitive data mask_sensitive_data=os.getenv("MAVERICK_MASK_SENSITIVE", "true").lower() == "true", # Remote logging enable_remote_logging=os.getenv("MAVERICK_REMOTE_LOGGING", "false").lower() == "true", remote_endpoint=os.getenv("MAVERICK_REMOTE_LOG_ENDPOINT"), remote_api_key=os.getenv("MAVERICK_REMOTE_LOG_API_KEY"), # Correlation and tracing enable_correlation_tracking=os.getenv( "MAVERICK_CORRELATION_TRACKING", "true" ).lower() == "true", correlation_id_header=os.getenv( "MAVERICK_CORRELATION_HEADER", "X-Correlation-ID" ), enable_request_tracing=os.getenv("MAVERICK_REQUEST_TRACING", "true").lower() == "true", ) def to_dict(self) -> dict[str, Any]: """Convert settings to dictionary for serialization.""" return { "log_level": self.log_level, "log_format": self.log_format, "enable_async_logging": self.enable_async_logging, "console_output": self.console_output, "enable_file_logging": self.enable_file_logging, "log_file_path": self.log_file_path, "enable_log_rotation": self.enable_log_rotation, "max_log_size_mb": self.max_log_size_mb, "backup_count": self.backup_count, "debug_enabled": self.debug_enabled, "verbose_modules": self.verbose_modules, "log_request_response": self.log_request_response, "max_payload_length": self.max_payload_length, "enable_performance_logging": self.enable_performance_logging, "performance_log_threshold_ms": self.performance_log_threshold_ms, "enable_resource_tracking": self.enable_resource_tracking, "enable_business_metrics": self.enable_business_metrics, "async_log_queue_size": self.async_log_queue_size, "async_log_flush_interval": self.async_log_flush_interval, "mask_sensitive_data": self.mask_sensitive_data, "sensitive_field_patterns": self.sensitive_field_patterns, "enable_remote_logging": self.enable_remote_logging, "remote_endpoint": self.remote_endpoint, "enable_correlation_tracking": self.enable_correlation_tracking, "correlation_id_header": self.correlation_id_header, "enable_request_tracing": self.enable_request_tracing, } def ensure_log_directory(self): """Ensure the log directory exists.""" if self.enable_file_logging and self.log_file_path: log_path = Path(self.log_file_path) log_path.parent.mkdir(parents=True, exist_ok=True) def get_debug_modules(self) -> list[str]: """Get list of modules for debug logging.""" if not self.debug_enabled: return [] if not self.verbose_modules: # Default debug modules for backtesting return [ "maverick_mcp.backtesting", "maverick_mcp.api.tools.backtesting", "maverick_mcp.providers", "maverick_mcp.data.cache", ] return self.verbose_modules def should_log_performance(self, duration_ms: float) -> bool: """Check if operation should be logged based on performance threshold.""" if not self.enable_performance_logging: return False return duration_ms >= self.performance_log_threshold_ms def get_log_file_config(self) -> dict[str, Any]: """Get file logging configuration.""" if not self.enable_file_logging: return {} config = { "filename": self.log_file_path, "mode": "a", "encoding": "utf-8", } if self.enable_log_rotation: config.update( { "maxBytes": self.max_log_size_mb * 1024 * 1024, "backupCount": self.backup_count, } ) return config def get_performance_config(self) -> dict[str, Any]: """Get performance monitoring configuration.""" return { "enabled": self.enable_performance_logging, "threshold_ms": self.performance_log_threshold_ms, "resource_tracking": self.enable_resource_tracking, "business_metrics": self.enable_business_metrics, } def get_debug_config(self) -> dict[str, Any]: """Get debug configuration.""" return { "enabled": self.debug_enabled, "verbose_modules": self.get_debug_modules(), "log_request_response": self.log_request_response, "max_payload_length": self.max_payload_length, } # Environment-specific configurations class EnvironmentLogSettings: """Environment-specific logging configurations.""" @staticmethod def development() -> LoggingSettings: """Development environment logging configuration.""" return LoggingSettings( log_level="DEBUG", log_format="text", debug_enabled=True, log_request_response=True, enable_performance_logging=True, performance_log_threshold_ms=100.0, # Lower threshold for development console_output="stdout", enable_file_logging=True, log_file_path="logs/dev_backtesting.log", ) @staticmethod def testing() -> LoggingSettings: """Testing environment logging configuration.""" return LoggingSettings( log_level="WARNING", log_format="text", debug_enabled=False, enable_performance_logging=False, enable_file_logging=False, console_output="stdout", enable_async_logging=False, # Synchronous for tests ) @staticmethod def production() -> LoggingSettings: """Production environment logging configuration.""" return LoggingSettings( log_level="INFO", log_format="json", debug_enabled=False, log_request_response=False, enable_performance_logging=True, performance_log_threshold_ms=2000.0, # Higher threshold for production console_output="stderr", enable_file_logging=True, log_file_path="/var/log/maverick/backtesting.log", enable_log_rotation=True, max_log_size_mb=50, # Larger files in production backup_count=10, enable_remote_logging=True, # Enable for log aggregation ) # Global logging settings instance _logging_settings: LoggingSettings | None = None def get_logging_settings() -> LoggingSettings: """Get global logging settings instance.""" global _logging_settings if _logging_settings is None: environment = os.getenv("MAVERICK_ENVIRONMENT", "development").lower() if environment == "development": _logging_settings = EnvironmentLogSettings.development() elif environment == "testing": _logging_settings = EnvironmentLogSettings.testing() elif environment == "production": _logging_settings = EnvironmentLogSettings.production() else: # Default to environment variables _logging_settings = LoggingSettings.from_env() # Override with any environment variables env_overrides = LoggingSettings.from_env() for key, value in env_overrides.to_dict().items(): if value is not None and value != getattr(LoggingSettings(), key): setattr(_logging_settings, key, value) # Ensure log directory exists _logging_settings.ensure_log_directory() return _logging_settings def configure_logging_for_environment(environment: str) -> LoggingSettings: """Configure logging for specific environment.""" global _logging_settings if environment.lower() == "development": _logging_settings = EnvironmentLogSettings.development() elif environment.lower() == "testing": _logging_settings = EnvironmentLogSettings.testing() elif environment.lower() == "production": _logging_settings = EnvironmentLogSettings.production() else: raise ValueError(f"Unknown environment: {environment}") _logging_settings.ensure_log_directory() return _logging_settings # Logging configuration validation def validate_logging_settings(settings: LoggingSettings) -> list[str]: """Validate logging settings and return list of warnings/errors.""" warnings = [] # Validate log level valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] if settings.log_level not in valid_levels: warnings.append(f"Invalid log level '{settings.log_level}', using INFO") # Validate log format valid_formats = ["json", "text"] if settings.log_format not in valid_formats: warnings.append(f"Invalid log format '{settings.log_format}', using json") # Validate console output valid_outputs = ["stdout", "stderr"] if settings.console_output not in valid_outputs: warnings.append( f"Invalid console output '{settings.console_output}', using stderr" ) # Validate file logging if settings.enable_file_logging: try: log_path = Path(settings.log_file_path) log_path.parent.mkdir(parents=True, exist_ok=True) except Exception as e: warnings.append(f"Cannot create log directory: {e}") # Validate performance settings if settings.performance_log_threshold_ms < 0: warnings.append("Performance threshold cannot be negative, using 1000ms") # Validate async settings if settings.async_log_queue_size < 100: warnings.append("Async log queue size too small, using 1000") return warnings

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wshobson/maverick-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server