从docker容器中结构化日志记录到日志

从docker容器中结构化日志记录到日志,docker,systemd,coreos,Docker,Systemd,Coreos,从docker容器中向journald写入结构化日志的最佳方法是什么 例如,我有一个应用程序,它使用 我没有更改应用程序,而是尝试通过 -v/var/log/systemd/journal:/var/log/systemd/journal 它可以在我的Ubuntu 16.04桌面上运行,但不能在运行应用程序的CoreOS实例上运行(使用Ubuntu 16.04基本映像)。我不太明白为什么。也许有更好的方式发送到日志 docker journald输出日志记录选项有哪些限制?似乎它支持的应用程序只

从docker容器中向journald写入结构化日志的最佳方法是什么

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

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

它可以在我的Ubuntu 16.04桌面上运行,但不能在运行应用程序的CoreOS实例上运行(使用Ubuntu 16.04基本映像)。我不太明白为什么。也许有更好的方式发送到日志

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

--

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

但另一个问题是,与启动docker容器的服务文件没有关联。手动添加单位: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;
}
#包括
#包括
#包括
int main(int argc,char*argv[]){
sd_journal_send(“MESSAGE=Hello World!”,
“消息_ID=52fb62f99e2c49d89cfbf9d6de5e3555”,
“优先级=5”,
“HOME=%s”,getenv(“HOME”),
“期限=%s”,getenv(“期限”),
“页面大小=%li”,sysconf(\u SC\u页面大小),
“N_CPU=%li”,sysconf(仅限处理器),
无效);
返回0;
}
这会写入日志并添加自定义字段,如HOME、TERM、PAGE_SIZE等。当我使用journalbeat将它们发送到ELK堆栈时,这些字段会很好地在elasticsearch中结束,我可以直接搜索它们

然而,docker似乎只是简单地获取了应用程序的stdout,并将其提供给journald,只添加了几个字段。e、 g.集装箱识别号

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

1) 我必须通过一些目录和设备文件才能让它用sd\u journal\u send来写

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

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

本质上,日志驱动程序在我的例子中是不够的

关于如何链接服务名称以便journalctl-u显示来自sd_journal_send的日志消息,有什么建议吗?因为那样可以解决问题

--
我找到了解决办法。如果其他人对我如何解决这个问题感兴趣,我会在下面给出答案。

最终的解决方案非常简单

我转而用纯json编写消息。因此journalctl-u现在可以工作并显示现在包含json数据的消息字段

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

在logstash.conf中,我添加了:

filter {
  json {
    source => "message"
  }
}
这样做的目的是在将json数据发送到elasticsearch之前,将其从消息字段扩展到顶层的单独字段中


有关logstash的json过滤器的详细信息,请参见

您需要安装
journald
侦听的套接字。对于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
(主机上)中得到一个条目


码头工人会做你想做的吗?除了日志消息外,它还存储其他元数据,如
容器ID
容器名称
容器标签
等。我可能没有说清楚。我需要添加我自己的字段,并将其绑定到同一个单元中。当docker容器应用程序从systemd服务文件启动时,发生的事情基本上是
journalctl-u
将其视为另一个单元。Docker是个问题。我必须深入了解源代码,看看docker中的sysyemd日志驱动程序支持什么。可能需要扩展它。这有帮助吗?@TarunLalwani-no。我如何在Ubuntu 16.04上编译它?我得到
vagrant@vagrant:~/journald$gcc test.c/tmp/ccLHPQPH.o:In function
main::test.c:(.text+0x8b):未定义对
sd\u journal\u send\u的引用,带\u location'collect2:error:ld返回了1个退出状态
Yes,但它在作为系统服务启动时会与单元文件失去关联。因此,journalbeat单元过滤器会删除这些消息。无论如何,我已经通过在logstash中使用json过滤器解决了这个问题,请参阅我的答案。
Aug 15 21:40:33 vagrant a.out[11263]: Hello World!