---
title: "配置 Python 日志用对日志级别并捕获异常和回溯"
description: "Python logging 在你配置之前只打印 WARNING 及以上级别。这里有 3 行配置、5 个日志级别，以及如何把异常连同回溯一起记录，全部来自 Python 3.13 上的实测。"
canonical: "https://logbrew.co/zh-Hans/guides/python-logging"
last_updated: "2026-11-10"
---

# 配置 Python 日志用对日志级别并捕获异常和回溯

Python `logging` 在你配置之前只打印 WARNING 及以上级别。这里有 3 行配置、5 个日志级别，以及如何把异常连同回溯一起记录，全部来自 Python 3.13 上的实测。

2026年11月10日 用 Python 3.13.5和logbrew-sdk 0.1.29 实测

作者 [Furkan Erday](https://github.com/furkanerday). Furkan Erday 是 LogBrew 的创始人，每天用 Claude Code 和 Codex 开发 LogBrew。

Canonical: https://logbrew.co/zh-Hans/guides/python-logging

## 不配置时和在 LogBrew 里，每个 Python 日志级别各发生什么

| 日志调用 | 不配置时打印 | 在 LogBrew 里的级别 |
| --- | --- | --- |
| `log.debug()` | 被丢弃 | 并入 `info` |
| `log.info()` | 被丢弃 | `info` |
| `log.warning()` | `WARNING:root:` | `warning` |
| `log.error()` | `ERROR:root:` | `error` |
| `log.critical()` | `CRITICAL:root:` | `critical` |

不做任何配置，在根记录器上调用 5 次，再把同样 5 次经过 LogBrew 处理器发送。满杯表示被打印出来，或者按自己的级别保留。

## 3 行配置日志

### 在第 1 次日志调用之前调用 1 次 basicConfig()

`basicConfig()` 设置根记录器的级别和每行的格式。不配置时，Python 只打印 WARNING 及以上级别，形如 `WARNING:root:keg below 10%`，并丢弃 DEBUG 和 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")
```

输出

```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
```

### 按模块给记录器命名

`logging.getLogger(__name__)` 给每个模块一个自己的记录器，这样每一行都写明来自哪个模块。根记录器只用于 `basicConfig()`。

### 从环境变量读取级别

`basicConfig()` 接受文本形式的级别名。用 `LOG_LEVEL=DEBUG` 运行这段代码，debug 那一行就会出现，不用改代码。

```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")
```

输出

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

### 传入 force=True 以重新配置

根记录器已经有处理器时，Python 会忽略第 2 次 `basicConfig()` 调用。传入 `force=True` 可以替换处理器，比如在测试里。

```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")
```

输出

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

## 把每个异常连同回溯一起记录

### 在 except 里调用 log.exception()

`log.exception()` 以 ERROR 级别记录并附上回溯。在其他任何级别的调用上传入 `exc_info=True`，效果相同。

```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")
```

输出

```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'
```

### 用日志处理器把每一行发到 LogBrew

把 `LogBrewLoggingHandler` 加到你的记录器上。每条记录都变成一条 LogBrew 日志，带着级别、记录器名称、文件和行号。异常的名称和消息会一起过来，`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")
```

输出

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

### 连同异常字段把日志读回来

`logbrew read logs --severity error --json` 返回这一行，包含 `exceptionName`、`exceptionMessage`、文件、函数、行号和你的 `order_id`。

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

输出

```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"
  }
}
```

### 捕获让进程退出的崩溃

`install_process_error_capture()` 接管 `sys.excepthook`。没人捕获的异常会变成一个 `critical` 级别、带 `handled: false` 的问题，在进程以代码 1 退出之前被记录下来。

```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
```

输出

```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}}
```

## 我们实测看到的结果

不配置时，根记录器上的 5 次调用向 stderr 打印了 3 行，丢弃了 DEBUG 和 INFO 调用。默认的一行由级别、记录器名称和消息组成，形如 `WARNING:root:keg below 10%`。

第 2 次 `basicConfig()` 调用什么都没改变，直到我们传入 `force=True`。经过 LogBrew 处理器后，DEBUG 和 INFO 记录以 `info` 到达，WARNING 是 `warning`，ERROR 是 `error`，CRITICAL 是 `critical`。

`log.exception()` 给 LogBrew 日志加上了 `exceptionName` 和 `exceptionMessage`。处理器上设置 `include_exception_text=True` 后，完整回溯以 `exceptionText` 到达。

```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")
```

输出

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

## 5 个日志级别以及各自何时使用

DEBUG 用于修 bug 时需要的值，比如酒桶压力、SQL 文本或重试次数。生产环境里关掉它。

INFO 记录程序做了什么，比如打开一个酒头或一笔订单已付款。WARNING 记录出了问题但已处理的事，比如酒桶低于 10%。ERROR 记录失败的请求或任务，比如酒桶空了。CRITICAL 记录让程序停下的事，比如冷柜离线。

每个异常只在你处理它的地方记录 1 次。调用堆栈的每一层都记一行，1 次失败会发出 3 条告警。

```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))
```

输出

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

## LogBrew 记录了什么

每条记录都变成 1 条日志，带着级别、记录器名称、文件名、函数、行号和模块。`extra={}` 里的基本类型值会变成可筛选的字段，处理器上的 `metadata={}` 给每一行加一个字段，比如上面的 `service`。

出现异常时，日志会带上 `exceptionName` 和 `exceptionMessage`。给处理器传入 `include_exception_text=True`，还会保存回溯文本。

`install_process_error_capture()` 把未捕获的异常变成 `critical` 级别的问题，带着异常类型、堆栈帧和机制 `python.excepthook`。

## 常见问题

### Python 默认的日志级别是什么？

WARNING。不调用 `basicConfig()` 时，根记录器把 WARNING、ERROR 和 CRITICAL 以 `WARNING:root:message` 的形式打印到 stderr，并丢弃 DEBUG 和 INFO。

### 为什么 logging.basicConfig 不起作用？

根记录器已经有处理器了，来自更早的 `basicConfig()` 调用或某个库，所以 Python 忽略这次调用。传入 `force=True` 可以替换处理器。

### logging.error 和 logging.exception 有什么区别？

`log.exception()` 就是带 `exc_info=True` 的 `log.error()`。它以 ERROR 级别记录并附上回溯。只在 `except` 块里调用它。

### Python 日志级别按顺序是什么？

DEBUG 10、INFO 20、WARNING 30、ERROR 40、CRITICAL 50。记录的数字达到或高于你设置的级别时，记录器才会放行。

### Python 里该用 print() 还是 logging？

用 `logging`。`print()` 没有级别、没有时间戳、没有记录器名称，也没有处理器。用 `logging` 你可以按模块开启日志行，并把它们发到文件或 LogBrew。

## 连同回溯查看每个 Python 异常

LogBrew Python SDK 发送每一行日志时都带上级别、记录器、文件和行号，并把未捕获的异常变成带堆栈的问题。免费额度充足，无需绑卡。

[免费开始](https://logbrew.co/zh-Hans)

## 参考来源

- [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)

## 更多智能体与 Python 指南

- [在 Claude Code 和 Codex 中添加 MCP 服务器](https://logbrew.co/zh-Hans/guides/mcp-server-claude-code-codex)
- [在 Claude Code 和 Codex 中使用 AGENTS.md](https://logbrew.co/zh-Hans/guides/agents-md)
- [解决 Claude overloaded 529 错误和 rate limit 429 错误](https://logbrew.co/zh-Hans/guides/claude-overloaded-rate-limit-error)
- [结构化日志用能按字段搜索的日志格式和日志级别](https://logbrew.co/zh-Hans/guides/structured-logging)
- [修复 Claude Code、Codex、JavaScript 和 Python 中的错误](https://logbrew.co/zh-Hans/guides)
