Python 日志管理完整指南

Python 日志管理完整指南 · Python Logging Full Guide

Python 日志管理完整指南(层级→配置→实践全链路)从 logging 基础到生产级日志管理,彻底理解日志级别与最佳实践

日志层级深度解析、logging 模块配置、Handler 与 Formatter、文件管理策略、结构化日志、性能优化、不同环境配置全覆盖

一、为什么需要日志管理

日志是程序运行时的”黑匣子”,记录系统状态、用户行为、异常信息和性能数据。没有日志,生产环境出问题时如同盲人摸象。

❌ 无日志(问题排查困难)

# 程序崩溃,只能看报错堆栈
# 不知道请求参数是什么
# 不知道数据流转到哪一步
# 不知道性能瓶颈在哪
# 复现 Bug 靠猜测

线上问题无法定位,只能加 print 重新部署排查,效率极低。

✅ 规范日志(问题一目了然)

2026-07-16 14:32:10 [INFO] 收到请求: /api/calculate, params={"a":10,"b":0}
2026-07-16 14:32:10 [DEBUG] 执行计算逻辑: divide(10, 0)
2026-07-16 14:32:10 [ERROR] 计算异常: ZeroDivisionError: division by zero
2026-07-16 14:32:10 [INFO] 返回响应: 400, {"error":"除数不能为零"}

请求参数、执行流程、异常信息、响应结果完整可追溯。

日志五大核心价值

故障排查 生产环境出问题,通过日志定位根因

行为审计 记录用户操作、数据变更、安全事件

性能分析 记录耗时,发现性能瓶颈

业务监控 统计接口调用量、错误率、吞吐量

调试辅助 开发阶段追踪程序执行流程

二、日志层级(Level)深度解析

日志层级是日志系统的核心机制,通过不同级别区分信息的重要程度,实现”不同场景看不同日志”的灵活控制。

2.1 Python 标准日志级别

级别数值含义使用场景生产环境
DEBUG10调试信息变量值、函数入参、执行流程关闭
INFO20常规信息服务启动、请求处理、状态变更开启
WARNING30警告信息资源不足、配置降级、非致命异常开启
ERROR40错误信息功能异常、业务失败、需人工介入开启
CRITICAL50严重错误系统崩溃、数据丢失、安全事件开启

2.2 如何正确选择日志级别

import logging

logger = logging.getLogger(__name__)

# DEBUG:开发调试,生产不输出
logger.debug(f"函数 divide 被调用,参数: a={a}, b={b}")
logger.debug(f"数据库查询 SQL: {sql}")

# INFO:关键业务节点,生产需要
logger.info(f"用户 {user_id} 登录成功,IP: {client_ip}")
logger.info(f"订单 {order_id} 创建成功,金额: {amount}")
logger.info("服务启动完成,监听端口 8000")

# WARNING:不影响主流程但需要关注
logger.warning(f"数据库连接池使用率超过 80%: {usage}%")
logger.warning(f"请求重试第 {retry_count} 次,URL: {url}")
logger.warning("使用默认配置,未找到 .env 文件")

# ERROR:功能异常,需要排查修复
logger.error(f"支付接口调用失败: {e}", exc_info=True)
logger.error(f"订单 {order_id} 状态更新失败,当前状态: {status}")

# CRITICAL:系统级灾难
logger.critical("数据库连接全部断开,服务不可用")
logger.critical("磁盘空间不足,写入操作已中止")

2.3 日志级别过滤原理

Logger 和 Handler 各自有级别设置,消息只有同时满足两者级别才会输出。

产生日志 Logger 级别过滤 Handler 级别过滤 Formatter 格式化 输出
# 示例:Logger 级别为 INFO,Handler 级别为 ERROR
# INFO 级别的日志会被 Logger 接收,但被 Handler 过滤
# ERROR 级别的日志会通过所有过滤

logger = logging.getLogger("myapp")
logger.setLevel(logging.INFO)           # 接收 INFO 及以上

handler = logging.StreamHandler()
handler.setLevel(logging.ERROR)         # 只输出 ERROR 及以上
logger.addHandler(handler)

logger.info("这条不会输出")    # 被 Handler 过滤
logger.error("这条会输出")     # 通过所有过滤

三、Python logging 模块架构

3.1 核心组件关系

Logger(记录器)
  ├── 层级命名:"myapp.api.auth" 继承 "myapp.api"
  ├── 级别过滤:setLevel()
  ├── 传播开关:propagate = True/False
  └── Handler(处理器)
        ├── StreamHandler    → 输出到控制台
        ├── FileHandler      → 输出到文件
        ├── RotatingFileHandler → 按大小轮转
        ├── TimedRotatingFileHandler → 按时间轮转
        └── 级别过滤 + Formatter(格式器)
              └── "%(asctime)s [%(levelname)s] %(message)s"

3.2 Logger 层级与继承

Logger 采用点号分隔的命名空间,形成树形结构,子 Logger 继承父 Logger 的配置。

import logging

# 根 Logger
root = logging.getLogger()           # ""

# 子 Logger
app = logging.getLogger("myapp")     # 继承 root
api = logging.getLogger("myapp.api") # 继承 myapp
auth = logging.getLogger("myapp.api.auth") # 继承 myapp.api

# 配置 root,所有子 Logger 默认继承
root.setLevel(logging.WARNING)
handler = logging.StreamHandler()
root.addHandler(handler)

# myapp.api.auth 会继承 root 的 Handler 和 Level
# 除非自己 addHandler 或 setLevel
注意:如果不配置根 Logger,直接使用 logging.info(),Python 会创建一个默认 Handler(输出到 stderr,级别 WARNING)。生产环境必须显式配置。

四、配置日志系统(代码/字典/文件)

4.1 代码方式配置(最基础)

import logging
import sys

def setup_logging(level: int = logging.INFO) -> None:
    """基础日志配置。"""
    formatter = logging.Formatter(
        fmt="%(asctime)s [%(levelname)-8s] %(name)s:%(lineno)d - %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S"
    )

    # 控制台 Handler
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(level)
    console.setFormatter(formatter)

    # 文件 Handler
    file_handler = logging.FileHandler("app.log", encoding="utf-8")
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)

    # 根 Logger
    root = logging.getLogger()
    root.setLevel(logging.DEBUG)
    root.addHandler(console)
    root.addHandler(file_handler)


# 使用
setup_logging()
logger = logging.getLogger(__name__)
logger.info("应用启动")

4.2 dictConfig 方式(推荐)

import logging.config

LOGGING_CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,

    "formatters": {
        "standard": {
            "format": "%(asctime)s [%(levelname)-8s] %(name)s:%(lineno)d - %(message)s",
            "datefmt": "%Y-%m-%d %H:%M:%S"
        },
        "detailed": {
            "format": "%(asctime)s [%(levelname)-8s] %(name)s %(filename)s:%(lineno)d %(funcName)s() - %(message)s"
        }
    },

    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "standard",
            "stream": "ext://sys.stdout"
        },
        "file": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "DEBUG",
            "formatter": "detailed",
            "filename": "logs/app.log",
            "maxBytes": 10485760,    # 10MB
            "backupCount": 5,
            "encoding": "utf-8"
        },
        "error_file": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "ERROR",
            "formatter": "detailed",
            "filename": "logs/error.log",
            "maxBytes": 10485760,
            "backupCount": 10,
            "encoding": "utf-8"
        }
    },

    "loggers": {
        "": {  # root logger
            "level": "DEBUG",
            "handlers": ["console", "file", "error_file"],
            "propagate": False
        },
        "uvicorn": {
            "level": "INFO",
            "handlers": ["console"],
            "propagate": False
        },
        "sqlalchemy.engine": {
            "level": "WARNING",
            "handlers": ["file"],
            "propagate": False
        }
    }
}

logging.config.dictConfig(LOGGING_CONFIG)

4.3 从文件加载配置

import logging.config
import json

# JSON 配置文件
with open("logging.json", "r", encoding="utf-8") as f:
    config = json.load(f)
logging.config.dictConfig(config)

# 或通过环境变量选择配置
import os
env = os.getenv("ENV", "dev")
config_file = f"config/logging.{env}.json"
with open(config_file, "r", encoding="utf-8") as f:
    logging.config.dictConfig(json.load(f))

五、日志文件管理策略

5.1 文件轮转策略对比

Handler轮转条件适用场景注意点
RotatingFileHandler文件大小达到 maxBytes流量稳定的应用多进程同时写入可能丢失
TimedRotatingFileHandler时间到达(天/小时/分钟)定时归档需求午夜切换时注意日志归属
WatchedFileHandler外部工具轮转信号配合 logrotate 使用Linux 系统级日志管理
QueueHandler异步队列高并发性能场景需要配合 QueueListener

5.2 按大小轮转配置

from logging.handlers import RotatingFileHandler

# 10MB 一个文件,保留 5 个备份
handler = RotatingFileHandler(
    "app.log",
    maxBytes=10 * 1024 * 1024,  # 10MB
    backupCount=5,
    encoding="utf-8"
)
# 生成: app.log, app.log.1, app.log.2, ..., app.log.5

5.3 按时间轮转配置

from logging.handlers import TimedRotatingFileHandler

# 每天午夜轮转,保留 30 天
handler = TimedRotatingFileHandler(
    "app.log",
    when="midnight",      # 可选: S(秒), M(分), H(小时), D(天), W0-W6(周几)
    interval=1,
    backupCount=30,
    encoding="utf-8"
)
# 生成: app.log, app.log.2026-07-15, app.log.2026-07-14, ...

5.4 配合 Linux logrotate

# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 0644 www-data www-data
    sharedscripts
    postrotate
        # 通知应用重新打开日志文件
        kill -USR1 $(cat /var/run/myapp.pid)
    endscript
}
多进程场景:标准 RotatingFileHandler 不支持多进程安全。高并发或部署多个 Worker 时,应使用 concurrent-log-handler 库或采用 SocketHandler + 独立日志进程。

六、不同环境的日志配置

6.1 环境配置对照

开发环境(dev)

level: DEBUG
handlers: [console]
format: 简洁,带行号
propagate: True

输出到控制台,DEBUG 级别全开,方便调试追踪。

测试环境(test)

level: DEBUG
handlers: [console, file]
format: 详细,带函数名
propagate: False

同时输出到控制台和文件,方便 CI 收集日志。

预发布环境(staging)

level: INFO
handlers: [file, error_file]
format: JSON 结构化
propagate: False

接近生产配置,使用 JSON 格式对接日志收集系统。

生产环境(prod)

level: INFO
handlers: [file]
format: JSON 结构化
propagate: False

INFO 及以上写入文件,ERROR 单独归档,结构化输出。

6.2 环境切换实现

import logging.config
import os

def get_logging_config(env: str) -> dict:
    """根据环境返回日志配置。"""
    base = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "simple": {"format": "%(levelname)s: %(message)s"},
            "standard": {"format": "%(asctime)s [%(levelname)s] %(name)s - %(message)s"},
            "json": {"class": "pythonjsonlogger.jsonlogger.JsonFormatter",
                     "format": "%(asctime)s %(levelname)s %(name)s %(message)s"}
        },
        "handlers": {},
        "loggers": {}
    }

    if env == "dev":
        base["handlers"]["console"] = {
            "class": "logging.StreamHandler",
            "level": "DEBUG",
            "formatter": "standard"
        }
        base["loggers"][""] = {"level": "DEBUG", "handlers": ["console"]}

    elif env == "prod":
        base["handlers"]["file"] = {
            "class": "logging.handlers.TimedRotatingFileHandler",
            "level": "INFO",
            "formatter": "json",
            "filename": "/var/log/myapp/app.log",
            "when": "midnight",
            "backupCount": 30
        }
        base["handlers"]["error"] = {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "ERROR",
            "formatter": "json",
            "filename": "/var/log/myapp/error.log",
            "maxBytes": 10485760,
            "backupCount": 10
        }
        base["loggers"][""] = {"level": "INFO", "handlers": ["file", "error"]}

    return base


env = os.getenv("ENV", "dev")
logging.config.dictConfig(get_logging_config(env))

七、结构化日志与 JSON 输出

结构化日志将日志输出为机器可解析的格式(如 JSON),便于日志收集系统(ELK、Loki、CloudWatch)索引、搜索和聚合分析。

7.1 普通文本 vs 结构化 JSON

❌ 普通文本日志

2026-07-16 14:32:10 [INFO] myapp:231 - 
用户 12345 登录成功,IP: 192.168.1.1,耗时: 45ms

解析困难,需要正则提取字段,搜索效率低。

✅ 结构化 JSON 日志

{"timestamp":"2026-07-16T14:32:10Z",
 "level":"INFO",
 "logger":"myapp.auth",
 "event":"user_login",
 "user_id":"12345",
 "client_ip":"192.168.1.1",
 "duration_ms":45}

字段明确,可直接被 Elasticsearch 等系统索引。

7.2 python-json-logger 配置

# pip install python-json-logger

from pythonjsonlogger import jsonlogger
import logging

logHandler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter(
    "%(asctime)s %(levelname)s %(name)s %(message)s",
    rename_fields={"levelname": "level", "asctime": "timestamp"}
)
logHandler.setFormatter(formatter)

logger = logging.getLogger()
logger.addHandler(logHandler)
logger.setLevel(logging.INFO)

# 输出结构化日志
logger.info("用户登录", extra={"user_id": "12345", "ip": "192.168.1.1"})
# {"timestamp": "2026-07-16 14:32:10", "level": "INFO", "name": "root",
#  "message": "用户登录", "user_id": "12345", "ip": "192.168.1.1"}

7.3 统一日志上下文(Context)

import logging
import contextvars
import uuid

# 上下文变量
request_id = contextvars.ContextVar("request_id", default="")

class ContextFilter(logging.Filter):
    """自动注入请求 ID 到日志记录。"""
    def filter(self, record):
        record.request_id = request_id.get()
        return True

# 配置
logger = logging.getLogger("myapp")
logger.addFilter(ContextFilter())

formatter = logging.Formatter(
    "%(asctime)s [%(levelname)s] [%(request_id)s] %(message)s"
)

# 使用
request_id.set(str(uuid.uuid4())[:8])
logger.info("处理请求开始")
logger.info("查询数据库")
logger.info("返回响应")
# 所有日志自动携带同一个 request_id,便于链路追踪

八、性能优化与异步日志

8.1 日志性能要点

性能优化原则

1. 延迟格式化 使用 logger.debug("value: %s", value) 而非 f"value: {value}",避免不必要的字符串拼接

2. 级别检查 DEBUG 日志密集场景使用 if logger.isEnabledFor(logging.DEBUG) 提前判断

3. 异步写入 高并发场景使用 QueueHandler 避免阻塞主线程

4. 批量刷新 文件 Handler 设置合理的 buffer size,减少 IO 次数

8.2 QueueHandler 异步配置

import logging
import logging.handlers
import queue

# 创建日志队列
log_queue = queue.Queue(-1)  # 无界队列

# 队列 Handler(生产者)
queue_handler = logging.handlers.QueueHandler(log_queue)

# 实际写入 Handler(消费者)
file_handler = logging.FileHandler("app.log", encoding="utf-8")
file_handler.setFormatter(logging.Formatter(
    "%(asctime)s [%(levelname)s] %(message)s"
))

# 队列监听器,在独立线程中消费日志
listener = logging.handlers.QueueListener(
    log_queue, file_handler, respect_handler_level=True
)

# 配置根 Logger
root = logging.getLogger()
root.addHandler(queue_handler)
root.setLevel(logging.DEBUG)

# 启动监听器
listener.start()

# ... 应用运行 ...

# 关闭时停止监听器
listener.stop()

九、日志安全与敏感信息处理

9.1 敏感信息红线

绝对禁止写入日志的内容:密码、Token、API Key、银行卡号、身份证号、手机号、Session Cookie、个人隐私数据。日志泄露是常见的安全事故来源。

9.2 敏感信息脱敏

import logging
import re

class SensitiveDataFilter(logging.Filter):
    """日志敏感信息脱敏过滤器。"""

    PATTERNS = {
        "password": re.compile(r'("password"\s*:\s*")[^"]*("', re.I),
        "token": re.compile(r'(Bearer\s+)[\w\-\.]+', re.I),
        "phone": re.compile(r'(1\d{2})\d{4}(\d{4})'),
        "id_card": re.compile(r'(\d{6})\d{8}(\d{4})'),
    }

    def filter(self, record):
        msg = record.getMessage()
        for name, pattern in self.PATTERNS.items():
            if name in ("password", "token"):
                msg = pattern.sub(r'\1***\2', msg)
            else:
                msg = pattern.sub(r'\1****\2', msg)
        record.msg = msg
        record.args = ()
        return True

# 应用过滤器
logger = logging.getLogger("myapp")
logger.addFilter(SensitiveDataFilter())

# 测试
logger.info('登录请求: {"username":"alice","password":"Secret123"}')
# 输出: 登录请求: {"username":"alice","password":"***"}

9.3 安全的日志记录实践

# ❌ 错误:记录敏感信息
logger.info(f"用户登录: {username}, 密码: {password}")
logger.debug(f"API 请求头: {headers}")  # 可能包含 Token

# ✅ 正确:脱敏后记录
logger.info(f"用户登录: {username}")
logger.debug(f"API 请求头 keys: {list(headers.keys())}")

# ✅ 正确:只记录必要的标识
logger.info(f"支付请求: order_id={order_id}, amount={amount}")
# 不要记录: card_number, cvv, 持卡人姓名

十、常见问题与排查模板

问题现象原因解决方案
日志不输出 Logger/Handler 级别设置过高,或 propagate=False 且未配置 Handler 检查 logger.level、handler.level、确认有 Handler 附加
日志重复输出 子 Logger 和根 Logger 都有 Handler,propagate=True 子 Logger 设置 propagate=False,或移除重复的 Handler
中文日志乱码 FileHandler 未设置 encoding=”utf-8″ 所有 FileHandler 显式设置 encoding=”utf-8″
日志文件不轮转 多进程下 RotatingFileHandler 竞争写入 使用 concurrent-log-handler,或改用 TimedRotatingFileHandler
日志丢失 程序崩溃前日志未 flush 到磁盘 关键位置调用 logging.shutdown(),或设置 stream.flush()
第三方库日志太多 urllib3、sqlalchemy 等库默认级别低 单独配置第三方 Logger: logging.getLogger(“urllib3”).setLevel(logging.WARNING)
日志阻塞主线程 同步文件 IO,高并发下性能瓶颈 使用 QueueHandler + QueueListener 实现异步日志

排查检查清单

# 1. 检查 Logger 层级和级别
for name in ["", "myapp", "myapp.api"]:
    logger = logging.getLogger(name)
    print(f"{name}: level={logger.level}, handlers={logger.handlers}, propagate={logger.propagate}")

# 2. 检查所有 Handler
for handler in logging.getLogger().handlers:
    print(f"Handler: {handler}, level={handler.level}, formatter={handler.formatter}")

# 3. 强制输出测试
logging.getLogger().debug("debug test")
logging.getLogger().info("info test")
logging.getLogger().warning("warning test")
全流程总结:理解日志层级(DEBUG→CRITICAL)→ 根据环境选择合适的级别 → 使用 dictConfig 统一管理配置 → 文件轮转避免磁盘打满 → 生产环境使用 JSON 结构化日志 → 敏感信息脱敏处理 → 高并发场景使用异步 QueueHandler → 配合日志收集系统实现集中监控。

Python Logging Complete GuideLevels → Configuration → Production Best Practices

Logging levels deep dive, logging module architecture, Handler & Formatter, file management, structured logs, performance, environment-specific configs

1 Why Logging Matters

Five Core Values of Logging

Troubleshooting Root cause analysis in production

Audit Trail Record user actions and data changes

Performance Identify bottlenecks via timing logs

Monitoring Track API calls, error rates, throughput

Debugging Trace execution flow during development

2 Logging Levels Deep Dive

LevelValueMeaningUse CaseProduction
DEBUG10Debug infoVariables, function paramsOff
INFO20General infoService start, requestsOn
WARNING30WarningResource low, retriesOn
ERROR40ErrorFailures, exceptionsOn
CRITICAL50CriticalSystem crash, data lossOn

Level Filtering Flow

Log emitted Logger level filter Handler level filter Formatter Output

3 Python logging Architecture

Logger (hierarchical namespace)
  ├── "myapp.api.auth" inherits "myapp.api"
  ├── setLevel() — minimum accepted level
  ├── propagate — pass to parent logger
  └── Handlers
        ├── StreamHandler → console
        ├── FileHandler → file
        ├── RotatingFileHandler → size-based rotation
        └── TimedRotatingFileHandler → time-based rotation
              └── Formatter: "%(asctime)s [%(levelname)s] %(message)s"

4 Configuring Logging

dictConfig (Recommended)

LOGGING_CONFIG = {
    "version": 1,
    "formatters": {
        "standard": {
            "format": "%(asctime)s [%(levelname)-8s] %(name)s - %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "standard"
        },
        "file": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "DEBUG",
            "formatter": "standard",
            "filename": "app.log",
            "maxBytes": 10485760,
            "backupCount": 5
        }
    },
    "loggers": {
        "": {"level": "DEBUG", "handlers": ["console", "file"]}
    }
}

logging.config.dictConfig(LOGGING_CONFIG)

5 Log File Management

Rotation Strategies

HandlerTriggerUse Case
RotatingFileHandlerFile sizeStable traffic apps
TimedRotatingFileHandlerTime (day/hour)Scheduled archiving
WatchedFileHandlerExternal signalLinux logrotate
QueueHandlerAsync queueHigh concurrency
# Size-based rotation: 10MB per file, keep 5 backups
RotatingFileHandler("app.log", maxBytes=10*1024*1024, backupCount=5)

# Time-based rotation: daily at midnight, keep 30 days
TimedRotatingFileHandler("app.log", when="midnight", backupCount=30)

6 Environment-Specific Configs

Development

level: DEBUG
handlers: [console]
format: simple with line numbers

Production

level: INFO
handlers: [file]
format: JSON structured
rotation: daily

7 Structured & JSON Logging

# pip install python-json-logger
from pythonjsonlogger import jsonlogger

formatter = jsonlogger.JsonFormatter(
    "%(asctime)s %(levelname)s %(name)s %(message)s",
    rename_fields={"levelname": "level"}
)

# Output:
# {"timestamp": "2026-07-16T14:32:10", "level": "INFO",
#  "name": "myapp", "message": "user login", "user_id": "12345"}

8 Performance & Async Logging

Performance Tips

1. Lazy formatting Use logger.debug("val: %s", val) not f"val: {val}"

2. Level guard Use logger.isEnabledFor(logging.DEBUG) for heavy debug logs

3. Async write Use QueueHandler to avoid blocking main thread

9 Security & Sensitive Data

Never log: passwords, tokens, API keys, credit cards, ID numbers, phone numbers, session cookies, personal data. Log leakage is a common security incident.
# ❌ Wrong
logger.info(f"Login: {username}, password: {password}")

# ✅ Correct
logger.info(f"Login: {username}")
logger.debug(f"Header keys: {list(headers.keys())}")

10 FAQ & Troubleshooting

IssueSolution
No outputCheck logger.level, handler.level, ensure handler attached
Duplicate outputSet propagate=False on child loggers
Chinese garbledSet encoding=”utf-8″ on all FileHandlers
Rotation not workingUse concurrent-log-handler for multi-process
Logs lost on crashCall logging.shutdown() or flush handlers
Too many 3rd-party logsSet urllib3/sqlalchemy loggers to WARNING
Summary: Understand logging levels (DEBUG→CRITICAL) → Choose levels per environment → Use dictConfig for unified config → Rotate files to prevent disk fill → Use JSON structured logs in production → Sanitize sensitive data → Use QueueHandler for high concurrency → Integrate with centralized log collection.