关于systemd:结构化日志记录从Docker容器中记录

Structured logging to journald from within a docker container

从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程序可以这样写入系统日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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服务名并查看消息,则不会看到那些日志消息,因为它们进入日志的路径不同,并且与运行他们的服务。

3)您可以使用Docker的日志记录驱动程序添加一些野性字段/标签,这些字段/标签是固定的,一次添加将出现在每条发送的消息中并且保持不变。它们不是像我从上面的C代码中想要的动态字段。

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

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

-
我找到了解决方案。如果其他人对我的解决方法感兴趣,我将在下面给出答案。


您需要安装journald侦听的套接字。 如果是ubuntu,则为/run/systemd/journal/socket。 将这个内部人映射到您的docker容器中,它将正常工作

用示例代码中的strace弄清楚了

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

并使用以下内容在ubuntu docker容器中进行测试

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

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

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


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

我转而将消息写为纯json。 因此journalctl -u现在可以工作并显示MESSAGE字段,其中现在包含json数据。

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

我添加到logstash.conf:

1
2
3
4
5
filter {
  json {
    source =>"message"
  }
}

它的作用是将json数据从message字段扩展到顶层的单独字段,然后再将其发送到elasticsearch。

有关logstash的json过滤器的详细信息可以在这里找到