日志记录及更多

不久前,GLib 获得了一个新的“结构化日志记录”功能。与此同时,它还获得了将日志写入 systemd 日志的支持。显然,GLib 中的日志记录变得更加复杂,并且可能有点令人困惑。

本文旨在澄清一些事情。

结构化与非结构化

传统的 GLib 日志记录工具是 g_message()g_debug() 等宏,它们最终调用 g_log() 函数,然后使用通过 g_log_set_handler() 设置的日志处理程序来执行实际写入。您可以在日志中放入任何您喜欢的信息,但它必须全部格式化为单个字符串,即消息。

g_debug ("You have %d eggs", 12 + 2);

g_log (G_LOG_DOMAIN,
       G_LOG_LEVEL_DEBUG,
       "You have %d eggs", 12 + 2);

使用新的结构化日志记录工具,您调用 g_log_structured(),然后使用日志写入函数来执行写入。到目前为止,这与较旧的日志记录工具非常相似。结构化日志的优点在于,您可以将多个字段放入日志中,而无需将其全部格式化为字符串。相反,您传递一个日志字段数组,这些字段是键值对。

g_log_structured (G_LOG_DOMAIN,
                  G_LOG_LEVEL_DEBUG,
                  "CODE_FILE", "mysource.c",
                  "CODE_LINE", 312,
                  "MESSSAGE_ID", "06d4df59e6c24647bfe69d2c27ef0b4e",
                  "MESSAGE", "You have %d eggs", 12 + 2);

CODE_FILECODE_LINEMESSAGE_ID 这里只是“标准”字段的示例。您也可以发明自己的字段。请注意,您仍然可以使用 printf 风格的格式化来格式化 MESSAGE 字段。

所以 GLib 现在有两个独立的日志记录工具。为了使事情更有趣,我们允许您将 g_message()g_debug() 等包装宏重定向为在底层使用 g_log_structured() 而不是 g_log()。要做到这一点,请在包含 glib.h 之前定义 G_LOG_USE_STRUCTURED 宏。

为什么这很有用?一方面,它可以省去您替换所有 g_debug() 的麻烦,并且仍然允许您利用结构化日志记录的一些优势 - 以这种方式使用时,传统的宏会为日志域、代码文件和行以及其他一些字段使用单独的字段,这对于在生成的日志中进行过滤和搜索非常有用,尤其是在使用 systemd 日志时。

另一个优点是,您可以使用单个后端(即日志写入函数)来控制新旧日志调用的最终位置。

我的所有日志都去哪儿了?

结构化日志记录通常与 systemd 日志相关联。因此,人们期望 g_log_structured() 的输出进入日志是很自然的。对于服务,或者当您从桌面图标启动应用程序时,这非常有用。但是,如果您从终端运行它,您可能希望在那里看到其输出。

为了满足这些相互竞争的需求,GLib 默认的日志写入函数试图变得智能。如果它检测到 stderr 被重定向到 journald 套接字,那么它会将结构化输出写入日志。否则,它会格式化消息并将其写入 stderr

GNOME Shell 和 DBus 都会在启动应用程序或服务时安排将 stderr 重定向到日志。将 stderr 显式重定向到日志的一种方法是在 systemd-cat 下运行您的应用程序。

systemd-cat my-app-that-logs

如果您确定希望日志始终进入日志,您可以告诉 GLib 使用一个执行此操作的日志写入器。

g_log_set_writer_func (g_log_writer_journald, NULL, NULL)

超越默认

即使 GLib 默认提供的日志写入函数应该满足许多需求,您可能仍然需要编写自己的函数。在这种情况下,GLib 有许多有用的函数可以帮助您,例如 g_log_writer_format_fields()g_log_writer_is_journald()g_log_writer_supports_color()

祝您日志记录愉快!

参考资料