Spaces:
Sleeping
Sleeping
| import logging | |
| import os | |
| from pathlib import Path | |
| from logging.handlers import RotatingFileHandler | |
| class IgnoreLogChangeDetectedFilter(logging.Filter): | |
| def filter(self, record: logging.LogRecord): | |
| return "Detected file change in" not in record.getMessage() | |
| def setup_logging(format: str = None): | |
| """ | |
| Configure logging for the application with log rotation. | |
| Environment variables: | |
| LOG_LEVEL: Log level (default: INFO) | |
| LOG_FILE_PATH: Path to log file (default: logs/application.log) | |
| LOG_MAX_SIZE: Max size in MB before rotating (default: 10MB) | |
| LOG_BACKUP_COUNT: Number of backup files to keep (default: 5) | |
| DISABLE_FILE_LOGGING: Set to "true" to disable file logging (default: false) | |
| Ensures log directory exists, prevents path traversal, and configures | |
| both rotating file and console handlers. Falls back to console-only logging | |
| if file logging fails due to permissions. | |
| """ | |
| # Check if file logging should be disabled | |
| disable_file_logging = os.environ.get("DISABLE_FILE_LOGGING", "false").lower() == "true" | |
| # Determine log directory and default file path | |
| base_dir = Path(__file__).parent | |
| # In containerized environments, use /tmp for logs if the default location isn't writable | |
| try: | |
| log_dir = base_dir / "logs" | |
| log_dir.mkdir(parents=True, exist_ok=True) | |
| default_log_file = log_dir / "application.log" | |
| except (PermissionError, OSError): | |
| # Fall back to /tmp for containerized environments | |
| log_dir = Path("/tmp/deepwiki-logs") | |
| try: | |
| log_dir.mkdir(parents=True, exist_ok=True) | |
| default_log_file = log_dir / "application.log" | |
| except (PermissionError, OSError): | |
| # If even /tmp fails, disable file logging | |
| disable_file_logging = True | |
| default_log_file = None | |
| # Get log level from environment - default to WARNING to reduce noise in production | |
| log_level_str = os.environ.get("LOG_LEVEL", "WARNING").upper() | |
| log_level = getattr(logging, log_level_str, logging.WARNING) | |
| # Get log file path (only if file logging is enabled) | |
| resolved_path = None | |
| if not disable_file_logging and default_log_file: | |
| log_file_path = Path(os.environ.get("LOG_FILE_PATH", str(default_log_file))) | |
| # Secure path check: must be inside logs/ directory | |
| log_dir_resolved = log_dir.resolve() | |
| resolved_path = log_file_path.resolve() | |
| if not str(resolved_path).startswith(str(log_dir_resolved) + os.sep): | |
| print(f"Warning: LOG_FILE_PATH '{log_file_path}' is outside the trusted log directory '{log_dir_resolved}'. Falling back to console logging.") | |
| disable_file_logging = True | |
| resolved_path = None | |
| # Ensure parent directories exist | |
| if resolved_path: | |
| try: | |
| resolved_path.parent.mkdir(parents=True, exist_ok=True) | |
| except (PermissionError, OSError): | |
| print("Warning: Cannot create log directory. Falling back to console logging.") | |
| disable_file_logging = True | |
| resolved_path = None | |
| # Get max log file size (default: 10MB) | |
| try: | |
| max_mb = int(os.environ.get("LOG_MAX_SIZE", 10)) # 10MB default | |
| max_bytes = max_mb * 1024 * 1024 | |
| except (TypeError, ValueError): | |
| max_bytes = 10 * 1024 * 1024 # fallback to 10MB on error | |
| # Get backup count (default: 5) | |
| try: | |
| backup_count = int(os.environ.get("LOG_BACKUP_COUNT", 5)) | |
| except ValueError: | |
| backup_count = 5 | |
| # Configure format | |
| log_format = format or "%(asctime)s - %(levelname)s - %(name)s - %(filename)s:%(lineno)d - %(message)s" | |
| # Create handlers list | |
| handlers = [] | |
| # Create console handler (always present) | |
| console_handler = logging.StreamHandler() | |
| formatter = logging.Formatter(log_format) | |
| console_handler.setFormatter(formatter) | |
| console_handler.addFilter(IgnoreLogChangeDetectedFilter()) | |
| handlers.append(console_handler) | |
| # Create file handler only if file logging is enabled and path is available | |
| file_handler = None | |
| if not disable_file_logging and resolved_path: | |
| try: | |
| file_handler = RotatingFileHandler(resolved_path, maxBytes=max_bytes, backupCount=backup_count, encoding="utf-8") | |
| file_handler.setFormatter(formatter) | |
| file_handler.addFilter(IgnoreLogChangeDetectedFilter()) | |
| handlers.append(file_handler) | |
| except (PermissionError, OSError) as e: | |
| print(f"Warning: Cannot create file handler: {e}. Using console logging only.") | |
| # Apply logging configuration | |
| logging.basicConfig(level=log_level, handlers=handlers, force=True) | |
| # Log configuration info | |
| logger = logging.getLogger(__name__) | |
| if file_handler and resolved_path: | |
| logger.debug( | |
| f"Logging configured: level={log_level_str}, " | |
| f"file={resolved_path}, max_size={max_bytes} bytes, " | |
| f"backup_count={backup_count}" | |
| ) | |
| else: | |
| logger.debug(f"Logging configured: level={log_level_str}, console only (file logging disabled)") | |