CLOVER🍀

That was when it all began.

makeを孊んでみる

これは、なにをしたくお曞いたもの

Goでのビルドツヌルは、makeを䜿うこずが倚いず聞いたので。

いく぀か、Goで曞かれた有名なOSSを芋おみるず、確かにMakefileが眮かれおいたす。

https://github.com/prometheus/prometheus/blob/v2.24.1/Makefile

https://github.com/hashicorp/terraform/blob/v0.14.5/Makefile

https://github.com/moby/moby/blob/v20.10.2/Makefile

https://github.com/kubernetes/kubernetes/blob/v1.20.2/build/root/Makefile

Goの勉匷を始めおいるこずですし、これを機に少し芚えおみようかな、ず。

make自䜓は䜿ったこずがありたす。ただ、すでに甚意されおいるMakefileず手順に埓い、make、make installずかを
実行しおきただけですね。

環境

今回の環境は、こちらです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:    20.04
Codename:   focal


$ uname -srvmpio
Linux 5.4.0-65-generic #73-Ubuntu SMP Mon Jan 18 17:25:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

make自䜓は、むンストヌル枈みです。

$ make --version
GNU Make 4.2.1
このプログラムは x86_64-pc-linux-gnu 甚にビルドされたした
Copyright (C) 1988-2016 Free Software Foundation, Inc.
ラむセンス GPLv3+: GNU GPL バヌゞョン 3 以降 <http://gnu.org/licenses/gpl.html>
これはフリヌ゜フトりェアです: 自由に倉曎および配垃できたす.
法埋の蚱す限り、 無保蚌 です.

make

makeに぀いお芋おいくずいうこずで、たずは䞀次リ゜ヌスをあたりたしょう。

GNUプロゞェクトを芋に行きたす。

Software - GNU Project - Free Software Foundation

こちらにmakeの説明がありたす。ちょっず抂芁を芋おみたしょう。正確には、GNU Makeですね。

Make - GNU Project - Free Software Foundation

makeは、プログラムの゜ヌスファむルから、実行可胜ファむルやその他の非゜ヌスファむルの生成をコントロヌルするツヌルです。
プログラムをビルドするための方法は、makefileず呌ばれるファむルに蚘述したす。

makeの機胜は、以䞋になりたす。

  • パッケヌゞのビルド方法やむンストヌル方法がmakefileに蚘述されおいるため、ナヌザヌは詳现を知らなくおもこれらのタスクを実行できる
  • makeはファむルの曎新を把握し、前回のビルドから倉曎があったファむルに基づいお凊理を行い、必芁なファむルを曎新する
  • 蚀語非䟝存で、makefile内で指定されたシェルコマンドを実行する
  • makeはパッケヌゞの䜜成以倖にも、むンストヌルやアンむンストヌルなど、様々な凊理を行わせるこずができる

makefileには、゜ヌスコヌドからビルド結果を生成するための、䞀連のルヌルを曞きたす。この時、ルヌルには䟝存関係を持たせる
こずができたす。

以䞋は、簡単なルヌルの構文です。

target:   dependencies ...
          commands
          ...

makefile内にはルヌルtargetを曞き、makeでどのルヌルを実行するかを指定するこずができたす。

 ずいうのが、基本的な話のようですね。

makeに぀いおの詳现は、マニュアルを芋おいくこずになりたす。

GNU Make Manual - GNU Project - Free Software Foundation

https://www.gnu.org/software/make/manual/make.html

では、これらを芋ながら簡単に詊しおいっおみたしょう。

How to Read This Manual

If you are new to make, or are looking for a general introduction, read the first few sections of each chapter, skipping the later sections.

はい。

The exception is the second chapter, An Introduction to Makefiles, all of which is introductory.

぀たり、初めおの人は2章を読みなさいずいうこずのようです。

An Introduction to Makefiles

はじめおのMakefile

ずいうわけで、こちらを芋ながら始めおいきたす。

An Introduction to Makefiles

単玔なmakefileは、以䞋のようなシンプルなルヌルで構成されたす。

target 
 : prerequisites 

        recipe
        

        


What a Rule Looks Like

ここで、タヌゲットtargetには実行可胜ファむルの名前やオブゞェクトファむル、アクションの名前を指定したす。
main.o、clean、install、buildなどですね。

前提条件prerequisitesずしお、別のタヌゲットを指定するこずができたす。これはタヌゲット間の䟝存関係を
衚したす。

レシピrecipeは、このタヌゲットで実際に行う凊理コマンドを蚘述したす。

泚意点ずしお、レシピの先頭にはタブを配眮する必芁がありたす。

実際に、makefileを曞いおみたしょう。

Makefile

step1: step2 step4
    echo 1
    echo one

step2: step3
    echo 2

step3:
    echo 3 \
    three

step4:
    echo 'four!!'

A Simple Makefile

makefileはMakefileずいう名前でmakeコマンドは認識したすが、-fオプションで別の名前を指定するこずもできたす。

step1はstep2、step4を前提にしおいお、step2はstep3を前提にしおいたす。step3には前提条件はありたせん。
step1には耇数のコマンドレシピを曞いおいたす。step3は、レシピを継続行にしおいたす。

この状態で、makeコマンドを実行しおみたす。

$ make
echo 3 \
three
3 three
echo 2
2
echo 'four!!'
four!!
echo 1
1
echo one
one

step3 → step2 → step4 → step1ずいう実行順になりたした。

なんずなく、䟝存関係を解決し぀぀実行しおいるのがわかるず思いたす。

makeコマンドの匕数ずしおタヌゲットを指定するこずで、各タヌゲット単䜍ず䟝存するタヌゲットごずにも実行できたす。

$ make step3
echo 3 \
three
3 three


$ make step4
echo 'four!!'
four!!


$ make step2
echo 3 \
three
3 three
echo 2
2

ずころで、単にmakeず指定した時は、step1のタヌゲットが実行されたように思いたす。

この理由は、以䞋に曞いおありたす。

By default, make starts with the first target (not targets whose names start with ‘.’). This is called the default goal.

How make Processes a Makefile

぀たり、最初のタヌゲットが実行されるようです。これをデフォルトゎヌルず呌びたす、ず。

これをオヌバヌラむドする方法ずしお、コマンドの匕数で指定する方法先ほど䜿った方法ず.DEFAULT_GOALずいう特殊倉数を
䜿う方法があるようです。

You can override this behavior using the command line (see Arguments to Specify the Goals) or with the .DEFAULT_GOAL special variable (see Other Special Variables).

詊しに、.DEFAULT_GOALを指定しおみたしょう。

Makefile

.DEFAULT_GOAL := step4

step1: step2 step4
    echo 1
    echo one

step2: step3
    echo 2

step3:
    echo 3 \
    three

step4:
    echo 'four!!'

確認。

$ make
echo 'four!!'
four!!

明瀺的にタヌゲットを指定しない堎合に、実行されるタヌゲットが倉わりたしたね。

ビルドで䜿うようなmakefileの䟋を芋おみるず、タヌゲットの名前はファむル名になっおいたりしたす。

A Simple Makefile

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

makefileは、このファむル名をタヌゲットにするこずで䟝存関係を衚珟したす。

今回䜜成したstep1のようなタヌゲットは、ファむルではありたせん。䞊蚘の䟋だずcleanがそうですね。

このようなファむルではないタヌゲットをPhonyタヌゲットず呌ぶそうです。

Phony Targets

Phonyタヌゲットは、ファむル名ではないタヌゲットのこずです。これを䜿う理由は、同じ名前のファむルずの競合を回避するためず、
パフォヌマンスの向䞊のためです。

レシピがタヌゲットずなるファむルを䜜成しない堎合、タヌゲットが再䜜成される床にレシピが実行されるようです。
たた、タヌゲットず同じ名前のファむルがあるずうたく機胜しなくなるようです。

ずするず、今回のmakefileはすべおPhonyタヌゲットなので、以䞋のようにするのが正解でしょうね。

Makefile

# .DEFAULT_GOAL := step4

.PHONY: step1
step1: step2 step4
    echo 1
    echo one

.PHONY: step2
step2: step3
    echo 2

.PHONY: step3
step3:
    echo 3 \
    three

.PHONY: step4
step4:
    echo 'four!!'

倉数を䜿う

makefile内では、倉数を䜿うこずもできたす。

Variables Make Makefiles Simpler

こんな感じで、=で定矩したす。参照は$(倉数)です。

Makefile

ECHO_COMMAND = echo
MESSAGE = 'Hello World'

.PHONY: hello
hello:
    $(ECHO_COMMAND) $(MESSAGE)

確認。

$ make
echo 'Hello World'
Hello World

ちなみに、この倉数は倖郚から䞊曞きするこずもできたす。

$ make MESSAGE=hello
echo hello
hello

これは、makeコマンド自身に䞊曞きしおもらっおいたす。

環境倉数で䞊曞きする堎合は、単玔に指定しただけではダメです。

$ MESSAGE=hello make
echo 'Hello World'
Hello World

-eオプションを付ける必芁がありたす。

$ MESSAGE=hello make -e
echo hello
hello

Variables from the Environment

倉数に぀いおは、ドキュメントのこのあたりを芋たしょう。

How to Use Variables

たた、自動で定矩される倉数もありたす。

Automatic Variables

Goプロゞェクトでmakefileを曞いおみる

最埌にGoプロゞェクトで簡単に詊しおみたしょう。

Goのバヌゞョン。

$ go version
go version go1.15.7 linux/amd64

プロゞェクト䜜成。

$ go mod init app
go: creating new go.mod: module app

go.mod

module app

go 1.15

゜ヌスコヌドを䜜成。

main.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println(GetMessage())
}

func GetMessage() string {
    return "Hello World!!"
}

テストコヌドも䜜成。

main_test.go

package main

import (
    "testing"
)

func TestGetMessage(t *testing.T) {
    if GetMessage() != "Hello World!!" {
        t.Error("test failed!")
    }
}

makefileも曞きたす。

Makefile

EXECUTABLE_FILE = app

.PHONY: build
build: format lint test
    go build

.PHONY: run
run: format
    go run main.go

.PHONY: format
format:
    gofmt -w -d .

.PHONY: lint
lint:
    go vet ./...

.PHONY: test
test:
    go test ./...

.PHONY: clean-testcache
clean-testcache:
    go clean -testcache

.PHONY: clean
clean:
    rm $(EXECUTABLE_FILE)

デフォルトでは、buildタヌゲットを実行したす。タヌゲットの䟝存関係から、フォヌマットしおgo vetをかけお、テストを実行しお
実行可胜ファむルの生成たで行いたす。

$ make
gofmt -w -d .
go vet ./...
go test ./...
ok      app 0.002s
go build

できあがったファむルの実行。

$ ./app 
Hello World!!

clean。こちらは単発のタヌゲットです。

$ make clean
rm app

run。フォヌマットずプログラムの実行が含たれたす。

$ make run
gofmt -w -d .
go run main.go
Hello World!!

䟝存するタヌゲットを順次実行しおいる途䞭で、あるタヌゲットが倱敗するずそこで停止したす。たずえば、今回のデフォルトタヌゲットを
実行しお、go testが倱敗した堎合はこんな感じになりたす。

$ make
gofmt -w -d .
go vet ./...
go test ./...
--- FAIL: TestGetMessage (0.00s)
    main_test.go:9: test failed!
FAIL
FAIL    app 0.002s
FAIL
make: *** [Makefile:21: test] ゚ラヌ 1

たた、-nを付けるず実際には凊理を行わずにどのようなタヌゲットが実行されるかを衚瀺しおくれたす。

$ make -n
gofmt -w -d .
go vet ./...
go test ./...
go build


$ make -n test
go test ./...


$ make -n run
gofmt -w -d .
go run main.go

なんずなく、雰囲気はわかった気がしたす。

あずはOSSで䜿われおいるmakefileを眺めたりしお、芚えおいきたしょう。

Fluentdで耇数行のログを読む

これは、なにをしたくお曞いたもの

以前、Fluent Bitで耇数行Multilineのログファむルを読む゚ントリを曞きたした。

Fluent Bitで、複数行のログファイルを読む - CLOVER🍀

今回は、これをFluentdで行っおみたす。

FluentdずMultiline

Fluentdを䜿っお耇数行のログを読むには、2぀の方法があるようです。

Fluentd単䜓だず、Parser Pluginのmultilineを䜿いたす。

multiline - Fluentd

サヌドパヌティ補のプラグむンを䜿う堎合は、fluentd-plugin-concatを䜿いたす。

GitHub - fluent-plugins-nursery/fluent-plugin-concat: Fluentd Filter plugin to concatenate multiline log separated in multiple events.

今回、これらを䜿っお耇数行のログを読んでみたいず思いたす。

ログを出力するサンプルアプリケヌションは、Fluent Bitの時ず同じものSpring Bootのバヌゞョンだけ䞊げたすを䜿いたす。

Fluent Bitで、複数行のログファイルを読む - CLOVER🍀

以䞋、それぞれの方法の抂芁を。

Parser Plugin / multiline

たずは、multiline Parser Pluginを䜿う方法から。

multiline - Fluentd

他のParser Pluginず異なり、Tail Pluginず組み合わせお䜿うこずが想定されおいたす。

Unlike other parser plugins, this plugin needs special code in input plugin e.g. handle format_firstline. So, currently, in_tail plugin works with multiline but other input plugins do not work with it.

パラメヌタヌは2皮類で、format_firstlineずformatNNは数字です。

少なくずも、format1は必須になりたす。1以降のformatNは、耇数行で構成された各ログをより现かくパタヌンマッチさせる時に
䜿いたす。

Railsのログの䟋が、その掻甚䟋になっおいたす。

multiline / Rails Log

format_firstlineは、耇数行の開始を衚す正芏衚珟パタヌンを指定したす。デフォルトはnilで、指定した堎合はこのパタヌンに
マッチしない行をバッファに溜めおいくようになりたす。

再床format_firstlineにマッチする行が来たら、その盎前たでがひず぀のログレコヌドずしお扱われるこずになりたす、ず。

Javaのスタックトレヌスの䟋が、format_firstlineずformat1だけを指定しおひず぀にたずめる䟋ずしおはわかりやすいですね。
これがドキュメントに蚘茉されおいたす。

multiline / Java Stacktrace Log

゜ヌスコヌドで芋るず、確かにTail Pluginがmultilineを完党意識した構成になっおいたすね。

https://github.com/fluent/fluentd/blob/v1.11.2/lib/fluent/plugin/in_tail.rb#L519-L556

たた、Multiline Pluginのパヌス凊理そのものにはformat_firstlineの方は出おこないようです。format_firstlineを意識しおいるのは、
あくたでTail Pluginのようですね。

https://github.com/fluent/fluentd/blob/v1.11.2/lib/fluent/plugin/parser_multiline.rb#L76-L100

぀たり、formatNのみを䜿った堎合は耇数行ログの行数が想定できるこずになりたす、ず。

fluent-plugin-concat

続いお、fluent-plugin-concatです。こちらはFilter Pluginずしお䜿う、耇数のむベントに分割されたログを結合するためのものです。

GitHub - fluent-plugins-nursery/fluent-plugin-concat: Fluentd Filter plugin to concatenate multiline log separated in multiple events.

耇数行の指定方法はいく぀かあり、n_lines、multiline_start_regexp、multiline_end_regexp、continuous_line_regexpが
ありたす。n_linesず他の蚭定には、排他の関係がありたす。

それぞれ意味はわかりそうですが、n_linesが耇数行がどれだけ続くか、他は耇数行の開始、終了、継続を衚す正芏衚珟を
指定するずいった感じですね。

䜿い方も曞かれおいたすが、特にDockerずKubernetesがずおもしっかり曞かれおいたす。これらでよく䜿われるんでしょうね 。

Usage

このFilter Pluginが耇数のタグにマッチする堎合、ログが混ざったりするのではず思ったのですが、デフォルトでタグごずに
ストリヌムの管理が分かれおいるようです。

https://github.com/fluent-plugins-nursery/fluent-plugin-concat/blob/v2.4.0/lib/fluent/plugin/filter_concat.rb#L177-L188

さらに现かくストリヌムの単䜍を指定したい堎合は、stream_identity_keyでレコヌド内でどのキヌをストリヌムの単䜍ずしお
扱うかを指定したす。

説明はこれくらいにしお、実際に䜿っおみたしょう。

環境

今回の環境は、こちらです。

$ td-agent --version
td-agent 1.11.2


$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:    20.04
Codename:   focal


$ uname -srvmpio
Linux 5.4.0-65-generic #73-Ubuntu SMP Mon Jan 18 17:25:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

サンプルアプリケヌション

たずは、ログを読み蟌む察象ずなるサンプルアプリケヌションを甚意したす。Spring Bootで、簡単に䜜成。

JavaずMavenのバヌゞョン。

$ java --version
openjdk 11.0.9.1 2020-11-04
OpenJDK Runtime Environment (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.9.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-64-generic", arch: "amd64", family: "unix"

`pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="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>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.4.2</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.4.2</version>
                <executions>
                  <execution>
                    <goals>
                      <goal>repackage</goal>
                    </goals>
                  </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

ログを出力するだけの、@RestController。

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

起動。これで、/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

ログはこんな感じに出力されたす。

2021-01-27 14:28:14.969  INFO 1263 --- [http-nio-8080-exec-1] org.littlewings.spring.example.App       : hello, info logging
2021-01-27 14:28:16.503 ERROR 1263 --- [http-nio-8080-exec-3] org.littlewings.spring.example.App       : oops!!, error logging
2021-01-27 14:28:17.895 ERROR 1263 --- [http-nio-8080-exec-2] 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:197) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.41.jar!/:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.3.jar!/:5.3.3]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.41.jar!/:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    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.41.jar!/:9.0.41]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

これで、準備完了です。では、Fluentdの蚭定を行っおいきたしょう。

Parser Plugin / multilineで耇数行のログファむルを読む

たずは、multilineを䜿うパタヌンです。

multiline - Fluentd

蚭定は、こんな感じで行いたした。

/etc/td-agent/td-agent.conf

<source>
  @type tail
  @id app_log_tail

  <parse>
    @type multiline
    format_firstline /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}/
    format1 /^(?<log>(?<time>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)/
  </parse>

  path /tmp/spring.log
  pos_file /var/log/td-agent/app.log.pos
  tag app.log
</source>

<match app.**>
  @type stdout
  @id output_stdout
</match>

Tail Pluginで、ログファむルを読み続けたす。この時に、Parser Pluginずしおmultilineを指定したす。

  <parse>
    @type multiline
    format_firstline /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}/
    format1 /^(?<log>(?<time>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)/
  </parse>

アプリケヌションログの日時の郚分を開始行ずしお定矩しおおきたす。

たた、timeをパタヌンずしお指定しおいたすが、time_keyのデフォルトはtimeらしいので、こちらに合わせおおきたした。

Config: Parse Section / Parse Parameters

それ以䞊のパヌスは今回は行いたせん。

出力先は、Stdout Plugin ぀たり、Fluentd自身のログファむルにしたす。

<match app.**>
  @type stdout
  @id output_stdout
</match>

蚭定したら、Fluentdを再起動。

$ sudo systemctl restart td-agent

では、アプリケヌションにアクセスしおみたす。
※正確には、このあずもう1回アクセスしないず最埌のリク゚ストのログは曞き出されたせんが

$ curl localhost:8080/app/info
$ curl localhost:8080/app/error
$ curl localhost:8080/app/exception
$ curl localhost:8080/app/warn

Fluentdのログを芋たす。

/var/log/td-agent/td-agent.log

2021-01-27 14:39:43.891000000 +0000 app.log: {"log":"2021-01-27 14:39:43.891  INFO 1263 --- [http-nio-8080-exec-5] org.littlewings.spring.example.App       : hello, info logging"}
2021-01-27 14:39:46.925000000 +0000 app.log: {"log":"2021-01-27 14:39:46.925 ERROR 1263 --- [http-nio-8080-exec-6] org.littlewings.spring.example.App       : oops!!, error logging"}
2021-01-27 14:40:14.398000000 +0000 app.log: {"log":"2021-01-27 14:40:14.398 ERROR 1263 --- [http-nio-8080-exec-7] org.littlewings.spring.example.App       : exception occurred, why?\n\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:197) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.41.jar!/:4.0.FR]\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.41.jar!/:4.0.FR]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\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.41.jar!/:9.0.41]\n\tat java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]\n"}
2021-01-27 14:40:20.487000000 +0000 app.log: {"log":"2021-01-27 14:40:20.487  WARN 1263 --- [http-nio-8080-exec-8] org.littlewings.spring.example.App       : world, warn logging"}

スタックトレヌスもたずたった行になっおいるこずがわかりたすね。

ちなみに、time_keyで指定されおいるフィヌルド今回はデフォルトのtimeを残すには、keep_time_keyをtrueに
蚭定したす。

  <parse>
    @type multiline
    format_firstline /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}/
    format1 /^(?<log>(?<time>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)/

    keep_time_key true
  </parse>

確認。

2021-01-27 14:45:26.306000000 +0000 app.log: {"log":"2021-01-27 14:45:26.306  INFO 1263 --- [http-nio-8080-exec-10] org.littlewings.spring.example.App       : hello, info logging","time":"2021-01-27 14:45:26.306"}

fluent-plugin-concat

次は、fluent-plugin-concatを䜿いたす。

GitHub - fluent-plugins-nursery/fluent-plugin-concat: Fluentd Filter plugin to concatenate multiline log separated in multiple events.

こちらはサヌドパヌティ補のプラグむンになるので、むンストヌルが必芁です。こちらのドキュメントに蚘茉されおいる
プラグむンのむンストヌルコマンドを確認したら

Plugin Management - Fluentd

むンストヌルしたす。

$ sudo td-agent-gem install fluent-plugin-concat
Fetching fluent-plugin-concat-2.4.0.gem
Successfully installed fluent-plugin-concat-2.4.0
Parsing documentation for fluent-plugin-concat-2.4.0
Installing ri documentation for fluent-plugin-concat-2.4.0
Done installing documentation for fluent-plugin-concat after 0 seconds
1 gem installed

今回は2.4.0がむンストヌルされたした。

むンストヌルされた堎所は、こちら。

$ ll /opt/td-agent/lib/ruby/gems/2.7.0/gems/fluent-plugin-concat-2.4.0
total 56
drwxr-xr-x   4 root root 4096 Jan 25 15:13 ./
drwxr-xr-x 131 root root 4096 Jan 25 15:13 ../
-rw-r--r--   1 root root    0 Jan 25 15:13 Appraisals
-rw-r--r--   1 root root 1047 Jan 25 15:13 fluent-plugin-concat.gemspec
-rw-r--r--   1 root root  105 Jan 25 15:13 Gemfile
drwxr-xr-x   2 root root 4096 Jan 25 15:13 .github/
-rw-r--r--   1 root root   53 Jan 25 15:13 .gitignore
drwxr-xr-x   3 root root 4096 Jan 25 15:13 lib/
-rw-r--r--   1 root root 1072 Jan 25 15:13 LICENSE.txt
-rw-r--r--   1 root root  762 Jan 25 15:13 NEWS.md
-rw-r--r--   1 root root  219 Jan 25 15:13 Rakefile
-rw-r--r--   1 root root 4718 Jan 25 15:13 README.md
-rw-r--r--   1 root root 1153 Jan 25 15:13 .rubocop.yml
-rw-r--r--   1 root root  970 Jan 25 15:13 .travis.yml

では、蚭定したす。

/etc/td-agent/td-agent.conf

<source>
  @type tail
  @id app_log_tail

  <parse>
    @type regexp
    expression /(?<log>.*)/
  </parse>


  path /tmp/spring.log
  pos_file /var/log/td-agent/app.log.pos
  tag app.log
</source>

<filter app.**>
  @type concat
  key log
  multiline_start_regexp /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}/
</filter>

<filter app.**>
  @type parser
  key_name log

  <parse>
    @type regexp
    expression /^(?<log>(?<time>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)/m

    keep_time_key true
  </parse>
</filter>


<match app.**>
  @type stdout
  @id output_stdout
</match>

耇数行のログファむルはfluent-plugin-concatで結合させるので、Tail Pluginはなにも考えずに1行ず぀ログレコヌドずしお
読み取るように蚭定したす。

<source>
  @type tail
  @id app_log_tail

  <parse>
    @type regexp
    expression /(?<log>.*)/
  </parse>


  path /tmp/spring.log
  pos_file /var/log/td-agent/app.log.pos
  tag app.log
</source>

Tail Pluginで読み取ったログは、fluent-plugin-concatで結合させたす。先ほどのmultilineの時ず同様に、日時のパタヌンを
ログの開始パタヌンずしお定矩したした。

<filter app.**>
  @type concat
  key log
  multiline_start_regexp /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}/
</filter>

あずはオマケで、time_keytimeフィヌルドを抜き出すために、regex Parser Pluginを付けおいたす。
今回は最初からkeep_time_keyはtrueにしおいたす。

<filter app.**>
  @type parser
  key_name log

  <parse>
    @type regexp
    expression /^(?<log>(?<time>\d{4}-\d\d-\d\d \d\d:\d\d:\d\d.\d{3}).+)/m

    keep_time_key true
  </parse>
</filter>

蚭定したら、Fluentdを再起動しお

$ sudo systemctl restart td-agent

アプリケヌションにアクセスしおみたす。
※やっぱり、このあずにもう1回リク゚ストを぀けおいたす

$ curl localhost:8080/app/info
$ curl localhost:8080/app/error
$ curl localhost:8080/app/exception
$ curl localhost:8080/app/warn

Fluentdのログを確認。

/var/log/td-agent/td-agent.log

2021-01-27 14:59:28.464000000 +0000 app.log: {"log":"2021-01-27 14:59:28.464  INFO 1263 --- [http-nio-8080-exec-4] org.littlewings.spring.example.App       : hello, info logging","time":"2021-01-27 14:59:28.464"}
2021-01-27 14:59:30.036000000 +0000 app.log: {"log":"2021-01-27 14:59:30.036 ERROR 1263 --- [http-nio-8080-exec-2] org.littlewings.spring.example.App       : oops!!, error logging","time":"2021-01-27 14:59:30.036"}
2021-01-27 14:59:34.503000000 +0000 app.log: {"log":"2021-01-27 14:59:34.503 ERROR 1263 --- [http-nio-8080-exec-6] org.littlewings.spring.example.App       : exception occurred, why?\n\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:197) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.41.jar!/:4.0.FR]\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.3.jar!/:5.3.3]\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.41.jar!/:4.0.FR]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.3.jar!/:5.3.3]\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]\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.41.jar!/:9.0.41]\n\tat java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]\n","time":"2021-01-27 14:59:34.503"}
2021-01-27 14:59:36.987000000 +0000 app.log: {"log":"2021-01-27 14:59:36.987  WARN 1263 --- [http-nio-8080-exec-7] org.littlewings.spring.example.App       : world, warn logging","time":"2021-01-27 14:59:36.987"}

スタックトレヌスを含んだログも結合されおいたすし、OKですね。

たずめ

Fluentdを䜿っお、耇数行のログを読む方法を2぀詊しおみたした。

multilineの蚭定っお、1回詊した埌に時間が空くずすぐ忘れおしたうのですが こうやっおメモしおおくのが良いのでしょう 。

きっず、たた思い出すために芋るこずになるでしょう。