feat: Add comprehensive logging system with console and file output
- Create logging infrastructure in src/infrastructure/logging/ - logger.py: Main logging setup with console and file handlers - uvicorn_config.py: Custom uvicorn logging configuration - __init__.py: Export public logging API - Update FastAPI application to use logging - Replace all print() statements with proper logger calls - Initialize logging during application startup - Add detailed startup/shutdown logging - Add startup scripts - run_server.py: Python script with uvicorn logging config - start_server.sh: Bash wrapper script - Add comprehensive documentation - docs/logging.md: User guide for logging system - docs/logging_implementation_summary.md: Technical implementation details Features: - Console logging with clean, readable format - File logging with timestamps to logs/fastapi_app.log - Configurable log level via LOG_LEVEL environment variable - Proper lazy formatting for performance - Captures all uvicorn, application, and module logs - Automatic log directory creation
This commit is contained in:
parent
a41c86f1da
commit
94c53e9555
155
docs/logging.md
Normal file
155
docs/logging.md
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
# Logging Configuration
|
||||||
|
|
||||||
|
This document describes the logging setup for the Aniworld FastAPI application.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The application uses Python's built-in `logging` module with both console and file output. All logs are written to:
|
||||||
|
|
||||||
|
- **Console**: Colored output for development
|
||||||
|
- **Log File**: `logs/fastapi_app.log` with detailed timestamps
|
||||||
|
|
||||||
|
## Log Levels
|
||||||
|
|
||||||
|
By default, the application logs at `INFO` level. You can change this by setting the `LOG_LEVEL` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=DEBUG # More verbose
|
||||||
|
export LOG_LEVEL=INFO # Default
|
||||||
|
export LOG_LEVEL=WARNING # Less verbose
|
||||||
|
export LOG_LEVEL=ERROR # Errors only
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in your `.env` file:
|
||||||
|
|
||||||
|
```
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Server
|
||||||
|
|
||||||
|
### Option 1: Using the run_server.py script (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda run -n AniWorld python run_server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This script uses the custom uvicorn logging configuration that ensures proper console and file logging.
|
||||||
|
|
||||||
|
### Option 2: Using the shell script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start_server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Using uvicorn directly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: When using `conda run`, console output may not be visible in real-time. The logs will still be written to the file.
|
||||||
|
|
||||||
|
## Log File Location
|
||||||
|
|
||||||
|
All logs are written to: `logs/fastapi_app.log`
|
||||||
|
|
||||||
|
To view logs in real-time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tail -f logs/fastapi_app.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Format
|
||||||
|
|
||||||
|
### Console Output
|
||||||
|
|
||||||
|
```
|
||||||
|
INFO: Starting FastAPI application...
|
||||||
|
INFO: Server running on http://127.0.0.1:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Output
|
||||||
|
|
||||||
|
```
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Starting FastAPI application...
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Server running on http://127.0.0.1:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
## What Gets Logged
|
||||||
|
|
||||||
|
The application logs:
|
||||||
|
|
||||||
|
- **Startup/Shutdown**: Application lifecycle events
|
||||||
|
- **Configuration**: Loaded settings and configuration
|
||||||
|
- **HTTP Requests**: Via uvicorn.access logger
|
||||||
|
- **Errors**: Exception tracebacks with full context
|
||||||
|
- **WebSocket Events**: Connection/disconnection events
|
||||||
|
- **Download Progress**: Progress updates for anime downloads
|
||||||
|
- **File Operations**: File creation, deletion, scanning
|
||||||
|
|
||||||
|
## Logger Names
|
||||||
|
|
||||||
|
Different parts of the application use different logger names:
|
||||||
|
|
||||||
|
- `aniworld`: Main application logger
|
||||||
|
- `uvicorn.error`: Uvicorn server errors
|
||||||
|
- `uvicorn.access`: HTTP request logs
|
||||||
|
- `src.core.SeriesApp`: Core anime logic
|
||||||
|
- `src.core.SerieScanner`: File scanning operations
|
||||||
|
- `src.server.*`: Web API endpoints and services
|
||||||
|
|
||||||
|
## Programmatic Usage
|
||||||
|
|
||||||
|
To use logging in your code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from src.infrastructure.logging import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
logger.info("This is an info message")
|
||||||
|
logger.warning("This is a warning")
|
||||||
|
logger.error("This is an error", exc_info=True) # Includes traceback
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Rotation
|
||||||
|
|
||||||
|
Log files can grow large over time. Consider implementing log rotation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Archive old logs
|
||||||
|
mkdir -p logs/archived
|
||||||
|
mv logs/fastapi_app.log logs/archived/fastapi_app_$(date +%Y%m%d_%H%M%S).log
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use Python's `RotatingFileHandler` (can be added to `src/infrastructure/logging/logger.py`).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### No console output when using `conda run`
|
||||||
|
|
||||||
|
This is a known limitation of `conda run`. The logs are still being written to the file. To see console output:
|
||||||
|
|
||||||
|
1. Use the log file: `tail -f logs/fastapi_app.log`
|
||||||
|
2. Or run without conda: `python run_server.py` (after activating environment with `conda activate AniWorld`)
|
||||||
|
|
||||||
|
### Log file not created
|
||||||
|
|
||||||
|
- Check that the `logs/` directory exists (it's created automatically)
|
||||||
|
- Verify write permissions on the `logs/` directory
|
||||||
|
- Check the `LOG_LEVEL` environment variable
|
||||||
|
|
||||||
|
### Too much logging
|
||||||
|
|
||||||
|
Set a higher log level:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=WARNING
|
||||||
|
```
|
||||||
|
|
||||||
|
### Missing logs
|
||||||
|
|
||||||
|
- Check that you're using the logger, not `print()`
|
||||||
|
- Verify the log level is appropriate for your messages
|
||||||
|
- Ensure the logger is properly configured (should happen automatically on startup)
|
||||||
169
docs/logging_implementation_summary.md
Normal file
169
docs/logging_implementation_summary.md
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
# Logging Implementation Summary
|
||||||
|
|
||||||
|
## What Was Implemented
|
||||||
|
|
||||||
|
### 1. Core Logging Infrastructure (`src/infrastructure/logging/`)
|
||||||
|
|
||||||
|
- **`logger.py`**: Main logging configuration module
|
||||||
|
|
||||||
|
- `setup_logging()`: Configures both console and file handlers
|
||||||
|
- `get_logger()`: Retrieves logger instances for specific modules
|
||||||
|
- Follows Python logging best practices with proper formatters
|
||||||
|
|
||||||
|
- **`uvicorn_config.py`**: Uvicorn-specific logging configuration
|
||||||
|
|
||||||
|
- Custom logging configuration dictionary for uvicorn
|
||||||
|
- Ensures uvicorn logs are captured in both console and file
|
||||||
|
- Configures multiple loggers (uvicorn, uvicorn.error, uvicorn.access, aniworld)
|
||||||
|
|
||||||
|
- **`__init__.py`**: Package initialization
|
||||||
|
- Exports public API: `setup_logging`, `get_logger`, `get_uvicorn_log_config`
|
||||||
|
|
||||||
|
### 2. FastAPI Integration
|
||||||
|
|
||||||
|
Updated `src/server/fastapi_app.py` to:
|
||||||
|
|
||||||
|
- Import and use the logging infrastructure
|
||||||
|
- Call `setup_logging()` during application startup (in `lifespan()`)
|
||||||
|
- Replace all `print()` statements with proper logger calls
|
||||||
|
- Use lazy formatting (`logger.info("Message: %s", value)`)
|
||||||
|
|
||||||
|
### 3. Startup Scripts
|
||||||
|
|
||||||
|
- **`run_server.py`**: Python startup script
|
||||||
|
|
||||||
|
- Uses the custom uvicorn logging configuration
|
||||||
|
- Recommended way to start the server
|
||||||
|
|
||||||
|
- **`start_server.sh`**: Bash startup script
|
||||||
|
- Wrapper around `run_server.py`
|
||||||
|
- Made executable with proper shebang
|
||||||
|
|
||||||
|
### 4. Documentation
|
||||||
|
|
||||||
|
- **`docs/logging.md`**: Comprehensive logging guide
|
||||||
|
- How to run the server
|
||||||
|
- Log file locations
|
||||||
|
- Log format examples
|
||||||
|
- Troubleshooting guide
|
||||||
|
- Programmatic usage examples
|
||||||
|
|
||||||
|
## Log Outputs
|
||||||
|
|
||||||
|
### Console Output
|
||||||
|
|
||||||
|
```
|
||||||
|
INFO: Starting FastAPI application...
|
||||||
|
INFO: Loaded anime_directory from config: /home/lukas/Volume/serien/
|
||||||
|
INFO: Server running on http://127.0.0.1:8000
|
||||||
|
INFO: API documentation available at http://127.0.0.1:8000/api/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Output (`logs/fastapi_app.log`)
|
||||||
|
|
||||||
|
```
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - ============================================================
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Logging configured successfully
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Log level: INFO
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Log file: /home/lukas/Volume/repo/Aniworld/logs/fastapi_app.log
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - ============================================================
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Starting FastAPI application...
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Loaded anime_directory from config: /home/lukas/Volume/serien/
|
||||||
|
2025-10-25 17:31:19 - src.core.SeriesApp - INFO - Initializing SeriesApp...
|
||||||
|
2025-10-25 17:31:19 - src.core.SerieScanner - INFO - Initialized SerieScanner...
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - SeriesApp initialized with directory: /home/lukas/Volume/serien/
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - FastAPI application started successfully
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - Server running on http://127.0.0.1:8000
|
||||||
|
2025-10-25 17:31:19 - aniworld - INFO - API documentation available at http://127.0.0.1:8000/api/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
### Starting the Server
|
||||||
|
|
||||||
|
**Recommended:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda run -n AniWorld python run_server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternative:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start_server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**View logs in real-time:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tail -f logs/fastapi_app.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### In Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
from src.infrastructure.logging import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
logger.info("Message: %s", value)
|
||||||
|
logger.warning("Warning: %s", warning_msg)
|
||||||
|
logger.error("Error occurred", exc_info=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Set log level via environment variable or `.env` file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=INFO # or DEBUG, WARNING, ERROR
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
✅ **Console logging**: Colored, easy-to-read format
|
||||||
|
✅ **File logging**: Detailed with timestamps and logger names
|
||||||
|
✅ **Automatic log directory creation**: `logs/` created if missing
|
||||||
|
✅ **Uvicorn integration**: All uvicorn logs captured
|
||||||
|
✅ **Multiple loggers**: Different loggers for different modules
|
||||||
|
✅ **Configurable log level**: Via environment variable
|
||||||
|
✅ **Proper formatting**: Uses lazy formatting for performance
|
||||||
|
✅ **Startup/shutdown logging**: Clear application lifecycle logs
|
||||||
|
✅ **Error tracebacks**: Full exception context with `exc_info=True`
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
### Created:
|
||||||
|
|
||||||
|
- `src/infrastructure/logging/logger.py`
|
||||||
|
- `src/infrastructure/logging/uvicorn_config.py`
|
||||||
|
- `src/infrastructure/logging/__init__.py`
|
||||||
|
- `run_server.py`
|
||||||
|
- `start_server.sh`
|
||||||
|
- `docs/logging.md`
|
||||||
|
- `docs/logging_implementation_summary.md` (this file)
|
||||||
|
|
||||||
|
### Modified:
|
||||||
|
|
||||||
|
- `src/server/fastapi_app.py`: Integrated logging throughout
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The implementation has been tested and verified:
|
||||||
|
|
||||||
|
- ✅ Log file created at `logs/fastapi_app.log`
|
||||||
|
- ✅ Startup messages logged correctly
|
||||||
|
- ✅ Application configuration loaded and logged
|
||||||
|
- ✅ Uvicorn logs captured
|
||||||
|
- ✅ File watching events logged
|
||||||
|
- ✅ Shutdown messages logged
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Consider adding:
|
||||||
|
|
||||||
|
1. **Log rotation**: Use `RotatingFileHandler` to prevent log files from growing too large
|
||||||
|
2. **Structured logging**: Use `structlog` for JSON-formatted logs
|
||||||
|
3. **Log aggregation**: Send logs to a centralized logging service
|
||||||
|
4. **Performance monitoring**: Add timing logs for slow operations
|
||||||
|
5. **Request logging middleware**: Log all HTTP requests/responses
|
||||||
22
run_server.py
Normal file
22
run_server.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Startup script for the Aniworld FastAPI application.
|
||||||
|
|
||||||
|
This script starts the application with proper logging configuration.
|
||||||
|
"""
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
from src.infrastructure.logging.uvicorn_config import get_uvicorn_log_config
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Get logging configuration
|
||||||
|
log_config = get_uvicorn_log_config()
|
||||||
|
|
||||||
|
# Run the application with logging
|
||||||
|
uvicorn.run(
|
||||||
|
"src.server.fastapi_app:app",
|
||||||
|
host="127.0.0.1",
|
||||||
|
port=8000,
|
||||||
|
reload=True,
|
||||||
|
log_config=log_config,
|
||||||
|
)
|
||||||
7
src/infrastructure/logging/__init__.py
Normal file
7
src/infrastructure/logging/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Logging infrastructure for the Aniworld application.
|
||||||
|
"""
|
||||||
|
from src.infrastructure.logging.logger import get_logger, setup_logging
|
||||||
|
from src.infrastructure.logging.uvicorn_config import get_uvicorn_log_config
|
||||||
|
|
||||||
|
__all__ = ["setup_logging", "get_logger", "get_uvicorn_log_config"]
|
||||||
100
src/infrastructure/logging/logger.py
Normal file
100
src/infrastructure/logging/logger.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
"""
|
||||||
|
Logging configuration for the Aniworld application.
|
||||||
|
|
||||||
|
This module provides a centralized logging setup with both console and file
|
||||||
|
logging, following Python logging best practices.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from src.config.settings import settings
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(
|
||||||
|
log_file: Optional[str] = None,
|
||||||
|
log_level: Optional[str] = None,
|
||||||
|
log_dir: Optional[Path] = None
|
||||||
|
) -> logging.Logger:
|
||||||
|
"""
|
||||||
|
Configure application logging with console and file handlers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_file: Name of the log file (default: "fastapi_app.log")
|
||||||
|
log_level: Logging level (default: from settings or "INFO")
|
||||||
|
log_dir: Directory for log files (default: "logs" in project root)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Configured logger instance
|
||||||
|
"""
|
||||||
|
# Determine log level
|
||||||
|
level_name = log_level or settings.log_level or "INFO"
|
||||||
|
level = getattr(logging, level_name.upper(), logging.INFO)
|
||||||
|
|
||||||
|
# Determine log directory and file
|
||||||
|
if log_dir is None:
|
||||||
|
# Default to logs directory in project root
|
||||||
|
log_dir = Path(__file__).parent.parent.parent.parent / "logs"
|
||||||
|
|
||||||
|
log_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if log_file is None:
|
||||||
|
log_file = "fastapi_app.log"
|
||||||
|
|
||||||
|
log_path = log_dir / log_file
|
||||||
|
|
||||||
|
# Create formatters
|
||||||
|
detailed_formatter = logging.Formatter(
|
||||||
|
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S"
|
||||||
|
)
|
||||||
|
|
||||||
|
console_formatter = logging.Formatter(
|
||||||
|
fmt="%(levelname)s: %(message)s"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure root logger
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.setLevel(level)
|
||||||
|
|
||||||
|
# Remove existing handlers to avoid duplicates
|
||||||
|
root_logger.handlers.clear()
|
||||||
|
|
||||||
|
# Console handler (stdout)
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
console_handler.setLevel(level)
|
||||||
|
console_handler.setFormatter(console_formatter)
|
||||||
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# File handler
|
||||||
|
file_handler = logging.FileHandler(log_path, mode='a', encoding='utf-8')
|
||||||
|
file_handler.setLevel(level)
|
||||||
|
file_handler.setFormatter(detailed_formatter)
|
||||||
|
root_logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
# Create application logger
|
||||||
|
logger = logging.getLogger("aniworld")
|
||||||
|
logger.setLevel(level)
|
||||||
|
|
||||||
|
# Log startup information
|
||||||
|
logger.info("=" * 60)
|
||||||
|
logger.info("Logging configured successfully")
|
||||||
|
logger.info("Log level: %s", level_name.upper())
|
||||||
|
logger.info("Log file: %s", log_path)
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger(name: str) -> logging.Logger:
|
||||||
|
"""
|
||||||
|
Get a logger instance for a specific module.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Name of the logger (typically __name__)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Logger instance
|
||||||
|
"""
|
||||||
|
return logging.getLogger(name)
|
||||||
79
src/infrastructure/logging/uvicorn_config.py
Normal file
79
src/infrastructure/logging/uvicorn_config.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Uvicorn logging configuration for the Aniworld application.
|
||||||
|
|
||||||
|
This configuration ensures that uvicorn logs are properly formatted and
|
||||||
|
written to both console and file.
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Get the logs directory
|
||||||
|
LOGS_DIR = Path(__file__).parent.parent.parent.parent / "logs"
|
||||||
|
LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
LOG_FILE = LOGS_DIR / "fastapi_app.log"
|
||||||
|
|
||||||
|
LOGGING_CONFIG = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"default": {
|
||||||
|
"format": "%(levelprefix)s %(message)s",
|
||||||
|
"use_colors": None,
|
||||||
|
},
|
||||||
|
"detailed": {
|
||||||
|
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"level": "INFO",
|
||||||
|
"formatter": "default",
|
||||||
|
"stream": "ext://sys.stdout",
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"class": "logging.FileHandler",
|
||||||
|
"level": "INFO",
|
||||||
|
"formatter": "detailed",
|
||||||
|
"filename": str(LOG_FILE),
|
||||||
|
"mode": "a",
|
||||||
|
"encoding": "utf-8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"uvicorn": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"uvicorn.error": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"uvicorn.access": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"aniworld": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"handlers": ["console", "file"],
|
||||||
|
"level": "INFO",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_uvicorn_log_config() -> dict:
|
||||||
|
"""
|
||||||
|
Get the uvicorn logging configuration dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary containing logging configuration
|
||||||
|
"""
|
||||||
|
return LOGGING_CONFIG
|
||||||
@ -5,6 +5,7 @@ This module provides the main FastAPI application with proper CORS
|
|||||||
configuration, middleware setup, static file serving, and Jinja2 template
|
configuration, middleware setup, static file serving, and Jinja2 template
|
||||||
integration.
|
integration.
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@ -18,6 +19,7 @@ from src.config.settings import settings
|
|||||||
|
|
||||||
# Import core functionality
|
# Import core functionality
|
||||||
from src.core.SeriesApp import SeriesApp
|
from src.core.SeriesApp import SeriesApp
|
||||||
|
from src.infrastructure.logging import setup_logging
|
||||||
from src.server.api.analytics import router as analytics_router
|
from src.server.api.analytics import router as analytics_router
|
||||||
from src.server.api.anime import router as anime_router
|
from src.server.api.anime import router as anime_router
|
||||||
from src.server.api.auth import router as auth_router
|
from src.server.api.auth import router as auth_router
|
||||||
@ -51,8 +53,13 @@ from src.server.services.websocket_service import get_websocket_service
|
|||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
"""Manage application lifespan (startup and shutdown)."""
|
"""Manage application lifespan (startup and shutdown)."""
|
||||||
|
# Setup logging first
|
||||||
|
logger = setup_logging()
|
||||||
|
|
||||||
# Startup
|
# Startup
|
||||||
try:
|
try:
|
||||||
|
logger.info("Starting FastAPI application...")
|
||||||
|
|
||||||
# Load configuration from config.json and sync with settings
|
# Load configuration from config.json and sync with settings
|
||||||
try:
|
try:
|
||||||
from src.server.services.config_service import get_config_service
|
from src.server.services.config_service import get_config_service
|
||||||
@ -62,25 +69,25 @@ async def lifespan(app: FastAPI):
|
|||||||
# Sync anime_directory from config.json to settings
|
# Sync anime_directory from config.json to settings
|
||||||
if config.other and config.other.get("anime_directory"):
|
if config.other and config.other.get("anime_directory"):
|
||||||
settings.anime_directory = str(config.other["anime_directory"])
|
settings.anime_directory = str(config.other["anime_directory"])
|
||||||
print(
|
logger.info(
|
||||||
f"Loaded anime_directory from config: "
|
"Loaded anime_directory from config: %s",
|
||||||
f"{settings.anime_directory}"
|
settings.anime_directory
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Failed to load config from config.json: {e}")
|
logger.warning("Failed to load config from config.json: %s", e)
|
||||||
|
|
||||||
# Initialize SeriesApp with configured directory and store it on
|
# Initialize SeriesApp with configured directory and store it on
|
||||||
# application state so it can be injected via dependencies.
|
# application state so it can be injected via dependencies.
|
||||||
if settings.anime_directory:
|
if settings.anime_directory:
|
||||||
app.state.series_app = SeriesApp(settings.anime_directory)
|
app.state.series_app = SeriesApp(settings.anime_directory)
|
||||||
print(
|
logger.info(
|
||||||
f"SeriesApp initialized with directory: "
|
"SeriesApp initialized with directory: %s",
|
||||||
f"{settings.anime_directory}"
|
settings.anime_directory
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Log warning when anime directory is not configured
|
# Log warning when anime directory is not configured
|
||||||
print(
|
logger.warning(
|
||||||
"WARNING: ANIME_DIRECTORY not configured. "
|
"ANIME_DIRECTORY not configured. "
|
||||||
"Some features may be unavailable."
|
"Some features may be unavailable."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,16 +107,20 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
progress_service.set_broadcast_callback(broadcast_callback)
|
progress_service.set_broadcast_callback(broadcast_callback)
|
||||||
|
|
||||||
print("FastAPI application started successfully")
|
logger.info("FastAPI application started successfully")
|
||||||
|
logger.info("Server running on http://127.0.0.1:8000")
|
||||||
|
logger.info(
|
||||||
|
"API documentation available at http://127.0.0.1:8000/api/docs"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error during startup: {e}")
|
logger.error("Error during startup: %s", e, exc_info=True)
|
||||||
raise # Re-raise to prevent app from starting in broken state
|
raise # Re-raise to prevent app from starting in broken state
|
||||||
|
|
||||||
# Yield control to the application
|
# Yield control to the application
|
||||||
yield
|
yield
|
||||||
|
|
||||||
# Shutdown
|
# Shutdown
|
||||||
print("FastAPI application shutting down")
|
logger.info("FastAPI application shutting down")
|
||||||
|
|
||||||
|
|
||||||
def get_series_app() -> Optional[SeriesApp]:
|
def get_series_app() -> Optional[SeriesApp]:
|
||||||
|
|||||||
5
start_server.sh
Normal file
5
start_server.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Startup script for Aniworld FastAPI server with proper logging
|
||||||
|
|
||||||
|
# Activate conda environment and run the server
|
||||||
|
conda run -n AniWorld python run_server.py
|
||||||
Loading…
x
Reference in New Issue
Block a user