Fix: Permission denied error for logs directory on GPU Space
Browse filesCRITICAL FIX for Space hardware migration (CPU -> GPU):
- GPU Spaces run as non-root user (different from CPU)
- Old code failed with 'PermissionError: Permission denied: /app/logs/'
Changes:
1. main.py: Wrap logger.add() in try-except, create logs dir with mkdir
2. worker.py: Same permission handling for worker logs
3. Dockerfile: Create logs directory with 777 permissions (chmod -R 777 logs)
Behavior:
- Try to create and write to logs directory
- If permission denied, gracefully fallback to stdout only
- Log warning but don't crash the application
This allows the app to run on:
- CPU Spaces (root user)
- GPU Spaces (non-root user)
- Any hardware tier without modification
Error fixed:
PermissionError: [Errno 13] Permission denied: '/app/logs/swara_api_2025-11-13.log'
- Dockerfile +3 -2
- app/main.py +18 -7
- app/worker.py +18 -7
|
@@ -22,8 +22,9 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|
| 22 |
# Copy application code
|
| 23 |
COPY . .
|
| 24 |
|
| 25 |
-
# Create necessary directories
|
| 26 |
-
RUN mkdir -p temp models logs
|
|
|
|
| 27 |
|
| 28 |
# Expose port
|
| 29 |
EXPOSE 7860
|
|
|
|
| 22 |
# Copy application code
|
| 23 |
COPY . .
|
| 24 |
|
| 25 |
+
# Create necessary directories with proper permissions
|
| 26 |
+
RUN mkdir -p temp models logs && \
|
| 27 |
+
chmod -R 777 temp logs
|
| 28 |
|
| 29 |
# Expose port
|
| 30 |
EXPOSE 7860
|
|
@@ -2,6 +2,8 @@
|
|
| 2 |
FastAPI Main Application
|
| 3 |
"""
|
| 4 |
import sys
|
|
|
|
|
|
|
| 5 |
from contextlib import asynccontextmanager
|
| 6 |
from fastapi import FastAPI
|
| 7 |
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -20,13 +22,22 @@ logger.add(
|
|
| 20 |
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>",
|
| 21 |
level=settings.LOG_LEVEL
|
| 22 |
)
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
|
| 32 |
@asynccontextmanager
|
|
|
|
| 2 |
FastAPI Main Application
|
| 3 |
"""
|
| 4 |
import sys
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
from contextlib import asynccontextmanager
|
| 8 |
from fastapi import FastAPI
|
| 9 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 22 |
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>",
|
| 23 |
level=settings.LOG_LEVEL
|
| 24 |
)
|
| 25 |
+
|
| 26 |
+
# Create logs directory if it doesn't exist
|
| 27 |
+
try:
|
| 28 |
+
log_dir = Path("logs")
|
| 29 |
+
log_dir.mkdir(parents=True, exist_ok=True)
|
| 30 |
+
|
| 31 |
+
logger.add(
|
| 32 |
+
"logs/swara_api_{time:YYYY-MM-DD}.log",
|
| 33 |
+
rotation="1 day",
|
| 34 |
+
retention="7 days",
|
| 35 |
+
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function} - {message}",
|
| 36 |
+
level=settings.LOG_LEVEL
|
| 37 |
+
)
|
| 38 |
+
except (PermissionError, OSError) as e:
|
| 39 |
+
# If we can't write to logs directory, only use stdout
|
| 40 |
+
logger.warning(f"Cannot create log file: {e}. Using stdout only.")
|
| 41 |
|
| 42 |
|
| 43 |
@asynccontextmanager
|
|
@@ -2,6 +2,8 @@
|
|
| 2 |
RQ Worker Entry Point
|
| 3 |
"""
|
| 4 |
import sys
|
|
|
|
|
|
|
| 5 |
from loguru import logger
|
| 6 |
from redis import Redis
|
| 7 |
from rq import Worker, Queue
|
|
@@ -17,13 +19,22 @@ logger.add(
|
|
| 17 |
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>",
|
| 18 |
level=settings.LOG_LEVEL
|
| 19 |
)
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
|
| 29 |
def main():
|
|
|
|
| 2 |
RQ Worker Entry Point
|
| 3 |
"""
|
| 4 |
import sys
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
from loguru import logger
|
| 8 |
from redis import Redis
|
| 9 |
from rq import Worker, Queue
|
|
|
|
| 19 |
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>",
|
| 20 |
level=settings.LOG_LEVEL
|
| 21 |
)
|
| 22 |
+
|
| 23 |
+
# Create logs directory if it doesn't exist
|
| 24 |
+
try:
|
| 25 |
+
log_dir = Path("logs")
|
| 26 |
+
log_dir.mkdir(parents=True, exist_ok=True)
|
| 27 |
+
|
| 28 |
+
logger.add(
|
| 29 |
+
"logs/swara_worker_{time:YYYY-MM-DD}.log",
|
| 30 |
+
rotation="1 day",
|
| 31 |
+
retention="7 days",
|
| 32 |
+
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function} - {message}",
|
| 33 |
+
level=settings.LOG_LEVEL
|
| 34 |
+
)
|
| 35 |
+
except (PermissionError, OSError) as e:
|
| 36 |
+
# If we can't write to logs directory, only use stdout
|
| 37 |
+
logger.warning(f"Cannot create log file: {e}. Using stdout only.")
|
| 38 |
|
| 39 |
|
| 40 |
def main():
|