【问题标题】:Structured logging to journald from within a docker container从 docker 容器中结构化日志记录到日志
【发布时间】:2023-03-23 17:10:01
【问题描述】:

从 docker 容器中将结构化日志写入日志的最佳方式是什么?

例如,我有一个使用sd_journal_send 编写的应用程序 我没有更改应用程序,而是尝试通过

-v /var/log/systemd/journal:/var/log/systemd/journal

它适用于我的 Ubuntu 16.04 桌面,但不适用于运行应用程序的 CoreOS 实例(使用 Ubuntu 16.04 基础映像)。我不太明白为什么。也许有更好的方式发送到期刊?

docker journald 输出日志记录选项有哪些限制?它似乎不支持应用程序编写的不仅仅是消息字段。

--

所以我发现我需要-v /dev/log:/dev/log

但是还有一个问题就是没有关联到启动docker容器的服务文件。手动添加 UNIT: servicename.service 没有解决它。因此,在查看和发送服务日志时,它与 exe 相关联,但与容器或服务无关。谁遇到了这些问题,您是如何解决的?

-- 好的,让我稍微扩展一下。

C 程序可以这样写入 systemd 日志:

#include <systemd/sd-journal.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
        sd_journal_send("MESSAGE=Hello World!",
                        "MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555",
                        "PRIORITY=5",
                        "HOME=%s", getenv("HOME"),
                        "TERM=%s", getenv("TERM"),
                        "PAGE_SIZE=%li", sysconf(_SC_PAGESIZE),
                        "N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN),
                        NULL);
        return 0;
}

这会写入日志并添加自定义字段,如 HOME、TERM、PAGE_SIZE 等。当我使用 journalbeat 将它们发送到 ELK 堆栈时,这些字段在 elasticsearch 中很好地结束,我可以直接搜索它们。

但是,docker 似乎只是简单地获取应用程序的标准输出,并将其提供给日志,只添加了几个字段。例如CONTAINER_ID。

在 docker 容器中使用这样的程序,然后从服务文件运行它们会产生一个小问题。

1) 我必须通过一些目录和设备文件才能使用 sd_journal_send 写入。

2) 如果您从 systemd .service 文件启动容器并希望使用 journalctl -u servicename 并查看消息,则看不到这些日志消息,因为它们以不同的路径进入日志并且没有得到与运行它们的服务相关联。

3) 您可以使用 docker 的 journald 日志记录驱动程序添加一些任意字段/标签,它们是固定的,一次添加会出现在每条发送的消息上并且不会改变。它们不是我想从上面的 C 代码中获得的动态字段。

从本质上讲,journald 日志驱动程序在我的情况下是不够的。

关于如何链接服务名称以便 journalctl -u 显示来自 sd_journal_send 的日志消息的任何建议?那样就可以解决问题了。

-- 我找到了解决方案。我会把答案放在下面,以防其他人对我的解决方法感兴趣。

【问题讨论】:

  • Docker journald logging driver 会做你想做的事吗?除了日志消息,它还存储其他元数据,如CONTAINER_IDCONTAINER_NAMECONTAINER_TAG 等。
  • 我可能没有说清楚。我需要添加自己的字段并将其全部绑定在同一个单元中。发生的事情基本上是journalctl -u 在从 systemd 服务文件启动 docker 容器应用程序时将其视为另一个单元。 Docker 是问题所在。我将不得不深入研究源代码并查看它支持的 docker 中的 sysyemd 日志记录驱动程序。可能需要扩展它。
  • @TarunLalwani - 不。
  • 如何在 Ubuntu 16.04 上编译它?我得到vagrant@vagrant:~/journald$ gcc test.c /tmp/ccLHPQPH.o: In function main': test.c:(.text+0x8b): undefined reference to sd_journal_send_with_location' collect2: error: ld returned 1 exit status

标签: docker systemd coreos


【解决方案1】:

最终的解决方案非常简单。

我改用纯 json 格式编写消息。所以 journalctl -u 现在可以工作并显示现在包含 json 数据的 MESSAGE 字段。

然后我使用 journalbeat 将其发送到 logstash。

我在 logstash.conf 中添加了:

filter {
  json {
    source => "message"
  }
}

它的作用是将消息字段中的 json 数据扩展为顶层的单独字段,然后将它们发送到 elasticsearch。

关于logstash的json过滤器的详细信息可以找到here

【讨论】:

    【解决方案2】:

    你需要挂载journald 监听的socket。对于 ubuntu,它是 /run/systemd/journal/socket。将此内部人员映射到 docker 容器中,它会正常工作

    在您的示例代码上使用 strace 解决了这个问题

    sendmsg(3, {msg_name(29)={sa_family=AF_LOCAL, sun_path="/run/systemd/journal/socket"}, 
    msg_iov(23)=[{"CODE_FILE=test.c", 16}, {"\n", 1}, {"CODE_LINE=13", 12}, {"\n", 1}, {"CODE_FUNC=main", 14}, {"\n", 1}, 
    {"MESSAGE=Hello World!", 20}, {"\n", 1}, {"MESSAGE_ID=52fb62f99e2c49d89cfbf"..., 43}, {"\n", 1}, {"PRIORITY=5", 10}, {"\n", 1}, 
    {"HOME=/home/vagrant", 18}, {"\n", 1}, {"TERM=xterm-256color", 19}, {"\n", 1}, {"PAGE_SIZE=4096", 14}, {"\n", 1}, 
    {"N_CPUS=1", 8}, {"\n", 1}, {"SYSLOG_IDENTIFIER=", 18}, {"a.out", 5}, {"\n", 1}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 208
    

    并在 ubuntu docker 容器中使用以下方法对其进行测试

    docker run -v /run/systemd/journal/socket:/run/systemd/journal/socket -v $PWD:/jd -it -w /jd ubuntu:16.04 ./a.out
    

    我在journalctl -f(主机上)中获得了一个条目

    Aug 15 21:40:33 vagrant a.out[11263]: Hello World!
    

    【讨论】:

    • 是的,但是当作为 systemd 服务启动时,它会失去与单元文件的关联。因此,journalbeat 单元过滤器会丢弃这些消息。无论如何,我已经通过在 logstash 中使用 json 过滤器解决了这个问题,请参阅我的答案。
    猜你喜欢
    • 1970-01-01
    • 2018-03-24
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 2022-09-29
    • 2017-02-04
    • 2017-10-02
    相关资源
    最近更新 更多