Files
Axel HocksandClaude Opus 4.8 75d28827e8 Initial commit: Oil Trading Bot (MT5, WTI)
Headless FastAPI-Backend (server.py + core/engine.py) mit Mobile-PWA (web/),
Strategie-/Backtest-Suite und Doku. Secrets, DB, Logs und Laufzeit-State sind
via .gitignore ausgeschlossen; Config-Vorlage: oil_widget_config.ini.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 08:29:23 +02:00

89 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
core/logger.py — Zentralisiertes Logging
==========================================
Schreibt sowohl in Konsole als auch in eine rotierende Logdatei.
Wird von allen anderen Modulen über `from core.logger import log` genutzt.
Verwendung:
log.info("Trade BUY 0.5L @ 74.30")
log.warning("Slow API response")
log.error("Order fehlgeschlagen", exc_info=True)
log.debug("Detail-Info") # nur bei DEBUG-Level sichtbar
Vorteile gegenüber print():
- Persistente Datei-Logs (überleben Crashes / pythonw)
- Log-Rotation (max 5 MB pro Datei, 5 Backups)
- Filterbar nach Komponente (logger.getChild('trade'))
- Zeit + Level pro Eintrag
"""
from __future__ import annotations
import logging
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
# ── Konfiguration ──────────────────────────────
LOG_FILE = Path(__file__).parent.parent / "oil_widget.log"
LOG_MAX_BYTES = 5_000_000
LOG_BACKUP_COUNT = 5
# Format mit Komponente, Level, Zeit
_FILE_FORMAT = "%(asctime)s [%(levelname)-7s] %(name)-14s | %(message)s"
_CONSOLE_FORMAT = "[%(name)s] %(message)s"
def _setup_logger(level: int = logging.INFO) -> logging.Logger:
"""Erstellt den Root-Logger 'oil' mit Datei- und Konsolen-Handler."""
logger = logging.getLogger("oil")
logger.setLevel(logging.DEBUG) # Detail-Filter erfolgt pro Handler
logger.propagate = False
# Doppelte Handler vermeiden (z.B. bei Reload)
if logger.handlers:
return logger
# Datei-Handler komplettes Detail
try:
fh = RotatingFileHandler(
LOG_FILE, maxBytes=LOG_MAX_BYTES,
backupCount=LOG_BACKUP_COUNT, encoding="utf-8")
fh.setLevel(logging.DEBUG)
fh.setFormatter(logging.Formatter(_FILE_FORMAT,
datefmt="%Y-%m-%d %H:%M:%S"))
logger.addHandler(fh)
except Exception as e:
# Fallback: kein File-Log möglich (z.B. Read-Only-FS)
sys.stderr.write(f"[Logger] Datei-Logging fehlgeschlagen: {e}\n")
# Konsolen-Handler kompakt
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(level)
ch.setFormatter(logging.Formatter(_CONSOLE_FORMAT))
logger.addHandler(ch)
return logger
# Globaler Root-Logger einmalig instanziiert
log = _setup_logger()
def get_logger(component: str) -> logging.Logger:
"""
Liefert einen Sub-Logger pro Komponente, z.B. 'trade', 'mt5', 'ai'.
Erscheint im Log dann als 'oil.trade'.
"""
return log.getChild(component)
def set_console_level(level: str | int):
"""Erlaubt Runtime-Änderung des Konsolen-Log-Levels."""
if isinstance(level, str):
level = getattr(logging, level.upper(), logging.INFO)
for h in log.handlers:
if isinstance(h, logging.StreamHandler) and not isinstance(
h, RotatingFileHandler):
h.setLevel(level)