3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CDK で Infrastructure as Code する: Fluent Bit編2。設定ファイルを読んでみる

Posted at

さてさて前回、Fluent Bitでログを転送する仕組みを学びましたが、そのつづきです。
Fluent Bit を使ってできる事をもうすこし深掘りしてみます。

概要

前回はすでに設定済みのFluent Bitを動かしたので、Fluent Bitがなにをやってくれてるのか、ちょっとわかりにくかったかもしれません。なので今回は、設定するまえのデフォルト状態でFluent Bitを動かしてログを出力してみます。

そのあとに最終的に、設定済みのFluent Bit を内容を理解したいと思います。

準備

今回用にソースをアップしてあるので、それでやってみます。

$ git clone --branch fluentbit02  https://github.com/masatomix/spring-boot-sample-tomcat.git
$ cd spring-boot-sample-tomcat/
spring-boot-sample-tomcat $ mvn package

...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.745 s
[INFO] Finished at: 2025-08-24T13:47:53Z
[INFO] ------------------------------------------------------------------------
spring-boot-sample-tomcat $ 
spring-boot-sample-tomcat$ docker compose up --build
[+] Building 0.0s (0/0)
[+] Running 1/0
 ✔ Container spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  Created                               
Attaching to spring-boot-sample-tomcat-spring-boot-sample-tomcat-1
...
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |   .   ____          _            __ _ _
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |   '  |____| .__|_| |_|_| |_\__, | / / / /
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |  =========|_|==============|___/=/_/_/_/
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |  :: Spring Boot ::                (v3.1.2)
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  |
...
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  | {"@timestamp":"2025-08-24T13:54:59.814799039Z","@version":"1","message":"Tomcat started on port(s): 8080 (http) with context path ''","logger_name":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer","thread_name":"main","level":"INFO","level_value":20000}
spring-boot-sample-tomcat-spring-boot-sample-tomcat-1  | {"@timestamp":"2025-08-24T13:54:59.82463053Z","@version":"1","message":"Started SampleTomcatApplication in 3.178 seconds (process running for 3.648)","logger_name":"nu.mine.kino.SampleTomcatApplication","thread_name":"main","level":"INFO","level_value":20000}

起動しました1

つづいてFluent Bitも起動しますが、Fluent Bitをデフォルト状態で動かすために、

spring-boot-sample-tomcat$ cd fluentbit/test/

に移動して fluent-bit-test.confの下記の記載あたりをコメントアウトします。

# [FILTER]
#     Name     parser
#     Match    *-firelens-*
#     Key_Name log
#     Parser   json
#     # Preserve_Key ON
#     Reserve_Data ON
# 
# [FILTER]
#     Name          rewrite_tag
#     Match         *-firelens-*
#     # Rule          $level (ERROR|WARN) error-$container_id true
#     Rule          $level (ERROR) error-$container_id true

また前回同様、インプットファイルのパスも変更します。

参考: AWS CDK で Infrastructure as Code する: Fluent Bit編1。ローカルのDockerで疎通確認

では起動。

spring-boot-sample-tomcat/fluentbit/test$ docker compose up
[+] Building 0.0s (0/0)                                                                                                                                                                                         
[+] Running 1/0
 ✔ Container test-fluent-bit-1  Created                                                                                                                                                                    0.0s 
Attaching to test-fluent-bit-1
test-fluent-bit-1  | Fluent Bit v1.9.10
test-fluent-bit-1  | * Copyright (C) 2015-2022 The Fluent Bit Authors
test-fluent-bit-1  | * Fluent Bit is a CNCF sub-project under the umbrella of Fluentd
test-fluent-bit-1  | * https://fluentbit.io
test-fluent-bit-1  | 
...
test-fluent-bit-1  | [2025/08/31 05:29:01] [ info] [output:stdout:stdout.1] worker #0 started
test-fluent-bit-1  | [2025/08/31 05:29:01] [ info] [output:file:file.0] worker #0 started

起動したので、他のコンソールから curlで疎通します。

%  curl 'http://localhost:8080/echoLogger' -i
HTTP/1.1 200
vary: accept-encoding
Content-Type: text/plain;charset=UTF-8
Content-Length: 16
Date: Sun, 31 Aug 2025 05:30:13 GMT

Default Message.
%

Fluent Bitのコンソールには、下記のようなログが出力されたと思います。

test-fluent-bit-1  | [0] -firelens-: [1756618213.532375132, {"log"=>"{"@timestamp":"2025-08-31T05:30:13.532127529Z","@version":"1","message":"Initializing Spring DispatcherServlet

...割愛

test-fluent-bit-1  | [4] -firelens-: [1756618213.560922780, {"log"=>"{"@timestamp":"2025-08-31T05:30:13.560482786Z","@version":"1","message":"Default Message.","logger_name":"nu.mine.kino.web.EchoController","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000}
test-fluent-bit-1  | ", "stream"=>"stdout", "time"=>"2025-08-31T05:30:13.56092278Z"}]

なんだかごちゃっとしていますが、最終行のログだけちょっと整形してみると

test-fluent-bit-1  | [4] -firelens-: [1756618213.560922780, 
{
  "log"=>
    "{"@timestamp":"2025-08-31T05:30:13.560482786Z","@version":"1","message":"Default Message.","logger_name":"nu.mine.kino.web.EchoController","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000}", 
  "stream"=>"stdout",
  "time"=>"2025-08-31T05:30:13.56092278Z"
}
]

ログはJSONデータになっていて 「 logプロパティ」があるようです。したがってコメントアウトした設定が有効な場合は、上記のようなデータを受け取り、設定に従ってFluent Bit内で処理がうごく、ってことになりそうです。

さてコメントアウトした設定たちが有効だった場合はどうなるか、順番に見てみます。

parser の処理

ひとつめのFILTER設定、parserの記述は以下の通りでした。

[FILTER]
    Name     parser
    Match    *-firelens-*
    Key_Name log
    Parser   json
    # Preserve_Key ON
    Reserve_Data ON

この処理の内容はだいたい以下の通りです。

  • Fluent Bit に入ってきたログデータのうち、タグが *-firelens-* にマッチするものにこのフィルタが適用される。

    • *-firelens-*のタグはECSで動いている場合は、AWS上(Firelens上)で勝手に設定されます。とりあえずは全部のログと思ってもらってOKです。
  • Key_Name logで、そのログデータの log プロパティの値をJSONとしてパース

    • logプロパティの値は先ほど見たとおり、

      {
          "@timestamp": "2025-08-31T05:30:13.560482786Z",
          "@version": "1",
          "message": "Default Message.",
          "logger_name": "nu.mine.kino.web.EchoController",
          "thread_name": "http-nio-8080-exec-1",
          "level": "INFO",
          "level_value": 20000
      }
      

      こんなJSONデータでした。

  • JSON の中身が展開され、上記の各キーがそれぞれ新しいプロパティとして追加される。

    • logプロパティと同レベルに、streamtimeなどのプロパティもありましたが、そこにlogプロパティの中身のデータが同列に並ぶ感じです。
  • Reserve_Data ON となっているので、log 以外の元々のプロパティ(streamtime、ローカル環境だと存在しないがcontainer_id など)は削除しないで残す。

  • (今回はコメントアウトされているけど )、Preserve_Key ONを指定するとlogプロパティ自体も削除しないで残す(今回は削除しちゃう)。

したがって、前回の記事のログに戻るのですが、これらの設定を有効にしてログを出力した場合は、

test-fluent-bit-1  | [1] -firelens-: [1756635383.401169885, 
{
    "@timestamp"=>"2025-08-31T10:16:23.401058661Z",
    "@version"=>"1",
    "message"=>"Default Message.",
    "logger_name"=>"nu.mine.kino.web.EchoController",
    "thread_name"=>"http-nio-8080-exec-7",
    "level"=>"INFO",
    "level_value"=>20000,
    "stream"=>"stdout",
    "time"=>"2025-08-31T10:16:23.401169885Z"
}
]

こんな感じになることが分かります。

デフォルトのログに levelプロパティなどが追加されたので、その内容によって後続処理を変えたりできるようになりました!

参考: https://docs.fluentbit.io/manual/pipeline/filters/parser

rewrite_tag の処理

ふたつめのFILTER設定、rewrite_tagの記述はこうでした。

[FILTER]
    Name          rewrite_tag
    Match         *-firelens-*
    Rule          $level (ERROR) error-$container_id true

この処理の内容はだいたい以下の通りです。

  • parser されたデータに対して、*-firelens-*のタグのデータを処理する
    • 前処理で別のタグを付けたりしていないので、引き続き全データが対象
  • levelプロパティが「ERROR」のログデータに対して、rewrite_tagする(別のタグをつける)。
    • 別の新しいタグ名はerror-$container_id (今回、ローカルなのでerror-ってなる)
    • 最後のtrueで、元のレコードも残して新しいタグのデータをコピーする2

参考: https://docs.fluentbit.io/manual/data-pipeline/filters/rewrite-tag

あとで実際にやりますが http://localhost:8080/serverException にアクセスすると、Server上のJavaプログラムが、

log.error(exception.getMessage(), exception);

とやって、結果 levelプロパティが「ERROR」のログが出力されるように作ってあります。

そのERRORなログだけは別のタグを付けるということですね。今回、Fluent Bitの設定ファイルは出力先を

[OUTPUT]
    Name file
    Match error*
    Path /fluent-bit/etc/output_dir

[OUTPUT]
    Name   stdout
    Match  *

としてあるので、error- というタグをつけたログは

  • 上の設定により、上記のフォルダ上のあるファイル
  • 下の設定により、Fluent Bitのコンソール

に出力されるはずです。

エラーになるリクエストを投げてみる

さてさて、コメントアウトした内容が理解できたところで、再度コメントアウトを戻して起動します。

  • Fluent BitのほうのDockerを停止
  • fluent-bit-test.conf のコメントアウトを戻す
  • 再度、Dockerを起動

でOKです。起動ができたら、正常終了するリクエストや、さきほどのエラーが発生するAPIにリクエストを発行してみます。

$ curl http://localhost:8080/serverException -i
HTTP/1.1 503
vary: accept-encoding
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 10 Sep 2025 07:54:34 GMT
Connection: close

{"code":"SERVICE_UNAVAILABLE","message":"サーバ起因の例外が発生しました"}
$ 

エラーが発生しました。

まずFluent Bitのコンソール上は下記のログが出力されたとおもいます。

fluent-bit-1  | [0] -firelens-: [1757490929.038618827, 
{
 "@timestamp"=>"2025-09-10T07:55:29.03697765Z",
 "@version"=>"1",
 "message"=>"サーバ起因の例外が発生しました",
 "logger_name"=>"nu.mine.kino.advice.GlobalExceptionHandler",
 "thread_name"=>"http-nio-8080-exec-2",
 "level"=>"ERROR",
 "level_value"=>40000,
 "stack_trace"=>"nu.mine.kino.exceptions.ServerException: サーバ起因の例外が発生しました
fluent-bit-1  |         at nu.mine.kino.web.EchoController.serverExcetpion(EchoController.java:150)
fluent-bit-1  |         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
... 割愛
fluent-bit-1  | ",
 "stream"=>"stdout",
 "time"=>"2025-09-10T07:55:29.038618827Z"
}]

fluent-bit-1  | [0] error-: [1757490929.038618827, 
{"@timestamp"=>"2025-09-10T07:55:29.03697765Z",
 "@version"=>"1",
 "message"=>"サーバ起因の例外が発生しました",
 "logger_name"=>"nu.mine.kino.advice.GlobalExceptionHandler",
 "thread_name"=>"http-nio-8080-exec-2",
 "level"=>"ERROR",
 "level_value"=>40000,
 "stack_trace"=>"nu.mine.kino.exceptions.ServerException: サーバ起因の例外が発生しました
fluent-bit-1  |         at nu.mine.kino.web.EchoController.serverExcetpion(EchoController.java:150)
fluent-bit-1  |         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
... 割愛
fluent-bit-1  | ",
 "stream"=>"stdout",
 "time"=>"2025-09-10T07:55:29.038618827Z"
}]

同じログが2行出ていますが、元々あった-firelens-タグのログと、error-という新たなタグのログがどちらも出力されているからですね。先ほどの trueの設定が効いていそうです。

つづいて、 levelプロパティが「ERROR」のログだけが出力されるはずの場所も見てみます。

spring-boot-sample-tomcat/fluentbit/test$ 
spring-boot-sample-tomcat/fluentbit/test$ ls -lrt ./output_dir/
-rw-r--r-- 1 root root 17307 Sep 10 16:55 error-
spring-boot-sample-tomcat/fluentbit/test$ cat  ./output_dir/error- 
error-: [1757490929.038618827, {"@timestamp":"2025-09-10T07:55:29.03697765Z","@version":"1","message":"サーバ起因...
省略
spring-boot-sample-tomcat/fluentbit/test$ 

error-というタグがついたログだけが出力されていました!よさそうですね。

おつかれさまでした。

ここまでのまとめ

まとめます。

  • parser 処理によってJSONデータを取り出して、ログに新たなプロパティを追加・出力できることが分かりました。

  • また rewrite_tag 処理によって、ログのプロパティの値によってタグを付け直したりできることが分かりました。

  • これらをうまく活用することで

    • アプリや共通のフレームワークが構造的なデータを出力
    • その情報に基づいて、タグを付け直す
    • タグごとに、ログ出力先を変える
      などができそうですね!

上記の活用例をTIPSとしてまとめました。

TIPS1: 業務アプリの出力ログと、共通機能(FW)の出力ログを分ける

業務アプリと共通機能(FW)があったとして、どちらも、いわゆる JavaでおなじみのLoggerの機構を使うと仮定します。それらログをそれぞれ別の場所に保存したいとします。

やりかたは

  • FW だけ「FWのログだよ」というプロパティを追加してログ出力3
  • parserでログを解析
  • Fluent Bitがそのプロパティを見て、該当ログだけ別のタグを付ける
  • タグごとに出力先を分ける

とかでできそうですね。

TIPS2: ERROR ログだけ出力先を分ける

ERRORログだけ出力先を分ける、たとえばAWSだと ERRORログだけCloudWatch Logsに保存、その他のログはS3に保存、などができそうです。やり方は上記のハンズオンでやったとおりです。

TIPS3: 特定のログだけメールする

ある特定のケースでのみ内容はメールで通知したいぞ、なんてときがあります。
この場合は、TIPS1で「FWのログを判別」したときと同様に

  • アプリがログ出力時に「メールしてほしいな」というプロパティを付けてログ出力
    • 宛先、内容、など可変の内容も、個別のプロパティとして出力しておく
  • そのログだけ、(メール送信ログ用の)S3へ保存。 可変の内容もJSONデータとして保存されます。
  • S3のトリガーにLambdaを設定しておくことで、Lambdaが起動。
    • Lambdaの処理では、S3へ保存されたJSONデータを取りだし、SES経由などでメール送信します。

などとやれば実現できますね。

さて、ここまでやって「アプリ達が、(Fluent Bitが処理を判定するための)構造的なデータをログに出力する」のが重要ってことが分かりました。今回はJava/SpringBootでアプリを作っていますが、Javaのログ出力時にプロパティを追加して構造的なデータを出力する方法について、次回はやってみたいと思います。

以上、おつかれさまでした。

関連リンク

  1. --build オプションを付けているのは、前回の記事のイメージが残っている場合、前回のイメージが起動してしまう可能性があるためです。一方、起動時に毎回このオプションを付けると CONTAINER ID が変わる可能性があるのでご注意(Fluent Bitの設定ファイルに記述する CONTAINER IDが変わってしまう)。

  2. ちなみに falseの場合は、元タグのデータはなくなります。

  3. 次回以降記事にしようと思いますが、MDCという機構を使います。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?