これは、なにをしてくて書いたもの?
Fluent Bitで、複数行のログ(Multiline)を読み込んでみることを、試してみようかなと。
Multiline
Fluent Bitで複数行のログを読み込むためには、tail inputプラグインの設定を調整します。
Tail - Fluent Bit: Official Manual
設定は、こちらに記載があります。
Multiline Configuration Parameters
MultilineをOnにして、Parserを設定することで利用できるようです。
設定はちょっと違いますが、複数行のファイルを読むということについては、Fluentdのドキュメントの方が視覚的には
わかりやすいかもしれません。
multiline - Fluentd
設定できる項目も、かなり似ていますし。複数行のログの例は、こちら。
Example
たとえば、Javaアプリケーションログのスタックトレースを、ひとつのログレコードとして扱いたい、みたいな時に
使うわけですね。
2013-3-03 14:27:33 [main] INFO Main - Start
2013-3-03 14:27:33 [main] ERROR Main - Exception
javax.management.RuntimeErrorException: null
at Main.main(Main.java:16) ~[bin/:na]
2013-3-03 14:27:33 [main] INFO Main - End
今回扱うお題も、Javaアプリケーションのログでいきましょう。
環境
今回の環境は、こちらです。
$ uname -srvmpio
Linux 4.15.0-99-generic #100-Ubuntu SMP Wed Apr 22 20:32:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.4 LTS
Release: 18.04
Codename: bionic
$ /opt/td-agent-bit/bin/td-agent-bit --version
Fluent Bit v1.4.3
td-agent-bitをsystemd上で有効化、起動。この時点では、デフォルト設定のままです。
$ sudo systemctl enable td-agent-bit
$ sudo systemctl start td-agent-bit
サンプルアプリケーションを用意する
まずは、ログを読み込む対象となるサンプルアプリケーションを用意します。Spring Bootで、簡単に作成。
JavaとMavenのバージョン。
$ java --version
openjdk 11.0.7 2020-04-14
OpenJDK Runtime Environment (build 11.0.7+10-post-Ubuntu-2ubuntu218.04)
OpenJDK 64-Bit Server VM (build 11.0.7+10-post-Ubuntu-2ubuntu218.04, mixed mode, sharing)
$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.7, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-99-generic", arch: "amd64", family: "unix"
pom.xml
xml version="1.0" encoding="UTF-8"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlnsxsi="http://www.w3.org/2001/XMLSchema-instance"
xsischemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.littlewings</groupId>
<artifactId>simple-logging-web-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<projectbuildsourceEncoding>UTF-8</projectbuildsourceEncoding>
<projectreportingoutputEncoding>UTF-8</projectreportingoutputEncoding>
<mavencompilersource>11</mavencompilersource>
<mavencompilertarget>11</mavencompilertarget>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.7.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
ログを出力するだけの、REST API。
src/main/java/org/littlewings/spring/example/App.java
package org.littlewings.spring.example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@RequestMapping("app")
public class App {
Logger logger = LoggerFactory.getLogger(App.class);
public static void main(String... args) {
SpringApplication.run(App.class, args);
}
@GetMapping("info")
public String info() {
logger.info("{}, info logging", "hello");
return "info logging";
}
@GetMapping("warn")
public String warn() {
logger.warn("{}, warn logging", "world");
return "warn logging";
}
@GetMapping("error")
public String error() {
logger.error("{}, error logging", "oops!!");
return "error logging";
}
@GetMapping("exception")
public String exception() {
Exception e = new RuntimeException("Oops!!");
logger.error("exception occurred, {}", "why?", e);
return "exception logging";
}
}
パッケージングして
$ mvn package
起動。Spring Bootで作っているので、これで「/tmp/spring.log」にログファイルが出力されます。
$ java -Dlogging.file.path=/tmp -jar target/simple-logging-web-app.jar
動作確認。
$ curl localhost:8080/app/info
info logging
$ curl localhost:8080/app/error
error logging
$ curl localhost:8080/app/exception
exception logging
ログを見てみます。
2020-05-09 02:59:52.579 INFO 2874 --- [http-nio-8080-exec-1] org.littlewings.spring.example.App : hello, info logging
2020-05-09 02:59:56.475 ERROR 2874 --- [http-nio-8080-exec-2] org.littlewings.spring.example.App : oops!!, error logging
2020-05-09 03:00:02.809 ERROR 2874 --- [http-nio-8080-exec-4] org.littlewings.spring.example.App : exception occurred, why?
java.lang.RuntimeException: Oops!!
at org.littlewings.spring.example.App.exception(App.java:44) ~[classes!/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
これで、Fluent Bitに読み込ませるログの準備は完了です。
Fluent BitでMultilineの設定を行う
では、こちらをパースできるようにFluent Bitを設定していきましょう。
Multiline Configuration Parameters
ドキュメントを確認すると、設定項目は以下の4つです。
- Multiline … Onにすると、複数行ログを扱えるようになる(デフォルトOff)
- Multiline_Flush … キューに入れられた複数行メッセージを処理するまでの待機時間を秒で指定(デフォルト4秒)
- Parser_Firstline … 複数行メッセージの、最初の先頭にマッチさせる、Parserの"名前"を指定する
- Parser_N … 複数行メッセージを解析するための、追加のパーサー。Nの部分には連番が入る(Parser_1、Parser_2、など)
まずはMultilineをOnにするとして、複数行を読めるようにするということは、継続するログがどこまでがひとつのログレコードかを
判定できないといけないわけで。
この点に関して、Parser_FirstlineとParser_Nを使います。
Parser_Firstlineでまず複数行の開始となる行を認識してもらい、継続するログをさらにパースする場合はParser_Nを指定していきます。
Fluentdでは、Parser_FirstlineおよびParser_Nに対応する設定項目はformat_firstline、formatNとなっており、どのように設定するかは
Fluentdのドキュメントを見ると雰囲気がつかめるでしょう。
Example
あまり細かくパースしないのであれば、Parser_Firstlineだけでもいいような気がします。
また、Parser_Firstlineにマッチしない行がくれば次のログレコードとして認識されるわけですが、イベント自体が来ない場合は
行の継続がわからずFluent Bitが待ち続けてしまいます。
その待ち時間を、どのくらいにするのかがMultiline_Flush(デフォルト4秒)です。
で、Fluent Bitに対して、input/outputに設定したのがこちら。
/etc/td-agent-bit/td-agent-bit.conf
[INPUT]
Name tail
Tag app.log
Path /tmp/spring.log
Multiline On
Parser_Firstline multiline_app_log
[OUTPUT]
Name stdout
Match *
Format json_lines
MultilineをOnにしてParser_Firstlineを指定し、出力はjson_linesで標準出力に向けることにしました。
今回Parser_Firstlineに指定している「multiline_app_log」というのはParserの名前なので、Parserの定義が必要です。
parsers.confに、以下のようなParser定義を加えました。
/etc/td-agent-bit/parsers.conf
[PARSER]
Name multiline_app_log
Format regex
Regex ^(?<log>(?<timestamp>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)
Time_Key timestamp
Time_Format %Y-%m-%d %H:%M:%S.%L
Time_Keep Off
今回は行をまるまる「log」フィールドとして残しつつ、Time_Keyとしてtimestampを認識させdropすることにしました。
Fluent Bitを再起動して
$ sudo systemctl restart td-agent-bit
アプリケーションにリクエストを送信。
$ curl localhost:8080/app/info
$ curl localhost:8080/app/error
$ curl localhost:8080/app/exception
$ curl localhost:8080/app/warn
Fluent Bitのログを確認します。
$ sudo journalctl -u td-agent-bit -f
結果がこちら。
May 09 10:44:31 ubuntu1804.localdomain td-agent-bit[4257]: {"date":1588995866.779000,"log":"2020-05-09 03:44:26.779 INFO 2874 --- [http-nio-8080-exec-8] org.littlewings.spring.example.App : hello, info logging"}
May 09 10:44:31 ubuntu1804.localdomain td-agent-bit[4257]: {"date":1588995868.790000,"log":"2020-05-09 03:44:28.790 ERROR 2874 --- [http-nio-8080-exec-10] org.littlewings.spring.example.App : oops!!, error logging"}
May 09 10:44:36 ubuntu1804.localdomain td-agent-bit[4257]: {"date":1588995870.845000,"log":"2020-05-09 03:44:30.845 ERROR 2874 --- [http-nio-8080-exec-1] org.littlewings.spring.example.App : exception occurred, why?\njava.lang.RuntimeException: Oops!!\n\tat org.littlewings.spring.example.App.exception(App.java:44) ~[classes!/:na]\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.34.jar!/:9.0.34]\n\tat java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]"}
May 09 10:44:41 ubuntu1804.localdomain td-agent-bit[4257]: {"date":1588995872.691000,"log":"2020-05-09 03:44:32.691 WARN 2874 --- [http-nio-8080-exec-2] org.littlewings.spring.example.App : world, warn logging"}
スタックトレース込みのログも、ひとつのレコードとして扱われていますね。
ところで、Time_KeepをOnにすると
[PARSER]
Name multiline_app_log
Format regex
Regex ^(?<log>(?<timestamp>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)
Time_Key timestamp
Time_Format %Y-%m-%d %H:%M:%S.%L
Time_Keep On
こんな感じで、「timestamp」フィールドが残ることになります。
May 09 10:45:51 ubuntu1804.localdomain td-agent-bit[4308]: {"date":1588995942.008000,"log":"2020-05-09 03:45:42.008 INFO 2874 --- [http-nio-8080-exec-5] org.littlewings.spring.example.App : hello, info logging","timestamp":"2020-05-09 03:45:42.008"}
簡単にですが、これでMultilineの確認ができました。