日志记录及更多

不久前,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()

祝您日志记录愉快!

参考

  • Philipps 关于结构化日志记录的演讲
  • GLib 日志记录文档
  • Systemd 日志文档

关于“日志记录及更多”的 2 条评论

  1. GLib 日志记录文档说:“不要在库代码中使用 g_warning()。改用 GError。”

    这很不现实。如果可以将错误映射到 API 函数调用,那么很好,GError 当然是首选方法。但是在任何足够复杂的库中,这种情况都很少发生……。

  2. @Michael:其意图是“不要使用 g_warning()/g_critical() 来进行可恢复的错误报告,并指望用户来报告它们”。警告(和严重)旨在用于程序员错误和内部状态一致性检查 — 并且预期任何遵循警告的内容都是未定义的行为。就我个人而言,我更喜欢断言而不是仅仅发出警告,因为这样可以捕获真正的程序员错误,但是警告通常具有更容易理解的消息。

    另一方面,GError 不用于程序员错误或内部状态检查:首先也是最重要的是:GError 仅应用于报告可恢复的运行时错误,绝不用于报告编程错误。

    当然,文档可以改进。请随时提交错误报告。

评论已关闭。