Python logging模块严重级别与Heroku控制台严重级别不匹配如何解决
问题根源
Heroku内置的日志收集组件Logplex对日志级别的判定逻辑是硬编码的:
- 所有输出到
stdout(标准输出流)的内容,统一标记为INFO级别 - 所有输出到
stderr(标准错误流)的内容,统一标记为ERROR级别
你当前的配置把所有级别的日志都输出到了stdout,所以不管你代码里打WARNING还是ERROR,Heroku都会识别为INFO级别,导致无法过滤。
解决方案
1. 修改logging配置,按日志级别分流输出
调整Handler规则,INFO及以下级别日志输出到stdout,WARNING及以上级别输出到stderr,参考配置如下:
import logging import sys # 自定义过滤器,只允许低于指定级别的日志通过 class LevelFilter(logging.Filter): def __init__(self, max_level): self.max_level = max_level super().__init__() def filter(self, record): return record.levelno <= self.max_level logging.basicConfig( format="[%(asctime)s] %(filename)s:%(lineno)-5d %(levelname)-8s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO, handlers=[ # stdout 只处理 INFO 及以下级别 logging.StreamHandler(sys.stdout), # stderr 处理 WARNING 及以上级别 logging.StreamHandler(sys.stderr) ] ) # 给两个Handler分别设置级别和过滤规则 handlers = logging.getLogger().handlers # 第一个handler是stdout,只保留<=INFO的日志 handlers[0].addFilter(LevelFilter(logging.INFO)) # 第二个handler是stderr,只保留>=WARNING的日志 handlers[1].setLevel(logging.WARNING) logger = logging.getLogger("plant-pot-backend") # 禁止日志向上传播到根日志器,避免和Uvicorn默认日志重复输出 logger.propagate = False
2. 替换业务代码中的print为对应级别的日志方法
你当前异常捕获里用的print(e)默认输出到stdout,会被标记为INFO,修改为logger.error(e)即可:
@router.post('/add') async def create(new_pot: PotHttpReq): try: pot_id = new_pot.id new_pot = new_pot_registration(pot_id) pots_collection.document(pot_id).set(new_pot.dict()) logger.warning("New pot added: {}".format(pot_id)) return {"success": True} except Exception as e: # exc_info可选,开启会打印完整错误栈 logger.error(f"An Error Occured: {e}", exc_info=True) return f"An Error Occured: {e}"
3. 可选:关闭Uvicorn默认日志重复输出
如果部署后发现日志重复,启动Uvicorn时添加参数关闭默认的访问日志即可:
uvicorn main:app --host 0.0.0.0 --port $PORT --no-access-log
配置完成后Heroku就会正确识别不同级别的日志,Papertrail等日志工具也可以正常按级别过滤。
内容的提问来源于stack exchange,提问作者hwleee
相关产品推荐
相关产品推荐

