资讯动态

glog 自定义 Sink(Custom Sinks)完整指南:将日志输出到任意目的地

发布时间:2026/9/28 13:01:46 来源:尧图企业网站定制
后端【免费下载链接】glogC implementation of the Google logging module项目地址https://gitcode.com/gh_mirrors/glog6/glog点击查看免费下载glog 默认将日志写入文件、stderr或stdout但许多场景需要把日志送到文件之外的任意目的地。本文基于 glog 官方文档docs/sinks.md深入讲解google::LogSink接口的完整用法如何自定义实现send()、如何通过AddLogSink/RemoveLogSink注册与注销、如何用LOG_TO_SINK系列宏直接定向输出并给出可编译运行的完整示例与底层源码级实现原理。何时需要自定义 Sink在默认配置下glog 会把日志写到本地文件、stderr或stdout具体行为由FLAGS_logtostderr、FLAGS_logtostdout等标志控制参见 docs/flags.md。但在某些场景下这些默认目的地并不够用例如将日志发送到网络日志收集服务如 syslog、Kafka、远程日志聚合平台将日志写入数据库或其他持久化存储将日志转发到消息队列供下游消费在 GUI 应用中把日志显示到界面对日志做二次加工后再输出例如脱敏、格式化、统计。此时 glog 提供了google::LogSink接口实现该接口后库会在每次记录日志时调用你的实现把日志内容写到任意你想要的地方。LogSink 基本接口LogSink接口定义在 src/glog/logging.h核心是一个纯虚函数class LogSink { public: virtual ~LogSink(); // 每次记录日志时被库调用。 // message_len 不包含末尾的 \n。 // 注意本方法内不能使用 LOG() 或 CHECK() virtual void send(LogSeverity severity, const char* full_filename, const char* base_filename, int line, const LogMessageTime time, const char* message, size_t message_len) 0; // 可选等待 send() 的日志逻辑全部完成默认空实现。 virtual void WaitTillSent(); // 静态辅助函数返回日志消息的标准文本输出含时间戳前缀等。 static std::string ToString(LogSeverity severity, const char* file, int line, const LogMessageTime time, const char* message, size_t message_len); };你需要继承LogSink并实现send()。各参数含义如下参数含义severity日志级别INFO/WARNING/ERROR/FATAL之一对应枚举google::LogSeverity定义见 src/glog/log_severity.hfull_filename触发日志的源文件完整路径base_filename触发日志的源文件名不含目录line触发日志的行号time日志时间类型为LogMessageTimemessage日志消息正文不含时间戳前缀且不包含末尾换行符message_len消息正文的字节长度注意嵌套日志导致死锁send()被调用时日志系统持有内部互斥锁mutex因此在send()内严禁再调用LOG()或CHECK()否则会造成死锁。这在头文件注释中也有明确说明见 src/glog/logging.h。接口还提供了两个辅助成员WaitTillSent()可选重写。在每次send()返回之后、LogMessage对象退出或崩溃之前被调用默认什么都不做。利用它可以实现更复杂的send()逻辑例如在send()中把消息加入队列并唤醒另一个线程去真正写日志同时自身可以安全地调用LOG()而WaitTillSent()则等待该线程处理完成从而既避免死锁又保证消息顺序源码注释见 src/glog/logging.h。ToString(...)静态方法把日志参数渲染成 glog 标准的文本行[IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg格式实现见 src/logging.cc。在实现send()时可用它直接得到与文件日志一致的格式化文本。注册与注销 Sink实现好LogSink后通过以下两个线程安全的函数注册或注销声明见 src/glog/logging.h实现见 src/logging.ccvoid google::AddLogSink(LogSink* destination); void google::RemoveLogSink(LogSink* destination);AddLogSink把 sink 指针加入全局注册表此后所有通过LOG()、CHECK()等宏产生的日志都会同时发送给该 sinkRemoveLogSink把 sink 从注册表移除之后不再接收日志两者内部都用互斥锁保护SinkLock l{sink_mutex_}见 src/logging.cc因此线程安全可在任意线程调用。危险所有权管理注册表保存的是指针而非对象拷贝。在调用RemoveLogSink解除注册之前严禁销毁该LogSink实例否则注册表将持有悬空指针后续日志触发send()时会造成未定义行为。从源码看注册的 sink 会存放在一个std::vectorLogSink*中src/logging.cc每次记录日志时LogDestination::LogToSinks会持共享锁遍历该 vector 依次调用每个 sink 的send()src/logging.cc。RemoveLogSink用std::remove加erase删除匹配指针不保证保留原有顺序源码注释明确“doesnt keep the sinks in order”。直接定向日志不注册也可用除了全局注册还可以在不注册的情况下把某条具体日志直接定向到某个 sink 实例。glog 提供了两个宏定义见 src/glog/logging.hLOG_TO_SINK(sink, severity) LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)LOG_TO_SINK(sink, severity)把消息同时发送给该 sink并且照常写入全局日志如日志文件相当于“双写”LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)只发送给该 sink不写入日志文件等全局日志目的地。两者的第二个参数severity必须是INFO、WARNING、ERROR、FATAL之一不能是变量因为宏内部拼成google::GLOG_##severity。底层原理见 src/logging.ccLOG_TO_SINK展开为LogMessage(..., sink, /*also_send_to_log*/true)最终调用LogMessage::SendToSinkAndLog()——先SendToSink()再把消息交给常规日志链路src/logging.ccLOG_TO_SINK_BUT_NOT_TO_LOGFILE则传false只调用SendToSink()src/logging.cc。SendToSink中可以看到send()收到的message是从message_text_中剔除前缀num_prefix_chars_和末尾换行后的正文与接口文档描述一致。完整示例把日志输出到 std::cout仓库中 examples/custom_sink.cc 给出了一个完整可编译运行的示例展示了注册/注销与直接定向两种用法#include glog/logging.h #include algorithm #include cstddef #include iostream #include iterator namespace { struct MyLogSink : google::LogSink { // (1)! void send(google::LogSeverity severity, const char* /*full_filename*/, const char* base_filename, int line, const google::LogMessageTime /*time*/, const char* message, std::size_t message_len) override { std::cout google::GetLogSeverityName(severity) base_filename : line ; std::copy_n(message, message_len, std::ostreambuf_iteratorchar{std::cout}); std::cout \n; } }; } // namespace int main(int /*argc*/, char** argv) { google::InitGoogleLogging(argv[0]); MyLogSink sink; google::AddLogSink(sink); // (2)! LOG(INFO) logging to MySink; google::RemoveLogSink(sink); // (3)! // 不注册也可以直接向 sink 写日志 LOG_TO_SINK(sink, INFO) direct logging; // (4)! LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, INFO) direct logging but not to file; }逐段解读实现自定义 sinkMyLogSink继承google::LogSink并重写send()把日志级别名、源文件名、行号和消息正文写到std::cout。这里用GetLogSeverityName(severity)得到级别字符串如INFO用std::copy_n按message_len精确拷贝消息正文。注册 sink调用google::AddLogSink(sink)后全局日志宏如LOG(INFO)产生的日志就会同时出现在std::cout。注销 sink不再需要时调用google::RemoveLogSink(sink)从注册表移除。注意 sink 是栈上对象注销后其生命周期随作用域结束而自然结束这正是文档强调的“先注销、后销毁”的正确范式。直接定向即使不注册也可以用LOG_TO_SINK/LOG_TO_SINK_BUT_NOT_TO_LOGFILE把特定消息发给该 sink。运行示例把日志目录设为当前目录使日志文件落在本地GLOG_log_dir. ./custom_sink_example终端stdout输出INFO custom_sink.cc:63 logging to MySink INFO custom_sink.cc:68 direct logging INFO custom_sink.cc:69 direct logging but not to file对应的日志文件内容custom_sink_example.INFO之类的文件Log file created at: 2024/06/11 13:24:27 Running on machine: pc Running duration (h:mm:ss): 0:00:00 Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg I20240611 13:24:27.476620 126237946035776 custom_sink.cc:63] logging to MySink I20240611 13:24:27.476796 126237946035776 custom_sink.cc:68] direct logging注意对比custom_sink.cc:69的direct logging but not to file只出现在 stdout 中没有出现在日志文件里——这正是LOG_TO_SINK_BUT_NOT_TO_LOGFILE的效果LOG_TO_SINK那条则会同时写入文件。这是验证两个宏行为差异的直观手段。实践要点与注意事项结合文档警告与源码实现使用自定义 sink 时有以下几点需要牢记不要在send()内使用LOG()/CHECK()send()执行期间日志系统持有互斥锁嵌套日志会死锁文档docs/sinks.md中的 warning以及 src/glog/logging.h 均有说明。若确需在 send 逻辑中记日志可考虑队列 后台线程模式并用WaitTillSent()同步。send()实现必须线程安全注册的 sink 是共享实例会被产生LOG(XXX)的任意线程调用src/glog/logging.h 注释明确要求 implementations must be thread-safe。多个线程同时写同一 sink 时需要自行加锁或保证目标输出本身线程安全。所有权与生命周期LogSink实例在调用RemoveLogSink之前不能被销毁。最佳实践是让 sink 的生命周期覆盖整个注册区间如栈对象 作用域管理或std::unique_ptr长期持有。区分两种使用方式全局注册AddLogSink影响所有日志宏直接定向LOG_TO_SINK系列宏只影响单条消息且不必注册。两种方式可同时使用注意注册后LOG_TO_SINK的消息会同时走全局 sink 和指定 sink。message_len语义send()收到的message不含时间戳前缀和末尾换行message_len即正文长度如需完整标准格式可用静态方法LogSink::ToString(...)生成。相关源码与文档速查接口定义与宏声明src/glog/logging.h、src/glog/logging.h注册/注销实现src/logging.cc、src/logging.ccsink 分发与等待逻辑src/logging.cc直接定向的底层路径src/logging.cc、src/logging.cc完整可运行示例examples/custom_sink.cc日志级别枚举src/glog/log_severity.h日志标志与默认行为docs/flags.md日志总体使用指南docs/logging.md赞分享后端【免费下载链接】glogC implementation of the Google logging module项目地址https://gitcode.com/gh_mirrors/glog6/glog点击查看免费下载相关推荐高效glog日志格式自定义完整指南打造专业级日志输出高效glog日志格式自定义完整指南打造专业级日志输出 想要提升C应用程序的日志输出质量吗Google Logging Library glog 提供了强后端Terragrunt 日志格式化完全指南用 --log-custom-format 自定义日志输出Terragrunt 日志格式化完全指南用 log custom format 自定义日志输出 Terragrunt 作为 OpenTofu/TerraforCLIDevOps云原生Hydra 日志自定义指南基于 dictConfig 定制 Hydra 与任务日志输出Hydra 日志自定义指南基于 dictConfig 定制 Hydra 与任务日志输出 Hydra 在内部直接使用 Python 标准库 logging 的开发工具后端CLI上一篇如何配置dnGrep实现批量文件搜索和替换完整操作流程下一篇Chakram核心功能解析HTTP状态码、Cookie与响应时间测试实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑