Spaces:
Sleeping
Sleeping
Commit ·
ddbf24a
1
Parent(s): 395fb5a
Fix Runtime error
Browse files- Dockerfile +1 -0
- api/logging_config.py +68 -29
Dockerfile
CHANGED
|
@@ -99,6 +99,7 @@ exit $?' > /app/start.sh && chmod +x /app/start.sh
|
|
| 99 |
ENV PORT=8001
|
| 100 |
ENV NODE_ENV=production
|
| 101 |
ENV SERVER_BASE_URL=http://localhost:${PORT:-8001}
|
|
|
|
| 102 |
|
| 103 |
# Create empty .env file (will be overridden if one exists at runtime)
|
| 104 |
RUN touch .env
|
|
|
|
| 99 |
ENV PORT=8001
|
| 100 |
ENV NODE_ENV=production
|
| 101 |
ENV SERVER_BASE_URL=http://localhost:${PORT:-8001}
|
| 102 |
+
ENV DISABLE_FILE_LOGGING=true
|
| 103 |
|
| 104 |
# Create empty .env file (will be overridden if one exists at runtime)
|
| 105 |
RUN touch .env
|
api/logging_config.py
CHANGED
|
@@ -18,31 +18,59 @@ def setup_logging(format: str = None):
|
|
| 18 |
LOG_FILE_PATH: Path to log file (default: logs/application.log)
|
| 19 |
LOG_MAX_SIZE: Max size in MB before rotating (default: 10MB)
|
| 20 |
LOG_BACKUP_COUNT: Number of backup files to keep (default: 5)
|
|
|
|
| 21 |
|
| 22 |
Ensures log directory exists, prevents path traversal, and configures
|
| 23 |
-
both rotating file and console handlers.
|
|
|
|
| 24 |
"""
|
|
|
|
|
|
|
|
|
|
| 25 |
# Determine log directory and default file path
|
| 26 |
base_dir = Path(__file__).parent
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
# Get log level from environment
|
| 32 |
log_level_str = os.environ.get("LOG_LEVEL", "INFO").upper()
|
| 33 |
log_level = getattr(logging, log_level_str, logging.INFO)
|
| 34 |
|
| 35 |
-
# Get log file path
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
# Get max log file size (default: 10MB)
|
| 48 |
try:
|
|
@@ -60,26 +88,37 @@ def setup_logging(format: str = None):
|
|
| 60 |
# Configure format
|
| 61 |
log_format = format or "%(asctime)s - %(levelname)s - %(name)s - %(filename)s:%(lineno)d - %(message)s"
|
| 62 |
|
| 63 |
-
# Create handlers
|
| 64 |
-
|
|
|
|
|
|
|
| 65 |
console_handler = logging.StreamHandler()
|
| 66 |
-
|
| 67 |
-
# Set format for both handlers
|
| 68 |
formatter = logging.Formatter(log_format)
|
| 69 |
-
file_handler.setFormatter(formatter)
|
| 70 |
console_handler.setFormatter(formatter)
|
| 71 |
-
|
| 72 |
-
# Add filter to suppress "Detected file change" messages
|
| 73 |
-
file_handler.addFilter(IgnoreLogChangeDetectedFilter())
|
| 74 |
console_handler.addFilter(IgnoreLogChangeDetectedFilter())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
# Apply logging configuration
|
| 77 |
-
logging.basicConfig(level=log_level, handlers=
|
| 78 |
|
| 79 |
# Log configuration info
|
| 80 |
logger = logging.getLogger(__name__)
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
LOG_FILE_PATH: Path to log file (default: logs/application.log)
|
| 19 |
LOG_MAX_SIZE: Max size in MB before rotating (default: 10MB)
|
| 20 |
LOG_BACKUP_COUNT: Number of backup files to keep (default: 5)
|
| 21 |
+
DISABLE_FILE_LOGGING: Set to "true" to disable file logging (default: false)
|
| 22 |
|
| 23 |
Ensures log directory exists, prevents path traversal, and configures
|
| 24 |
+
both rotating file and console handlers. Falls back to console-only logging
|
| 25 |
+
if file logging fails due to permissions.
|
| 26 |
"""
|
| 27 |
+
# Check if file logging should be disabled
|
| 28 |
+
disable_file_logging = os.environ.get("DISABLE_FILE_LOGGING", "false").lower() == "true"
|
| 29 |
+
|
| 30 |
# Determine log directory and default file path
|
| 31 |
base_dir = Path(__file__).parent
|
| 32 |
+
|
| 33 |
+
# In containerized environments, use /tmp for logs if the default location isn't writable
|
| 34 |
+
try:
|
| 35 |
+
log_dir = base_dir / "logs"
|
| 36 |
+
log_dir.mkdir(parents=True, exist_ok=True)
|
| 37 |
+
default_log_file = log_dir / "application.log"
|
| 38 |
+
except (PermissionError, OSError):
|
| 39 |
+
# Fall back to /tmp for containerized environments
|
| 40 |
+
log_dir = Path("/tmp/deepwiki-logs")
|
| 41 |
+
try:
|
| 42 |
+
log_dir.mkdir(parents=True, exist_ok=True)
|
| 43 |
+
default_log_file = log_dir / "application.log"
|
| 44 |
+
except (PermissionError, OSError):
|
| 45 |
+
# If even /tmp fails, disable file logging
|
| 46 |
+
disable_file_logging = True
|
| 47 |
+
default_log_file = None
|
| 48 |
|
| 49 |
# Get log level from environment
|
| 50 |
log_level_str = os.environ.get("LOG_LEVEL", "INFO").upper()
|
| 51 |
log_level = getattr(logging, log_level_str, logging.INFO)
|
| 52 |
|
| 53 |
+
# Get log file path (only if file logging is enabled)
|
| 54 |
+
resolved_path = None
|
| 55 |
+
if not disable_file_logging and default_log_file:
|
| 56 |
+
log_file_path = Path(os.environ.get("LOG_FILE_PATH", str(default_log_file)))
|
| 57 |
+
|
| 58 |
+
# Secure path check: must be inside logs/ directory
|
| 59 |
+
log_dir_resolved = log_dir.resolve()
|
| 60 |
+
resolved_path = log_file_path.resolve()
|
| 61 |
+
if not str(resolved_path).startswith(str(log_dir_resolved) + os.sep):
|
| 62 |
+
print(f"Warning: LOG_FILE_PATH '{log_file_path}' is outside the trusted log directory '{log_dir_resolved}'. Falling back to console logging.")
|
| 63 |
+
disable_file_logging = True
|
| 64 |
+
resolved_path = None
|
| 65 |
+
|
| 66 |
+
# Ensure parent directories exist
|
| 67 |
+
if resolved_path:
|
| 68 |
+
try:
|
| 69 |
+
resolved_path.parent.mkdir(parents=True, exist_ok=True)
|
| 70 |
+
except (PermissionError, OSError):
|
| 71 |
+
print("Warning: Cannot create log directory. Falling back to console logging.")
|
| 72 |
+
disable_file_logging = True
|
| 73 |
+
resolved_path = None
|
| 74 |
|
| 75 |
# Get max log file size (default: 10MB)
|
| 76 |
try:
|
|
|
|
| 88 |
# Configure format
|
| 89 |
log_format = format or "%(asctime)s - %(levelname)s - %(name)s - %(filename)s:%(lineno)d - %(message)s"
|
| 90 |
|
| 91 |
+
# Create handlers list
|
| 92 |
+
handlers = []
|
| 93 |
+
|
| 94 |
+
# Create console handler (always present)
|
| 95 |
console_handler = logging.StreamHandler()
|
|
|
|
|
|
|
| 96 |
formatter = logging.Formatter(log_format)
|
|
|
|
| 97 |
console_handler.setFormatter(formatter)
|
|
|
|
|
|
|
|
|
|
| 98 |
console_handler.addFilter(IgnoreLogChangeDetectedFilter())
|
| 99 |
+
handlers.append(console_handler)
|
| 100 |
+
|
| 101 |
+
# Create file handler only if file logging is enabled and path is available
|
| 102 |
+
file_handler = None
|
| 103 |
+
if not disable_file_logging and resolved_path:
|
| 104 |
+
try:
|
| 105 |
+
file_handler = RotatingFileHandler(resolved_path, maxBytes=max_bytes, backupCount=backup_count, encoding="utf-8")
|
| 106 |
+
file_handler.setFormatter(formatter)
|
| 107 |
+
file_handler.addFilter(IgnoreLogChangeDetectedFilter())
|
| 108 |
+
handlers.append(file_handler)
|
| 109 |
+
except (PermissionError, OSError) as e:
|
| 110 |
+
print(f"Warning: Cannot create file handler: {e}. Using console logging only.")
|
| 111 |
|
| 112 |
# Apply logging configuration
|
| 113 |
+
logging.basicConfig(level=log_level, handlers=handlers, force=True)
|
| 114 |
|
| 115 |
# Log configuration info
|
| 116 |
logger = logging.getLogger(__name__)
|
| 117 |
+
if file_handler and resolved_path:
|
| 118 |
+
logger.debug(
|
| 119 |
+
f"Logging configured: level={log_level_str}, "
|
| 120 |
+
f"file={resolved_path}, max_size={max_bytes} bytes, "
|
| 121 |
+
f"backup_count={backup_count}"
|
| 122 |
+
)
|
| 123 |
+
else:
|
| 124 |
+
logger.debug(f"Logging configured: level={log_level_str}, console only (file logging disabled)")
|