不久前,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_FILE
、CODE_LINE
和 MESSAGE_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()
。
祝您日志记录愉快!