Skip to content

Commit eb6688d

Browse files
authored
feat/chore: commonizing logging into one spot (#180)
1 parent 187751b commit eb6688d

File tree

7 files changed

+103
-35
lines changed

7 files changed

+103
-35
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,42 @@ If you want to use embedding models compatible with the OpenAI API (such as Alib
255255

256256
This allows you to seamlessly switch to any OpenAI-compatible embedding service without code changes.
257257

258+
### Logging
259+
260+
DeepWiki uses Python's built-in `logging` module for diagnostic output. You can configure the verbosity and log file destination via environment variables:
261+
262+
| Variable | Description | Default |
263+
|-----------------|--------------------------------------------------------------------|------------------------------|
264+
| `LOG_LEVEL` | Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). | INFO |
265+
| `LOG_FILE_PATH` | Path to the log file. If set, logs will be written to this file. | `api/logs/application.log` |
266+
267+
To enable debug logging and direct logs to a custom file:
268+
```bash
269+
export LOG_LEVEL=DEBUG
270+
export LOG_FILE_PATH=./debug.log
271+
python -m api.main
272+
```
273+
Or with Docker Compose:
274+
```bash
275+
LOG_LEVEL=DEBUG LOG_FILE_PATH=./debug.log docker-compose up
276+
```
277+
278+
When running with Docker Compose, the container's `api/logs` directory is bind-mounted to `./api/logs` on your host (see the `volumes` section in `docker-compose.yml`), ensuring log files persist across restarts.
279+
280+
Alternatively, you can store these settings in your `.env` file:
281+
282+
```bash
283+
LOG_LEVEL=DEBUG
284+
LOG_FILE_PATH=./debug.log
285+
```
286+
Then simply run:
287+
288+
```bash
289+
docker-compose up
290+
```
291+
292+
**Logging Path Security Considerations:** In production environments, ensure the `api/logs` directory and any custom log file path are secured with appropriate filesystem permissions and access controls. The application enforces that `LOG_FILE_PATH` resides within the project's `api/logs` directory to prevent path traversal or unauthorized writes.
293+
258294
## 🛠️ Advanced Setup
259295

260296
### Environment Variables

api/logging_config.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import logging
2+
import os
3+
from pathlib import Path
4+
5+
6+
def setup_logging(format: str = None):
7+
"""
8+
Configure logging for the application.
9+
Reads LOG_LEVEL and LOG_FILE_PATH from environment (defaults: INFO, logs/application.log).
10+
Ensures log directory exists, and configures both file and console handlers.
11+
"""
12+
# Determine log directory and default file path
13+
base_dir = Path(__file__).parent
14+
log_dir = base_dir / "logs"
15+
log_dir.mkdir(parents=True, exist_ok=True)
16+
default_log_file = log_dir / "application.log"
17+
18+
# Get log level and file path from environment
19+
log_level_str = os.environ.get("LOG_LEVEL", "INFO").upper()
20+
log_level = getattr(logging, log_level_str, logging.INFO)
21+
log_file_path = Path(os.environ.get(
22+
"LOG_FILE_PATH", str(default_log_file)))
23+
24+
# ensure log_file_path is within the project's logs directory to prevent path traversal
25+
log_dir_resolved = log_dir.resolve()
26+
resolved_path = log_file_path.resolve()
27+
if not str(resolved_path).startswith(str(log_dir_resolved) + os.sep):
28+
raise ValueError(
29+
f"LOG_FILE_PATH '{log_file_path}' is outside the trusted log directory '{log_dir_resolved}'"
30+
)
31+
# Ensure parent dirs exist for the log file
32+
resolved_path.parent.mkdir(parents=True, exist_ok=True)
33+
34+
# Configure logging handlers and format
35+
logging.basicConfig(
36+
level=log_level,
37+
format=format or "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
38+
handlers=[
39+
logging.FileHandler(resolved_path),
40+
logging.StreamHandler()
41+
],
42+
force=True
43+
)
44+
45+
# Initial debug message to confirm configuration
46+
logger = logging.getLogger(__name__)
47+
logger.debug(f"Log level set to {log_level_str}, log file: {resolved_path}")

api/main.py

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,10 @@
77
# Load environment variables from .env file
88
load_dotenv()
99

10-
# --- Unified Logging Configuration ---
11-
# Determine the project's base directory (assuming main.py is in 'api' subdirectory)
12-
# Adjust if your structure is different, e.g., if main.py is at the root.
13-
# This assumes 'api/main.py', so logs will be in 'api/logs/application.log'
14-
LOG_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logs")
15-
os.makedirs(LOG_DIR, exist_ok=True)
16-
LOG_FILE_PATH = os.path.join(LOG_DIR, "application.log")
17-
18-
logging.basicConfig(
19-
level=logging.INFO,
20-
format="%(asctime)s - %(lineno)d %(filename)s:%(funcName)s - %(levelname)s - %(message)s",
21-
handlers=[
22-
logging.FileHandler(LOG_FILE_PATH),
23-
logging.StreamHandler() # Also keep logging to console
24-
],
25-
force=True # Ensure this configuration takes precedence and clears any existing handlers
26-
)
27-
28-
# Get a logger for this main module (optional, but good practice)
10+
from api.logging_config import setup_logging
11+
12+
# Setup logging with detailed format for main
13+
setup_logging(format="%(asctime)s - %(lineno)d %(filename)s:%(funcName)s - %(levelname)s - %(message)s")
2914
logger = logging.getLogger(__name__)
3015

3116
# Add the current directory to the path so we can import the api package

api/ollama_patch.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
from adalflow.core.types import Document
77
from adalflow.core.component import DataComponent
88

9-
# Configure logging
10-
logging.basicConfig(
11-
level=logging.INFO,
12-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
13-
)
9+
# Unified logging setup
10+
from api.logging_config import setup_logging
11+
12+
setup_logging()
1413
logger = logging.getLogger(__name__)
1514

1615
class OllamaDocumentProcessor(DataComponent):

api/simple_chat.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@
1818
from api.bedrock_client import BedrockClient
1919
from api.rag import RAG
2020

21-
# Configure logging
22-
logging.basicConfig(
23-
level=logging.INFO,
24-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
25-
)
21+
# Unified logging setup
22+
from api.logging_config import setup_logging
23+
24+
setup_logging()
2625
logger = logging.getLogger(__name__)
2726

2827
# Get API keys from environment variables

api/websocket_wiki.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@
1515
from api.openrouter_client import OpenRouterClient
1616
from api.rag import RAG
1717

18-
# Configure logging
19-
logging.basicConfig(
20-
level=logging.INFO,
21-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
22-
)
18+
# Unified logging setup
19+
from api.logging_config import setup_logging
20+
21+
setup_logging()
2322
logger = logging.getLogger(__name__)
2423

2524
# Get API keys from environment variables

docker-compose.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ services:
1515
- NODE_ENV=production
1616
- SERVER_BASE_URL=http://localhost:${PORT:-8001}
1717
- NEXT_PUBLIC_SERVER_BASE_URL=http://localhost:${PORT:-8001}
18+
- LOG_LEVEL=${LOG_LEVEL:-INFO}
19+
- LOG_FILE_PATH=${LOG_FILE_PATH:-api/logs/application.log}
1820
volumes:
19-
- ~/.adalflow:/root/.adalflow # Persist repository and embedding data
21+
- ~/.adalflow:/root/.adalflow # Persist repository and embedding data
22+
- ./api/logs:/app/api/logs # Persist log files across container restarts
2023
# Resource limits for docker-compose up (not Swarm mode)
2124
mem_limit: 6g
2225
mem_reservation: 2g

0 commit comments

Comments
 (0)