FastAPI ist ein fantastisches Web-Framework für den schnellen Aufbau von APIs mit Python. Doch manchmal kann das Logging eine Herausforderung darstellen, besonders wenn man sicherstellen möchte, dass es sowohl lokal mit Uvicorn als auch in der Produktion einwandfrei funktioniert. In diesem Tutorial zeige ich dir, wie du FastAPI-Logging so konfigurierst, dass es sowohl lokal als auch in Produktionsumgebungen zuverlässig arbeitet.
Vorbereitung
Zunächst benötigen wir eine Standard-FastAPI-Anwendung. Erstelle eine Datei namens api.py und füge folgenden Code ein:
from fastapi import FastAPI
import logging
import uvicorn
app = FastAPI(title="api")
LOG = logging.getLogger(__name__)
LOG.info("API is starting up")
LOG.info(uvicorn.Config.asgi_version)
@app.get("/")
async def get_index():
LOG.info("GET /")
return {"Hello": "Api"}
Problemstellung
Beim lokalen Ausführen der Anwendung mit Uvicorn wird das Logging nicht wie erwartet angezeigt:
uvicorn api:app --reload
Dies führt zu den folgenden Informationen in der Konsole:
INFO: Will watch for changes in these directories: ['/Users/user/code/backend/api']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [44258] using StatReload
INFO: Started server process [44260]
INFO: Waiting for application startup.
INFO: Application startup complete.
Aber es fehlen die Startup-Nachrichten, die wir in unserem Code definiert haben.
Lösung
Um das Logging korrekt zu konfigurieren, müssen wir sicherstellen, dass wir sowohl einen StreamHandler als auch einen FileHandler hinzufügen und die Log-Level entsprechend setzen.
Schritt-für-Schritt Anleitung
1. Logging konfigurieren
Erstelle oder modifiziere deine api.py wie folgt:
from fastapi import FastAPI
import logging
import sys
app = FastAPI(title='api')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# StreamHandler für die Konsole
stream_handler = logging.StreamHandler(sys.stdout)
log_formatter = logging.Formatter("%(asctime)s [%(processName)s: %(process)d] [%(threadName)s: %(thread)d] [%(levelname)s] %(name)s: %(message)s")
stream_handler.setFormatter(log_formatter)
logger.addHandler(stream_handler)
logger.info('API is starting up')
@app.get('/')
async def main():
logger.info('GET /')
return 'ok'
2. Uvicorn-Logger nutzen
Wir können auch den Uvicorn-Logger direkt verwenden, um sicherzustellen, dass die Log-Nachrichten korrekt angezeigt werden:
import logging
from fastapi import FastAPI
app = FastAPI(title='api')
logger = logging.getLogger('uvicorn.error')
@app.get('/')
async def main():
logger.debug('this is a debug message')
return 'ok'
3. Log-Level einstellen
Das Log-Level wird durch die Uvicorn-Befehlszeilenoption --log-level debug gesteuert:
uvicorn api:app --reload --log-level debug
Dies wird zu folgenden Ausgaben führen:
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
DEBUG: this is a debug message
4. Produktionsumgebung
In der Produktionsumgebung kannst du Uvicorn mit gunicorn kombinieren, um eine robustere Lösung zu erhalten:
gunicorn -k uvicorn.workers.UvicornWorker api:app --log-level debug
Fazit
Durch das Hinzufügen und Konfigurieren von StreamHandlern und FileHandlern sowie das korrekte Einstellen der Log-Level kannst du sicherstellen, dass deine FastAPI-Anwendung sowohl lokal als auch in der Produktion ordnungsgemäß protokolliert wird. Dies erleichtert das Debuggen und Monitoring deiner Anwendung erheblich.
Viel Erfolg bei der Implementierung und happy coding!
