首页
/ Folly 日志库日志级别(LogLevels)完全指南:从 FATAL 到 DBG9 的级别体系、数值模型与配置实践

Folly 日志库日志级别(LogLevels)完全指南:从 FATAL 到 DBG9 的级别体系、数值模型与配置实践

2026-09-09 22:49:37作者:宣聪麟

导读

本文围绕 folly/logging/docs/LogLevels.md 展开,系统讲解 Folly(Facebook 开源的 C++ 基础库)日志库中完整的日志级别体系:FATALDFATALCRITICALERRWARNINFO 以及 DBG0DBG9 十个编号调试级别各自的分工与语义。通过阅读本文,你将掌握日志级别背后的数值模型(为何 DBG0DBG9 更重要、DEBUG 为何能一次打开所有调试级别)、级别名称与配置字符串之间的解析规则,以及如何在 XLOG / FB_LOG 宏和日志配置字符串中正确选用级别来精确控制程序的日志输出粒度。

一、日志级别总体设计:越重要数值越大

Folly 日志库的全部日志级别定义在 folly/logging/LogLevel.h 中,其核心是一个强类型枚举 enum class LogLevel : uint32_t。设计原则非常清晰:数值越大,级别越重要(越少出现)。下面是该头文件中定义的关键级别及其实际数值:

级别 数值(uint32_t) 说明
UNINITIALIZED 0 未初始化占位值
NONE / MIN_LEVEL 1 不启用任何日志
DBG(即 DEBUG 1000 最低(最啰嗦)的调试级别,主要用于配置
DBG0DBG9 1999 ~ 1990 十个细分调试级别,编号越小越重要
INFO 2000 普通信息
INFO0INFO9 2999 ~ 2990 细分信息级别(源码中同样存在)
WARNWARNING 3000 警告
ERR 4000 错误
CRITICAL 5000 严重错误
DFATAL 0x7ffffffe 仅 debug 构建下终止程序
FATAL / MAX_LEVEL 0x7fffffff 任何构建下都终止程序

需要注意两处源码细节:

  • 头文件注释明确指出,DBG*INFO* 名称中的数字是反转的DBG0 是最不啰嗦的调试级别,DBG1 更啰嗦一级,以此类推,DBG9 最啰嗦。数值上则体现为 DBG0 = 1999 > DBG9 = 1990,与"数值越大越重要"的总原则保持一致(参见 LogLevel.h)。
  • FATAL 就是 MAX_LEVEL(0x7fffffff),因为 LogCategory 需要用最高位存储标志位,所以最大值必须清除该位(参见 LogLevel.h)。

此外头文件还提供了两个实用常量:默认级别 kDefaultLogLevel = LogLevel::INFO,以及最小致命级别 kMinFatalLogLevel——在 debug 构建(定义了 folly::kIsDebug)下为 DFATAL,否则为 FATAL;函数 isLogLevelFatal() 同样按构建模式判断一个级别是否致命(参见 LogLevel.h)。

二、逐级解读:从 FATAL 到 DEBUG

FATAL:必定终止程序

使用 FATAL 级别记录的日志消息会直接中止(abort)你的程序,并且无法被禁用。即使某个日志分类明确设置了自己的级别,FATAL 消息也永远会被处理。特别地,如果程序在记录 FATAL 消息时还没有配置任何日志处理器(handler),该消息会被打印到 stderr,确保程序不会"静默"退出。

DFATAL:仅 debug 构建下致命

DFATALFATAL 行为类似,但只在 debug 构建下才终止程序——即构建时没有定义 NDEBUG 预处理器宏(folly 中通过 folly::kIsDebug 判定)。在 release 构建下,DFATAL 降级为一个普通的(但级别很高的)日志消息,不会导致程序退出。这非常适合在开发阶段强制暴露不可达状态、在发布版本中又不至于因为一条日志直接崩溃的场景。

CRITICAL:介于 ERRFATAL 之间的重要错误

CRITICAL 面向重要的错误消息,其重要程度位于 ERRFATAL 之间(数值 5000)。当一条错误足够严重、值得被高度关注,但还不足以让程序中止时,应当使用 CRITICAL

ERR:普通错误

ERR 面向一般错误消息。文档特别解释了命名原因:这一类别叫做 ERR 而不是 ERROR,是因为 Windows 的公共头文件会把 ERROR 定义为预处理器宏,直接用 ERROR 作为枚举名会与宏冲突(这一点在 LogLevel.h 的注释中同样有说明)。不过在配置字符串中,ERROR 仍被接受为 ERR 的别名(见下文解析规则)。

WARN(又名 WARNING):警告

WARN 面向警告消息,且 WARNING 被接受为 WARN 的别名。在源码枚举中两者数值完全相同:WARN = 3000, WARNING = 3000(参见 LogLevel.h),因此它们在一切场景下都等价。

INFO:信息

INFO 面向一般信息性消息,同时它也是日志库的默认日志级别kDefaultLogLevel)。这意味着如果某个分类没有显式配置级别,默认只输出 INFO 及以上(更重要的)消息。

DBG0DBG9:十个细分调试级别

日志库提供 10 个编号调试级别 DBG0DBG1、…、DBG9,用于按"啰嗦程度"精细切分调试输出。DBG0DBG9 更重要——编号可以理解为该级别的 verbosity(冗余度):编号越大越啰嗦、越不重要。

这一语义直接决定了"设置级别"的效果。文档给出的规则是:把某个日志分类的级别设为 DBG5,会启用 DBG0DBG5 的消息(以及 INFO 及以上的更高级别),而 DBG6DBG9 的消息将被禁用。用数值模型可以精确验证:设置有效级别为 DBG5(1994)后,只有级别数值 ≥ 1994 的消息才会输出,DBG0(1999)~DBG5(1994)全部满足,DBG6(1993)~DBG9(1990)全部被过滤。

DEBUG:低于 DBG9 的总开关

DEBUG 类别位于 DBG9 之下(数值上就是 DBG = 1000)。把日志分类的级别设置为 DEBUG,会自动启用所有编号的 DBG 级别:因为有效级别被压到 1000,所有 ≥ 1000 的级别(DBG0DBG9INFO 及以上)都会被放行。因此 DEBUG 主要用于配置场景,是"打开全部调试输出"的快捷开关。

需要注意一个命名细节:源码中该级别枚举名是 DBG 而不是 DEBUG,注释说明这是因为一些开源项目会把 DEBUG 定义为预处理器宏(参见 LogLevel.h)。DBG 本身并不是设计用来在代码中直接使用的级别,头文件建议在代码里使用更细粒度的 DBGn,而在分类配置里再用 DBG 或某个 DBGn 来控制开关。

三、级别字符串的解析与格式化:别名、编号与整数

配置字符串、命令行参数以及 logLevelToString() 的输出都需要在"级别名"与"枚举值"之间转换,这部分逻辑实现在 folly/logging/LogLevel.cppstringToLogLevel()logLevelToString() 中。

stringToLogLevel() 的解析规则(全部大小写不敏感)包括:

  • 标准名称UNINITIALIZEDNONEDEBUG/DBGINFOWARN/WARNINGERROR/ERRCRITICALDFATALFATALMAX/MAX_LEVEL
  • 编号级别DBG0DBG9INFO0INFO9,超出范围的编号会抛出 std::range_error(如 DBG10INFO15);
  • LogLevel::fooLogLevel(1234) 包装形式:函数会把 LogLevel::WARNLogLevel(1234) 剥壳后继续解析,保证 logLevelToString() 的输出可以被原样回读(参见 LogLevel.cpp);
  • 纯整数:任意正整数会被直接转换为对应的 LogLevel 数值,例如 4000 等价于 ERR1994 等价于 DBG5

logLevelToString() 则按相反方向输出:标准级别输出标准名(注意 DBG 输出为 DEBUG),编号级别输出 DBG0DBG9INFO0INFO9,无法识别的数值输出为 LogLevel(<数值>) 形式(参见 LogLevel.cpp)。

四、级别如何生效:LogCategory、有效级别与继承

日志级别本身不会单独起作用,它必须挂在"日志分类"(LogCategory)上。每个分类维护一个 LogLevel 级别设置,控制该分类以及子分类哪些消息被启用。核心实现在 folly/logging/LogCategory.h

  • getLevel() 返回该分类自身配置的级别;
  • getEffectiveLevel() 返回实际生效的级别(原子加载,memory_order_acquire),它存储在 effectiveLevel_ 这个 std::atomic<LogLevel> 成员中;
  • setLevel(LogLevel level, bool inherit = true) 用于修改级别与继承标志。

继承规则是理解级别生效的关键:分类的层级结构由名称中的 . 分隔符决定(例如 spacesimspacesim.ships 的父分类)。当 inherit 为 true 时,某个分类的有效级别是其自身级别与其父分类有效级别中"更小(更啰嗦)"的那个;当 inherit 为 false 时,有效级别就是它自身的级别,完全不受父分类影响(参见 LogCategory.h)。因此向父分类(乃至根分类)下调级别,会沿层级自动传播到所有子孙分类——这正是"在配置里把 spacesim.ships 设为 INFO 就能一次打开该模块全部代码日志"的底层原理。相关的分类层级与传播语义可进一步参考 folly/logging/docs/LogCategories.md

五、在配置字符串中使用日志级别

日志库支持通过配置字符串控制级别,这是日常调参最常用的方式,完整语法见 folly/logging/docs/Config.md。基本格式是逗号分隔的 分类=级别 列表,单独的级别名则作用于根分类:

folly=INFO,folly.io.async=DBG2
WARN

级别既可以用名称(INFODBG2WARNERROR 等),也可以用正整数指定。基于上文级别体系,常见的配置示例及其含义:

配置字符串 效果
ERROR 根分类级别设为 ERRERROR 是别名)
folly=INFO,folly.io=DBG2 folly 分类为 INFOfolly.io 分类为 DBG2
folly=DBG2,folly.io:=INFO folly.io:= 禁用继承,有效级别被钉死在 INFO,父分类的 DBG2 不会渗透进来
folly:=WARN folly 分类锁定为 WARN,屏蔽其噪音输出,其余分类保持默认 INFO

其中 := 运算符正是对应上面 setLevel(level, inherit=false) 的语义——禁用该分类的级别继承。如果你希望"某个子模块的调试输出不随上级模块被放大"或"某组件太吵想单独按掉",:= 是最直接的手段。

六、在代码中使用日志级别

日常写代码时,级别作为宏的第一个参数出现(相关用法详见 folly/logging/docs/Usage.md):

// 最常用:自动按文件名选择分类
XLOG(INFO) << "hello world!";
XLOG(ERR) << "something went wrong";
XLOG(DBG1) << "detailed debug info";

// 多参数自动拼接:folly::to<string>()
XLOG(INFO, "the number is ", 2 + 2);  // 输出 "the number is 4"

// 指定分类
folly::Logger eventLogger("eden.events");
FB_LOG(eventLogger, WARN) << "something happened";

// Python 风格格式化
XLOGF(DBG1, "cannot engage {} thruster: {}", thruster.name(), err.what());

XLOG 系列宏定义在 folly/logging/xlog.hFB_LOG 定义在 folly/logging/Logger.h。宏的关键优势在于惰性求值:当消息被禁用时,参数表达式根本不会被执行,日志语句退化为一次条件判断,因此可以在热点代码中放心保留大量 DBG 级别的语句(可参考 folly/logging/docs/Overview.md 对"非常廉价的调试日志"目标的说明)。

七、实践建议

综合文档与源码,给出几条可落地的级别选用建议:

  1. 用编号调试级别做精细控制:在代码中尽量使用 DBG0DBG9 而非笼统的 DEBUG,这样运维时可以通过分类配置精确选择"啰嗦到什么程度"——DBG0 只给最关键的一层调试信息,DBG9 则近似全量追踪。
  2. DEBUG(即 DBG)做一键总开关:排查问题时把分类级别设为 DEBUG,即可一次启用全部 DBG0DBG9 消息;问题定位后再逐步收紧到某个具体的 DBGn
  3. 区分 ERR / CRITICAL / FATAL 的严重度阶梯:可恢复的错误用 ERR,需要立即人工关注但程序仍可继续的用 CRITICAL,不可恢复的状态用 FATAL(或 debug 阶段用 DFATAL 兜底)。
  4. 善用 := 切断继承:对于依赖库或不关心的模块,用 folly:=WARN 之类的写法单独压音量,避免被上层分类的 verbose 级别放大。
  5. 级别数值可用于程序化判断:由于所有级别都是可比较的 uint32_t 枚举,且 LogLevel 支持与整数做 +/- 运算(见 LogLevel.h,加法结果封顶于 MAX_LEVEL),你可以在代码里对级别做相对调整,例如 level + 1 表示"稍重要一级"。

八、延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525