Spaces:
Sleeping
Sleeping
File size: 5,194 Bytes
8e0dd55 ddbf24a 8e0dd55 ddbf24a 8e0dd55 ddbf24a 8e0dd55 ddbf24a 8e0dd55 113f6a0 8e0dd55 ddbf24a 8e0dd55 ddbf24a 8e0dd55 ddbf24a 8e0dd55 ddbf24a 8e0dd55 ddbf24a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 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)")
|