---
title: "Python-Logging einrichten mit den richtigen Log-Leveln und Exception-Erfassung"
description: "Python logging gibt ohne Konfiguration nur WARNING und höher aus. Hier die Einrichtung in 3 Zeilen, die 5 Log-Level und das Loggen einer Exception mit Traceback, getestet auf Python 3.13."
canonical: "https://logbrew.co/de/guides/python-logging"
last_updated: "2026-11-10"
---

# Python-Logging einrichten mit den richtigen Log-Leveln und Exception-Erfassung

Python `logging` gibt ohne Konfiguration nur WARNING und höher aus. Hier die Einrichtung in 3 Zeilen, die 5 Log-Level und das Loggen einer Exception mit Traceback, getestet auf Python 3.13.

Getestet am 10. November 2026 mit Python 3.13.5 und logbrew-sdk 0.1.29

Von [Furkan Erday](https://github.com/furkanerday). Furkan Erday hat LogBrew gegründet. Er entwickelt LogBrew jeden Tag mit Claude Code und Codex.

Canonical: https://logbrew.co/de/guides/python-logging

## Was jeder Python-Log-Level ohne Einrichtung tut, und in LogBrew

| Logging-Aufruf | Ohne Einrichtung ausgegeben | Eigener Level in LogBrew |
| --- | --- | --- |
| `log.debug()` | Verworfen | Zusammengelegt in `info` |
| `log.info()` | Verworfen | `info` |
| `log.warning()` | `WARNING:root:` | `warning` |
| `log.error()` | `ERROR:root:` | `error` |
| `log.critical()` | `CRITICAL:root:` | `critical` |

5 Aufrufe auf dem Root-Logger ohne Konfiguration, dann dieselben 5 über den LogBrew-Handler. Eine volle Tasse heißt ausgegeben, oder als eigener Level behalten.

## Logging in 3 Zeilen einrichten

### basicConfig() einmal aufrufen, vor dem ersten Log-Aufruf

`basicConfig()` setzt den Level und das Zeilenformat des Root-Loggers. Ohne Einrichtung gibt Python nur WARNING und höher aus, als `WARNING:root:keg below 10%`, und verwirft DEBUG und INFO.

```bash
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
log = logging.getLogger("taproom")

log.debug("keg pressure read")
log.info("tap opened")
log.warning("keg below 10%")
log.error("keg empty")
log.critical("cooler offline")
```

Ausgabe

```text
2026-11-10 08:34:04,828 INFO taproom tap opened
2026-11-10 08:34:04,828 WARNING taproom keg below 10%
2026-11-10 08:34:04,828 ERROR taproom keg empty
2026-11-10 08:34:04,828 CRITICAL taproom cooler offline
```

### Pro Modul einen Logger benennen

`logging.getLogger(__name__)` gibt jedem Modul seinen eigenen Logger, sodass jede Zeile das Modul nennt, aus dem sie kommt. Den Root-Logger nur für `basicConfig()` verwenden.

### Den Level aus der Umgebung lesen

`basicConfig()` nimmt einen Level-Namen als Text an. Starte das mit `LOG_LEVEL=DEBUG`, und die Debug-Zeile erscheint, ohne Codeänderung.

```bash
import logging
import os

logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))
log = logging.getLogger("taproom")
log.debug("keg pressure read")
log.info("tap opened")
```

Ausgabe

```text
DEBUG:taproom:keg pressure read
INFO:taproom:tap opened
```

### force=True übergeben, um neu zu konfigurieren

Python ignoriert einen zweiten `basicConfig()`-Aufruf, wenn der Root-Logger schon einen Handler hat. Übergib `force=True`, um die Handler zu ersetzen, zum Beispiel in Tests.

```bash
import logging

logging.basicConfig(level=logging.WARNING)
logging.basicConfig(level=logging.DEBUG)
logging.debug("second basicConfig: not printed")
logging.basicConfig(level=logging.DEBUG, force=True)
logging.debug("basicConfig with force=True: printed")
```

Ausgabe

```text
DEBUG:root:basicConfig with force=True: printed
```

## Jede Exception mit Traceback loggen

### log.exception() im except-Block aufrufen

`log.exception()` loggt auf ERROR und hängt den Traceback an. Übergib `exc_info=True` an einen Aufruf auf einem anderen Level, um dort dasselbe zu tun.

```bash
import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s %(message)s")
log = logging.getLogger("taproom")
taps = {"ipa": 12}

def pour(beer):
    return taps[beer]

try:
    pour("stout")
except KeyError:
    log.exception("pour failed")
```

Ausgabe

```text
ERROR taproom pour failed
Traceback (most recent call last):
  File "app.py", line 11, in <module>
    pour("stout")
    ~~~~^^^^^^^^^
  File "app.py", line 8, in pour
    return taps[beer]
           ~~~~^^^^^^
KeyError: 'stout'
```

### Jede Zeile mit dem Logging-Handler an LogBrew senden

Füge `LogBrewLoggingHandler` zu deinem Logger hinzu. Jeder Record wird ein LogBrew-Log mit Level, Logger-Name, Datei und Zeile. Name und Meldung der Exception kommen mit, ebenso die Werte aus `extra={}`.

```bash
import logging
import os

from logbrew_sdk import HttpTransport, LogBrewClient, LogBrewLoggingHandler

client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"])
transport = HttpTransport()
log = logging.getLogger("taproom")
log.addHandler(LogBrewLoggingHandler(client, transport, metadata={"service": "taproom"}))
log.setLevel(logging.INFO)

taps = {"ipa": 12}

def pour(beer):
    return taps[beer]

try:
    pour("stout")
except KeyError:
    log.exception("pour failed", extra={"order_id": "ord_42"})

response = client.flush(transport)
print("HTTP", response.status_code, "accepted", response.accepted_events, "logs")
```

Ausgabe

```text
HTTP 200 accepted 1 logs
```

### Das Log mit seinen Exception-Feldern zurücklesen

`logbrew read logs --severity error --json` liefert die Zeile mit `exceptionName`, `exceptionMessage`, der Datei, der Funktion, der Zeilennummer und deiner `order_id`.

```bash
logbrew read logs --project <project_id> --severity error --since 1h --json | jq '.[0] | {level, message, logger: .attributes.logger, metadata: .attributes.metadata}'
```

Ausgabe

```text
{
  "level": "error",
  "message": "pour failed",
  "logger": "taproom",
  "metadata": {
    "exceptionMessage": "'stout'",
    "exceptionName": "KeyError",
    "fileName": "send.py",
    "functionName": "<module>",
    "levelName": "ERROR",
    "levelNumber": 40,
    "lineNumber": 20,
    "module": "send",
    "order_id": "ord_42",
    "processName": "MainProcess",
    "service": "taproom",
    "threadName": "MainThread"
  }
}
```

### Den Absturz abfangen, der den Prozess beendet

`install_process_error_capture()` hängt sich in `sys.excepthook` ein. Eine Exception, die niemand fängt, wird ein Issue auf Level `critical` mit `handled: false`, erfasst, bevor der Prozess mit Code 1 endet.

```bash
import atexit
import json
import os

from logbrew_sdk import LogBrewClient, RecordingTransport, install_process_error_capture

transport = RecordingTransport.always_accept()
client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"], transport=transport)
install_process_error_capture(client, on_capture_error=print)


@atexit.register
def show_captured_issue() -> None:
    issue = json.loads(transport.last_body())["events"][0]["attributes"]
    print(json.dumps({k: issue[k] for k in ("title", "level")}), json.dumps(issue["exception"]))


kegs = {"ipa": 3}
kegs["stout"] -= 1
```

Ausgabe

```text
Traceback (most recent call last):
  File "<stdin>", line 19, in <module>
KeyError: 'stout'
{"title": "KeyError", "level": "critical"} {"type": "KeyError", "mechanism": {"type": "python.excepthook", "handled": false}}
```

## Was wir beim Testen gesehen haben

Ohne Konfiguration haben 5 Aufrufe auf dem Root-Logger 3 Zeilen auf stderr ausgegeben und die DEBUG- und INFO-Aufrufe verworfen. Die Standardzeile besteht aus Level, Logger-Name und Meldung, als `WARNING:root:keg below 10%`.

Ein zweiter `basicConfig()`-Aufruf hat nichts geändert, bis wir `force=True` übergeben haben. Über den LogBrew-Handler kamen DEBUG- und INFO-Records als `info` an, WARNING als `warning`, ERROR als `error` und CRITICAL als `critical`.

`log.exception()` hat `exceptionName` und `exceptionMessage` zum LogBrew-Log hinzugefügt. Mit `include_exception_text=True` auf dem Handler kam der volle Traceback als `exceptionText` an.

```bash
import logging

logging.debug("keg pressure read")
logging.info("tap opened")
logging.warning("keg below 10%")
logging.error("keg empty")
logging.critical("cooler offline")
```

Ausgabe

```text
WARNING:root:keg below 10%
ERROR:root:keg empty
CRITICAL:root:cooler offline
```

## Die 5 Log-Level und wann du welchen nimmst

DEBUG ist für die Werte, die du beim Beheben eines Fehlers brauchst, etwa den Fassdruck, den SQL-Text oder die Zahl der Wiederholungen. Lass ihn in Produktion aus.

INFO hält fest, was das Programm getan hat, etwa einen geöffneten Zapfhahn oder eine bezahlte Bestellung. WARNING hält fest, was schiefging, aber behandelt wurde, etwa ein Fass unter 10 %. ERROR hält eine fehlgeschlagene Anfrage oder einen fehlgeschlagenen Job fest, etwa ein leeres Fass. CRITICAL hält fest, was das Programm stoppt, etwa einen ausgefallenen Kühler.

Logge jede Exception einmal, dort, wo du sie behandelst. Eine Zeile auf jeder Ebene des Call-Stacks sendet 3 Alarme für 1 Fehler.

```bash
import logging

for name in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
    print(name, logging.getLevelName(name))
print("root logger level:", logging.getLevelName(logging.getLogger().level))
```

Ausgabe

```text
DEBUG 10
INFO 20
WARNING 30
ERROR 40
CRITICAL 50
root logger level: WARNING
```

## Was LogBrew aufzeichnet

Jeder Record wird 1 Log mit Level, Logger-Name, Dateiname, Funktion, Zeilennummer und Modul. Primitive Werte aus `extra={}` werden Felder, nach denen du filtern kannst, und `metadata={}` auf dem Handler fügt jeder Zeile ein Feld hinzu, wie `service` oben.

Bei einer Exception bekommt das Log `exceptionName` und `exceptionMessage`. Übergib `include_exception_text=True` an den Handler, um auch den Traceback-Text zu speichern.

`install_process_error_capture()` macht aus einer ungefangenen Exception ein Issue auf Level `critical`, mit dem Exception-Typ, den Stack-Frames und dem Mechanismus `python.excepthook`.

## Häufige Fragen

### Was ist der Standard-Log-Level in Python?

WARNING. Ohne `basicConfig()`-Aufruf gibt der Root-Logger WARNING, ERROR und CRITICAL auf stderr aus, als `WARNING:root:message`, und verwirft DEBUG und INFO.

### Warum tut logging.basicConfig nichts?

Der Root-Logger hat schon einen Handler, aus einem früheren `basicConfig()`-Aufruf oder von einer Bibliothek, also ignoriert Python den Aufruf. Übergib `force=True`, um die Handler zu ersetzen.

### Was ist der Unterschied zwischen logging.error und logging.exception?

`log.exception()` ist `log.error()` mit `exc_info=True`. Es loggt auf ERROR und hängt den Traceback an. Rufe es nur in einem `except`-Block auf.

### Welche Python-Log-Level gibt es, in welcher Reihenfolge?

DEBUG 10, INFO 20, WARNING 30, ERROR 40, CRITICAL 50. Ein Logger lässt einen Record durch, wenn dessen Zahl auf oder über dem gesetzten Level liegt.

### Soll ich in Python print() oder logging verwenden?

Verwende `logging`. `print()` hat keinen Level, keinen Zeitstempel, keinen Logger-Namen und keinen Handler. Mit `logging` schaltest du Zeilen pro Modul ein und sendest sie in eine Datei oder an LogBrew.

## Sieh jede Python-Exception mit ihrem Traceback

Das LogBrew Python SDK sendet jede Log-Zeile mit Level, Logger, Datei und Zeile und macht aus einer ungefangenen Exception ein Issue mit Stacktrace. Starte mit dem großzügigen kostenlosen Plan, ohne Kreditkarte.

[Kostenlos starten](https://logbrew.co/de)

## Quellen

- [Python docs, Logging HOWTO](https://docs.python.org/3/howto/logging.html)
- [Python docs, logging module](https://docs.python.org/3/library/logging.html)
- [python/cpython#78078](https://github.com/python/cpython/issues/78078)

## Weitere Anleitungen für Agenten und Python

- [MCP-Server in Claude Code und Codex hinzufügen](https://logbrew.co/de/guides/mcp-server-claude-code-codex)
- [AGENTS.md in Claude Code und Codex nutzen](https://logbrew.co/de/guides/agents-md)
- [Claude-Fehler 529 overloaded und 429 Rate Limit beheben](https://logbrew.co/de/guides/claude-overloaded-rate-limit-error)
- [Strukturiertes Logging mit Log-Format und Log-Leveln, die du nach Feld durchsuchst](https://logbrew.co/de/guides/structured-logging)
- [Fehler beheben in Claude Code, Codex, JavaScript und Python](https://logbrew.co/de/guides)
