ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

folly logging 库使用指南:从 XLOG 宏到日志配置的完整实战

folly logging 库使用指南:从 XLOG 宏到日志配置的完整实战 folly logging 库使用指南从 XLOG 宏到日志配置的完整实战【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly本篇指南以 folly 项目自带的 logging 使用文档 为核心骨架系统讲解 folly logging 库的日志宏XLOG/FB_LOG/XLOGF、日志分类自动选择机制、配置字符串语法基础语法与 JSON 语法并结合 Config.md、LogLevels.md 与 example 目录中的源码实例进行纵深展开。读完本篇你将能够熟练地在自己的 C 工程中使用 folly logging 输出日志并通过命令行参数或配置文件精确控制任意模块的日志级别与输出目标。为什么使用日志宏惰性求值folly logging 要求通过少量预处理器宏来记录日志核心原因在于宏可以做到参数的惰性求值当一条日志消息被禁用时其参数表达式根本不会被执行。这意味着你可以放心地在性能敏感的热路径上保留大量调试日志日常运行时它们只退化为一次轻量的条件检查而不会产生任何参数拼接或格式化开销。这一设计目标在 logging/README.md 中有明确说明disabled log statements should boil down to a single conditional check被禁用的日志语句应退化为一次条件检查这也是该库坚持使用宏而非函数接口的根本原因。日志宏XLOG()按文件自动分类绝大多数场景下你只需要XLOG()XLOG(INFO) hello world!;XLOG()定义在 folly/logging/xlog.h 中它的第一个参数是日志级别之后可以跟随任意数量的参数。宏内部会依据当前源文件自动推导日志分类因此你无需手动维护每个文件的 logger 对象。FB_LOG()显式指定日志分类如果希望把消息记录到某个显式的日志分类可以使用FB_LOG()。它与XLOG()行为一致但要求第一个参数是一个folly::Logger对象用来指明目标分类folly::Logger eventLogger(eden.events); FB_LOG(eventLogger, INFO) something happened;FB_LOG()定义在 folly/logging/Logger.h 中。folly::Logger本质上是LogCategory指针的轻量包装主要作为语法糖存在——构造时若该分类不存在会自动创建见 Logger.h。宏参数函数风格与流式风格XLOG()/FB_LOG()的第一个参数是日志级别可用级别清单见 LogLevels.md。其后的附加参数会通过folly::tostd::string()转换为字符串并拼接为消息内容。例如XLOG(INFO, the number is , 2 2);将产生消息the number is 4——参数是在级别检查通过之后才被求值并拼接的。两种参数风格可以混用函数风格参数与ostream风格流式输出可以同时出现XLOG(INFO, the number is ) 2 2;FB_LOG()除第一个参数必须是Logger对象外其余参数与XLOG()完全相同。Python 风格格式化XLOGF()与FB_LOGF()XLOGF()和FB_LOGF()允许使用类似 Pythonstr.format()的格式串来生成日志消息XLOGF(DBG1, cannot engage {} thruster: {}, thruster.name(), err.what());其内部调用fmt::format()完成格式化因此支持fmt的完整格式语法例如精度控制{:.3f}见下文示例程序。这一点也是 folly logging 相对 glog 的显著优势之一——提供类型安全的fmt::format()格式化机制见 README.md。条件与限流变体宏在 xlog.h 中除了基础的XLOG/XLOGF还提供了一系列实用变体XLOG_IF(level, cond, ...)/XLOGF_IF(level, cond, fmt, ...)仅当条件谓词为真时才记录注意条件只在日志级别检查通过后才被求值XLOG_EVERY_MS(level, ms, ...)每个ms毫秒最多记录一次线程安全内部基于IntervalRateLimiter定义于 folly/logging/RateLimiter.hXLOG_EVERY_N(level, n, ...)大约每n次调用记录一次计数器为进程级且线程安全但在高竞争下使用非原子自增可能出现少量过度或欠记录见 xlog.h 的注释说明以上还各有_IF、_OR条件为真或达到频率阈值等组合变体以及XLOG_SET_CATEGORY_NAME()用于覆盖分类名。日志分类自动选择XLOG()会根据当前源文件路径自动选择日志分类路径中的目录分隔符被替换为.字符。例如源文件src/tiefighter/thruster.cpp中默认的XLOG()分类是src.tiefighter.thruster.cpp。这一规则在 xlog.h 的头部注释中有明确说明。注意分类名依赖编译器传入的__FILE__如果你在src/foo目录下只以bar.cpp作为文件名编译则分类名只会是bar。因此官方建议总是从仓库根目录调用编译器以保证分类名完整可预测见 xlog.h。在.cpp文件中可以通过XLOG_SET_CATEGORY_NAME()宏在文件顶层作用域覆盖默认分类名之后该文件内所有XLOG()语句都会使用新分类名。切勿在头文件中使用此宏否则会波及所有包含该头文件的.cpp文件见 Usage.md。分类层级、级别检查与日志处理器在深入配置语法之前需要理解分类与级别的底层模型见 README.md分类名是分层的以.分隔folly.io与folly.futures都是folly的子分类根分类名为空字符串准入检查消息记录到某分类时会比较消息级别与该分类的有效级别。默认情况下分类的有效级别是其自身级别与所有父分类级别中的最小值即更详细的那一个——因此调高某个分类的详细程度会自动提升其整棵子分类树的详细程度继承可关闭可以为特定分类关闭级别继承从而在大范围调高详细度、个别子分类仍保持低详细度之间取得平衡处理器LogHandler可挂载到分类上消息到达后被交给该分类上的所有处理器并沿父链向上传播至根分类。处理器可执行任意动作写入本地文件、打印到 stdout/stderr、发送到远端日志服务等启用继承的最小级别LoggerDB在消息启用与向父分类传播时使用不同的最小级别判断可通过 JSON 配置中的propagate字段单独控制。LogCategory、LogLevel、LogHandler等核心类的实现在 folly/logging 目录下其中 LogCategory.cpp、LogLevel.cpp、StandardLogHandler.cpp 对应了上述准入、级别解析与消息派发逻辑。日志级别清单LogLevel定义于 folly/logging/LogLevel.h级别从高到低依次为FATAL记录后直接中止程序不可被禁用若此时没有任何已配置的处理器消息会打印到 stderr 以确保程序不会静默崩溃DFATAL类似FATAL但仅在调试构建未定义NDEBUG中才中止程序CRITICAL重要错误消息介于ERR与FATAL之间ERR错误消息。之所以命名为ERR而非ERROR是因为 Windows 公共头文件会把ERROR#define为预处理器宏见 LogLevels.mdWARN别名WARNING警告消息INFO普通信息消息DBG0~DBG910 个编号调试级别。DBG0比DBG9更重要编号可理解为冗长度分类级别设为DBG5会启用DBG0~DBG5以及更高级别的INFO等而DBG6~DBG9被禁用DEBUG位于DBG9之下设置该级别会自动启用所有编号DBG级别。编译期裁剪FOLLY_XLOG_MIN_LEVELxlog.h 还提供了编译期裁剪宏FOLLY_XLOG_MIN_LEVEL级别低于该值的XLOG消息若能在编译期确定不会打印则整条语句会被编译掉实现零开销。该值不应高于FATALstatic_assert会禁止禁用致命消息。日志配置配置入口 APIfolly logging 提供了多层配置 API见 Usage.md 与 Init.hfolly::parseLogConfig(configString)把配置字符串解析为LogConfig对象字符串语法见 Config.md同时兼容 JSON见下文LoggerDB::get().updateConfig(cfg)以增量方式更新当前配置未提及的设置保持不变LoggerDB::get().resetConfig(cfg)用新配置整体替换现有设置folly::initLogging(configString)程序启动期的便捷入口——先取getBaseLoggingConfig()返回的基础配置再用传入的配置字符串通过parseLogConfig()解析并覆盖基础配置最后以updateConfig()应用到全局单例LoggerDB。出错时抛异常initLoggingOrDie()则在出错时打印错误并exit(1)优雅终止。可选的folly::getBaseLoggingConfig()允许各可执行文件自定义默认配置返回的字符串需为静态存储期、以\0结尾见 Init.h。基础配置语法配置字符串最常用的形态是逗号分隔的CATEGORYLEVEL列表例如follyINFO,folly.io.asyncDBG2也可以只写一个级别名用于设置根分类WARN完整的parseLogConfig()文法如下摘自 Config.mdconfig :: category_configs handler_configs category_configs :: category_config | category_config , category_configs | empty_string handler_configs :: ; handler_config | ; handler_config handler_configs | empty_string category_config :: cat_level_config handler_list cat_level_config :: level | category_name level | category_name : level handler_list :: : handler_name handler_list | empty_string handler_config :: handler_name handler_type : handler_options | handler_name : handler_options handler_options :: , option_name option_value handler_options | empty_string atom :: any sequence of characters except ;, ,, , or :, with leading and trailing whitespace ignored level :: log_level_string | positive_integer要点基础格式整体以分号分段——第一个分号之前是分类配置逗号分隔的列表其后每个分号段定义一条日志处理器配置。基础格式不支持任何字符转义如果分类名中需要包含逗号、分号等特殊字符请改用 JSON 格式。分类配置的三种写法分类配置条目形如NAMELEVEL:HANDLER1:HANDLER2省略分类名与配置应用于根分类根分类也可以显式命名为空字符串或.与:的区别:会关闭该分类的级别继承强制有效级别精确等于指定值即使父分类有更详细的设置也不会被继承处理器列表可省略更新时保持该分类现有处理器不变若显式给出空列表:后无处理器则清空该分类的处理器列表。处理器配置每个处理器配置段形如NAMETYPE:OPTION1VALUE1,OPTION2VALUE2NAME是处理器名TYPE是处理器类型对应注册在LoggerDB上的LogHandlerFactory逗号分隔的namevalue选项会被交给该类型的工厂解析。省略类型NAME:OPTION1VALUE1表示更新一个已存在的处理器只有提到的选项被更新其余保持不变。注意更新已有处理器包括asynctrue等选项修改只能用于updateConfig()不能用于resetConfig()因为重置模式下没有可引用的既有对象。基础语法示例以下示例全部来自 Config.md可直接在命令行或配置文件中使用配置字符串含义ERROR将根分类级别设为ERR配置字符串中ERROR是LogLevel::ERR的合法别名follyINFO,folly.ioDBG2folly分类设为 INFOfolly.io分类设为 DBG2follyDBG2,folly.io:INFOfolly设为 DBG2folly.io设为 INFO 且关闭继承因此发往folly.io的 DBG2 消息会被丢弃尽管父分类已启用该级别folly:WARNfolly分类设为 WARN 并关闭继承默认级别通常是 INFO。适合用来静默某个话痨组件而保持其他部分不变ERROR:stderr, follyINFO; stderrstream:streamstderr根分类级别设为 ERROR 并挂到stderr处理器folly级别设为 INFO定义写往 stderr 的处理器stderrERROR:x,follyINFO:y;xstream:streamstderr;yfile:path/tmp/y.log定义两个处理器x写 stderr、y写文件/tmp/y.log根分类用xfolly分类用yERROR:default:x; defaultstream:streamstderr; xfile:path/tmp/x.log定义defaultstderr与x/tmp/x.log两个处理器根分类 ERROR 级别同时使用两者ERROR:根分类级别设为 ERR 并清空其处理器列表:后无处理器注意与完全不写:保持现有处理器不变的区别;defaultstream:streamstdout不改任何分类设置仅定义写往 stdout 的default处理器适用于已有该处理器并想更新其配置的场景ERROR; stderr:asynctrue根分类级别设为 ERR并给已存在的stderr处理器打开异步只能搭配updateConfig()使用INFO; default:asynctrue,sync_levelWARN根分类级别设为 INFOdefault处理器开启异步日志同时设置sync_levelWARN保证 WARN 及以上级别同步落盘崩溃前可持久化WARN 以下级别非阻塞最后一条示例中的sync_level是一个很实用的生产配置异步日志降低写盘延迟而关键级别WARN仍同步处理避免进程崩溃前丢失重要日志。JSON 配置语法parseLogConfig()会同时接受以{开头允许前置空白的 JSON 对象字符串也可以用parseLogConfigJson()显式要求按 JSON 解析。JSON 解析采用宽松模式允许 C/C 风格注释与尾随逗号见 Config.md。JSON 配置必须是对象含两个可选成员categories与handlers其余成员被忽略。categories是一个对象键为分类名值为该分类的配置对象字段包括level必填字符串或正整数指定该分类的日志级别inherit可选默认true布尔值是否从父分类继承有效级别propagate可选字符串或正整数指定应传播到父分类的消息的最小级别默认等于最小级别。若某个分类的值直接写成字符串或整数而非对象则等价于指定其level且inherit为 true。handlers是一个对象键为处理器名值为处理器配置对象字段包括type处理器类型名必须对应注册在LoggerDB上的LogHandlerFactory缺省表示更新一个已存在的处理器options会并入其现有选项options可选字符串到字符串的映射交给对应工厂用于构造处理器。完整的 JSON 示例来自 Config.md注意原示例中categories对象后缺少逗号属文档笔误实际 JSON 中应补齐{ categories: { foo: { level: INFO, handlers: [stderr] }, foo.only_fatal: { level: FATAL, inherit: false } }, handlers: { stderr: { type: stream, options: { stream: stderr, async: true, sync_level: WARN, max_buffer_size: 4096000 } } } }该示例配置了分类foo为 INFO 并挂载stderr处理器分类foo.only_fatal为 FATAL 且关闭继承只接受致命消息处理器stderr为流式处理器写 stderr、异步模式、WARN 及以上同步、缓冲区上限 4 MB。程序化配置与注意事项除配置字符串外也可以直接操作LogCategory对象上的级别等设置。LogConfig内部类同样允许用户程序化构造后交给updateConfig()/resetConfig()。官方建议创建新LogHandler优先走这两个 API——如果你手动 new 一个处理器并直接挂到分类上LoggerDB::getConfig()将无法为它返回完整信息因为它没有可写入配置的名称和类型见 Config.md。端到端示例example 程序拆解仓库自带的 folly/logging/example 演示了上述全部要点。以 main.cpp 为例#include folly/init/Init.h #include folly/logging/Init.h #include folly/logging/example/lib.h #include folly/logging/xlog.h // main() 之前使用 XLOG() 是安全的会采用 folly::initializeLoggerDB() // 提供的默认设置。 static ExampleObject staticInitialized(static); // 用 FOLLY_INIT_LOGGING_CONFIG 声明基础配置 // 根分类 WARNINGfolly 分类 INFOdefault 处理器异步且 WARN 同步。 FOLLY_INIT_LOGGING_CONFIG( .WARNING,follyINFO; default:asynctrue,sync_levelWARNING); int main(int argc, char* argv[]) { // initLogging() 之前低于 INFO 的消息被忽略ERR 会打到 stderr XLOG(DBG) log messages less than INFO will be ignored before initLogging; XLOG(ERR) error messages before initLogging() will be logged to stderr; // folly::Init() 自动按 FOLLY_INIT_LOGGING_CONFIG 声明 // 与 --logging 命令行 flag 完成初始化。 folly::Init init(argc, argv); // 本文件所有 XLOG() 都记录到 folly.logging.example.main 分类 XLOG(INFO, now the normal log settings have been applied); XLOG(DBG1, log arguments are concatenated: , 12345, , , 92.0); XLOGF(DBG1, XLOGF supports {}-style formatting: {:.3f}, python, 1.0 / 3); XLOG(DBG2) streaming syntax is also supported: 1234; XLOG(DBG2, if you really want, , you can even) mix function-style and streaming syntax: 42; XLOGF(DBG3, and {} can mix {} style, you, format) and streaming; ExampleObject(foo); XLOG(INFO) main returning; return 0; }示例中值得注意的运行语义初始化之前static对象构造与main()早期阶段的XLOG使用默认设置INFO 打到 stderr因此示例特意用 DBG 与 ERR 各打一条来演示差异FOLLY_INIT_LOGGING_CONFIG声明式给出基础配置与folly::Init组合后还会自动接入--logging命令行 flag——这是配置来自命令行 flag 或配置文件的标准落地方式对应initLogging()的设计目标分类名自动推导main.cpp 中所有XLOG归属folly.logging.example.main而 lib.cpp 中的XLOGF(DBG1, ExampleObject({}) at {} destroyed, value_, fmt::ptr(this))归属folly.logging.example.lib——同一个对象析构时消息会出现在与源文件对应的分类下便于按文件定位日志来源。lib.h中ExampleObject的析构打印演示了fmt::ptr()输出对象地址的用法也展示了XLOGF的 Python 风格格式化与对象生命周期日志的结合。总结folly logging 通过宏 惰性求值保证了禁用日志的近零开销通过文件名推导分类 分层级别继承让日志级别的运行时控制变得极其灵活再通过统一的基础/JSON 配置语法把分类与处理器的设置收敛为可命令行传递的字符串。核心 API 与文件速查如下日志宏XLOG/XLOGF/XLOG_IF/XLOG_EVERY_MS/XLOG_EVERY_N等见 folly/logging/xlog.hFB_LOG/FB_LOGF/FB_LOG_RAW见 folly/logging/Logger.h日志级别见 folly/logging/docs/LogLevels.md 与 folly/logging/LogLevel.h配置语法与示例见 folly/logging/docs/Config.md解析实现在 folly/logging/LogConfigParser.h初始化 APIinitLogging/initLoggingOrDie/getBaseLoggingConfig见 folly/logging/Init.h可运行示例见 folly/logging/example/main.cpp 与 folly/logging/example/lib.cpp。理解了这四层宏、分类、级别、配置你就能像在 Facebook 内部那样把调试日志铺满整个代码库再在线上按需精准开启任意模块的详细日志。【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表