Loguru 中如何实现按日志级别或条件将消息写入指定文件(避免重复写入)?

loguru 的 logger 是全局单例,多次 `add()` 会向同一 logger 添加多个处理器(sinks),所有满足级别阈值的日志都会广播到所有匹配的 sink——因此 error 日志同时写入 info 和 error 文件。解决方法是使用 `filter` 参数精确控制每条日志的路由。

在 Loguru 中,logger 对象本质上是全局的(from loguru import logger 导入的是同一个实例),因此 loggerEven = logger 和 loggerUneven = logger 并未创建独立 logger,只是两个指向同一对象的引用。调用 logger.add(..., level="INFO") 和 logger.add(..., level="ERROR") 实际上是为同一个 logger添加了两个文件 sink。Loguru 的日志分发机制是:每条日志消息会依次经过所有已注册 sink,只要该 sink 的 level 阈值 ≤ 当前日志级别,就会被写入

因此,当执行 logger.error("Uneven") 时:

  • "UnEven.txt" sink 的 level="ERROR" ✅ 匹配 → 写入;
  • "Even.txt" sink 的 level="INFO"(INFO

⚠️ 关键误区:level 控制的是“最低可接收级别”,而非“专属级别”。它不能实现“仅 ERROR 写入 error 文件、仅 INFO 写入 info 文件”的排他路由。

✅ 正确解法:使用 filter 参数(支持函数或字符串)对每个 sink 做精细化过滤。推荐使用函数形式,返回 True 表示允许该日志进入此 sink:

from loguru import logger
import random

# 清空默认 handler(可选,避免控制台重复输出)
logger.remove()

# 只接收 INFO 级别且 message 不含 "ERROR" 的日志(示例逻辑)
logger.add("Even.txt", mode='w', level="INFO",
           filter=lambda record: record["level"].no == 20)  # INFO 的数值为 20

# 只接收 ERROR 级别日志
logger.add("UnEven.txt", mode='w', level="ERROR",
           filter=lambda record: record["level"].no == 40)  # ERROR 的数值为 40

x = random.randint(0, 11)
print(x)
if x % 2 == 1:
    logger.error("Uneven")
else:
    logger.info("Even")

更灵活的方式是基于日志内容、

模块名或自定义字段过滤:

# 按日志消息关键字过滤(不推荐用于生产,仅作演示)
logger.add("errors_only.log", level="ERROR",
           filter=lambda r: "critical" in r["message"].lower() or r["level"].name == "ERROR")

# 或使用字典式 filter(推荐用于多条件)
def error_only_filter(record):
    return record["level"].name in ["ERROR", "CRITICAL"]

logger.add("app_errors.log", level="ERROR", filter=error_only_filter)

? 注意事项:

  • filter 函数接收 record 字典(含 level, message, module, function, time 等键),务必确保逻辑无副作用且返回布尔值;
  • 若需完全隔离日志流(如不同模块/组件使用独立日志器),应使用 logger.bind() + filter 组合,或按官方 Recipe 创建命名子 logger(通过 logger.opt() 或封装类);
  • 避免在 filter 中执行耗时操作(如 I/O、网络请求),否则会阻塞日志主线程;
  • 调试时可用 logger.debug("Record: {}", record)(需先启用 DEBUG 级别)查看实际 record 结构。

总结:Loguru 的核心设计是“一个 logger,多个 sink,广播式分发”,level 仅做最小门槛控制;真正实现精准路由必须依赖 filter。合理组合 level(粗筛)与 filter(精筛),即可优雅达成“ERROR 只进 error 文件、INFO 只进 info 文件”的目标。