こんにちは。Fluentdのメンテナーの福田です。
Fluentdは、様々なデータソースからデータを収集し、様々な出力先へ転送することができる便利なフリーソフトウェアです!
Fluentdでは、プラグインを組み合わせることで様々な用途を実現できます。 その中でも、ファイルからログを収集したい場合にはin_tailプラグインがよく使われます。
しかし、ファイルと一言で言っても、その更新のされ方やログローテートのされ方は様々です。
加えて、in_tailプラグインにはとても多くの設定項目があります。
本記事では、
- まずはこれを把握すれば、大体のケースで
in_tailプラグインを問題なく使える!
という点をFluentdメンテナー視点でいくつか解説します。
Fluentdを使ってみたいけど設定がよく分からず使えていない、とか、使っているけど設定にあまり自信がない、という方は、ぜひご覧ください。
(以下の説明は Fluentd v1.16.3 時点のものになります)
最初に注意!
Fluentd v1.16.3 (fluent-package v5.0.2, td-agent v4.5.2)以降のバージョンを使用してください。
in_tailプラグインでは、エラーを出さずに収集が停止してしまう、といった大きな不具合が過去にありました。
Fluentd v1.16.3 (fluent-package v5.0.2, td-agent v4.5.2)時点でそのような大きな不具合を修正しています。
詳しくは Fluent Package v5.0.2リリース - in_tailプラグインの重大な不具合の修正 の記事で紹介しているので、ご覧ください。
動作確認の仕方
Fluentdは簡単に手元で動作を確認することができます。 Fluentdの簡単な動かし方については、以前の記事 Fluentdを動かしてみよう! で解説しているので、ぜひご覧ください。
ぜひ今回も実際にin_tailの動きを確認してみてください。
Fluentdを動かしてみよう! では、次の設定でFluentdが実際にデータを処理する様子を確認しました。
<source>
@type sample
tag test
</source>
<match test.**>
@type stdout
</match>
このsource設定の部分を、in_sampleプラグインからin_tailプラグインの設定に置き換えてください。
<source>
@type tail
tag test
path /tmp/test.log
pos_file /tmp/tail-test.pos
<parse>
@type none
</parse>
</source>
<match test.**>
@type stdout
</match>
そうすれば、 Fluentdを動かしてみよう! と同じように、Fluentdが出力するログメッセージとしてin_tailの収集ログを確認することができます。
最も基本的な使い方
まずは最も基本的な使い方を説明します。
設定
in_tailはInputプラグインなので、sourceセクションを足して次の5つの設定をします。
@type- 使いたいプラグインの名前を指定します。名前はそのプラグインのドキュメントを確認しましょう。今回は"tail"です1。
tag- 後続のFilterプラグインやOutputプラグインへのルーティング等に使うタグを指定します。今回はテスト用に
testとします。 - 詳しく知りたい方は Config File Syntax - Interlude: Routing や How do the match patterns work? をご覧ください。
- 後続のFilterプラグインやOutputプラグインへのルーティング等に使うタグを指定します。今回はテスト用に
- path
- 収集したいファイルのファイルパスを指定します。
- pos_file
- どこまで収集したかの記録を残すためのファイルパスを指定します。
in_tail設定を複数行う場合は、それぞれ別々のパスを指定してください。
- parseセクション
- 収集したログのパース設定を行います。好きなParserプラグインを使えます。そのまま出力したい場合は、parser_noneプラグインを設定すればよいです(つまり
@type noneを指定します)。
- 収集したログのパース設定を行います。好きなParserプラグインを使えます。そのまま出力したい場合は、parser_noneプラグインを設定すればよいです(つまり
以上を設定すると、例えば次のようなsource設定になります。
<source>
@type tail
tag test
path /tmp/test.log
pos_file /tmp/tail-test.pos
<parse>
@type none
</parse>
</source>
in_tailの動作
この設定を動かしてみると、in_tailは/tmp/test.logファイルの末尾を監視して、末尾に追記されるログをどんどん収集します。
一度Fluentdを停止しても、pos_fileにどこまで収集したかの記録が残るため、次回起動時には前回の続きから漏れなく収集してくれます。
前の章の「動作確認の仕方」で説明しているように、out_stdoutプラグインと組み合わせて動作を確認できます。 例えば次のようにファイルにログを追記すると、
echo "test log" >> /tmp/test.log
次のようなログをFluentdが出力するので、in_tailがログを収集したことが分かります。
2024-03-13 14:49:02.480691808 +0900 test: {"message":"test log"}
補足: in_tailがファイルを発見して監視している状態であれば、新規に追記したログをすばやく(基本的に数秒以内に)収集します。
ただし、もし新しくファイルを作成した場合、refresh_interval(デフォルト60秒)の間隔で監視ファイルリストを更新するため、1分程度待つ必要があります。
補足: 権限について
pathやpos_fileに指定するファイルパスにFluentdがアクセスできる十分な権限がないと、うまく動かないので注意が必要です。
権限が足りない場合、Permission denied @ rb_sysopen - /tmp/test.logのようなエラーが出ます。
- 例:
pathに対する読み込み権限が足りない2024-03-14 11:53:42 +0900 [warn]: #0 Permission denied @ rb_sysopen - /tmp/test.log - 例:
pos_fileに対する書き込み権限が足りない2024-03-14 11:45:24 +0900 [error]: #0 unexpected error error_class=Errno::EACCES error="Permission denied @ rb_sysopen - /tmp/tail-test.pos"
例えば、Unix系OSにおいてこのように権限が足りない場合は、chmodやchownコマンドを使って権限を調整したり、ユーザーやグループ設定を調整したりします(パッケージ「Fluent Package」は、デフォルトでfluentdユーザーおよびグループでサービスを実行します)。
pos_fileは読み込みと書き込みの双方の権限が必要です。
パッケージ「Fluent Package」の場合、デフォルトで/var/log/fluentd/が書き込み可能なパスとして作成されているので、pos_fileに/var/log/fluentd/pos/tail-test.posのようなパスを指定するのもよいでしょう。
pathは読み込み権限が必要です。
例えば、syslogが出力する一部のファイルには読み込み権限がないことがあります。
この場合、単にchmodやchownコマンドを使ってカレントファイルの権限を調整するだけでなく、今後ログローテート時に新規作成されるファイルの権限も調整する必要があります。
本記事では詳しい説明を省きますが、必要に応じてsyslog側の設定の調整も検討してください。
前半まとめ
ここまでで基本的なin_tailの設定と動きを確認しました。
以下では、in_tailを使う際に考慮する必要がある主な点として、以下の4点をそれぞれ説明します。
read_from_head: (初回起動時に)新規ファイルを先頭から読ませる- ログローテートへの対応
- カレントファイルのファイル名が変化する出力形式への対応
in_tailで収集するべきファイルであるかどうかの確認
まずはこれらの点を把握すれば、大体のケースでin_tailを問題なく使えるはずです!
より高度な使い方をしたい場合は、公式ドキュメントでどんな設定があるのかを確認してみてください。
本記事では紹介しきれない様々な機能をin_tailは持っています!
read_from_head: (初回起動時に)新規ファイルを先頭から読ませる
in_tailは、ファイル末尾への追記を収集し続ける性質のため、ファイル内容を全て読み込むとは限りません。
一度そのファイルの収集を開始した後は、pos_fileの記録を元にそれ以降の追記を漏れなく収集します(一時的にFluentdが停止したとしても)。
しかし、最初にファイルの収集を開始するときには、デフォルトではファイルの末尾から開始するため、既存のファイル内容は収集されません。
そのため、基本的には初回起動時のみ気にするべき話になります。
もし初回起動時にファイルの既存の内容を収集してほしい場合は、read_from_headをtrueに設定します。
過去にread_from_head設定が推奨されていた経緯
過去のバージョンで、read_from_head設定が一部の設定に実質必須であった、という経緯がありました。
そのため、とりあえずread_from_head trueを設定しておいた方が安心、という話を今でも聞くことがあります。
しかし、最近のバージョンでは状況が異なります。
Fluentd v1.14.3 (td-agent v4.3.0) 以降のバージョンでは、このパラメターはFluentd起動時の動作にしか影響しません。 そのため、これ以降のバージョンでは、
- Fluentd起動時に新規ファイルの内容を全部収集したい
という場合のみread_from_head trueを設定する、という考え方で問題ありません。
補足: Fluentd v1.14.3 (td-agent v4.3.0) よりも前のバージョンでは、pathに日付フォーマット(%Y%m%dなど)を使う場合やfollow_inodes機能を使う場合にはread_from_head trueが実質必須でした。
それ以降のバージョンでは、こういった動的に新規ファイルを発見する機能においてはread_from_head設定に依らずにファイルの内容を全て読み込むように修正されています。
ログローテートへの対応
多くの場合、末尾への追記形式のログファイルはログローテートします。
in_tailはログローテートにうまく対応しますが、形式によっては注意が必要なケースもあります。
in_tailがログローテートに対応する仕組み
in_tailは対象ファイルを監視していますが、一定の条件を満たすと対象ファイルにログローテートが発生したと検知します。
ログローテート発生を検出すると、対象ファイルの先頭から収集を再開します。
これによって、ログローテートが発生しても、漏れなく新しいログファイルの収集を継続します。
ログローテート検知の条件は、次の2つの少なくともどちらか一方を満たすことです。
- A: inode番号(ファイル固有のid)が変わる
- B: ファイルサイズが小さくなる
Aを満たすようなログローテート形式であれば、全く気にすることはありません。 安定してログローテートを検知できます。
一方で、Aを満たさないようなログローテート形式の場合は注意が必要です。 Bのファイルサイズでしか判定できないからです。
次の章で詳しく説明します。
対象ファイルのログローテート形式
前章の「A: inode番号(ファイル固有のid)が変わる」を満たすようなログローテート形式は、次のような動きになります。
- カレントファイルをリネームする:
test.log->test.log.1 - 新規カレントファイルを作成する:
test.logを新規作成
Linux系のシステムでよく見られるのがこの形式です。
一方で、次のようなログローテート形式も存在します。
- カレントファイルの内容をコピーする:
test.logの内容をtest.log.1にコピー - カレントファイルの内容をクリアする:
test.logのサイズが0になる
結果としては最初の形式と同じに見えます。 しかし、カレントファイルの中身は初期化されていますが、ファイル自体は以前と同じファイルのままです。 そのため、ファイル固有のidであるinode番号が変化しておらず、前章の「A: inode番号(ファイル固有のid)が変わる」の条件を満たしません。
この他にも、世の中にはいくつかのログローテート形式が存在します。
in_tailを使う際には、inode番号が変わる(カレントファイルが新規生成される)ようなログローテート形式なのかどうかを確認してください。
もし、そうでない場合は、前章の「B: ファイルサイズが小さくなる」という条件を満たさないと、ログローテートを検知できないことに注意してください。 事前に、ログローテートを検知して漏れなく収集できるかどうか、十分に検証することを推奨します。
カレントファイルのファイル名が変化する出力形式への対応
ログローテートの亜種として、次のようにカレントファイルのファイル名が変化する形式があります。 例えば、次のように日毎に生成された新規ファイルが、その日の間カレントファイルになる、という場合があります。
/tmp/test_20240306.log
/tmp/test_20240307.log <- 昨日のカレントファイル
/tmp/test_20240308.log <- 本日のカレントファイル
これを簡単に収集する2つの方法を紹介します。
strftime()のフォーマット文字列で、当日のファイルパスを収集する- ワイルドカードでまとめて収集する
以下それぞれの紹介です。
strftime()のフォーマット文字列で、当日のファイルパスを収集する
path設定には %Y, %m, %d, といったstrftime()のフォーマット文字列を利用できます。
例えば、/tmp/test_%Y%m%d.logのように設定すると、次のようにその日時に応じて動的に収集対象とするファイルパスを変更できます。
- 2024年3月7日時点:
/tmp/test_20240307.logを収集 - 2024年3月8日時点:
/tmp/test_20240308.logを収集
これによって、カレントファイル名が定期的に変化するようなケースに対応できます。
in_tail全体の設定例は次のようになります。
<source>
@type tail
tag test
path /tmp/test_%Y%m%d.log
pos_file /tmp/tail-test.pos
<parse>
@type none
</parse>
</source>
ワイルドカードでまとめて収集する
path設定にはワイルドカード*を利用できます。
これによって、複数のファイルをまとめて収集対象とすることができます。
例えば、/tmp/test_*.logのように設定することで、以下のファイルをまとめて収集対象にできます。
/tmp/test_20240306.log
/tmp/test_20240307.log
/tmp/test_20240308.log
これによって、カレントファイル名が定期的に変化するようなケースにも対応できます。
しかし、今回のように前日以前のファイルにはもうログが出力されなくて、当日に相当するファイルだけ収集対象にできれば十分、というケースもあります。 その場合に、このように全部まとめて収集対象とするのはリソースの無駄使いであり、特にファイル数が多い場合はファイルハンドルなどのリソース値を圧迫する懸念があります。
そのような場合は、limit_recently_modified設定を併用して、ファイルの更新日時がある程度最近のファイルだけに収集対象を絞るとよいです。
例えば、limit_recently_modified 3dのように設定することで、3日以内に更新されたファイルのみに収集対象を絞り込むことができます。
(当日のファイルのみ収集すればよい場合は1dでもよいのですが、念のために少し余裕を持たせてもよいでしょう)。
以上に基づくと、in_tail全体の設定例は次のようになります。
<source>
@type tail
tag test
path /tmp/test_*.log
pos_file /tmp/tail-test.pos
limit_recently_modified 3d
<parse>
@type none
</parse>
</source>
in_tailで収集するべきファイルであるかどうかの確認
in_tailは、ファイルからデータを収集するプラグインとしてFluentdに標準で組み込まれているため、ファイルからの収集によく使われます。
しかし、データソースがファイルだからといって、必ずしもin_tailが適切なプラグインとは限りません。
in_tailは、末尾への追記形式であるテキストファイルの収集を担うプラグインである、という点に注意してください。
もしそうではないファイルの収集を行いたい場合は、適切な収集方法を広く(Fluentd以外の機能を使う選択肢も含めて)検討してください。
以下、in_tailが適さない主な事例です。
- バイナリファイル
- 上書き形式で更新されるファイル
まとめ
本記事では、in_tailプラグインの基本的な使い方について、メンテナー視点で解説しました。
より高度な使い方をしたい場合は、公式ドキュメントでどんな設定があるのかを確認してみてください。
本記事では紹介しきれなかった様々な機能をin_tailは持っています!
また、Fluentdコミュニティーでは、日本語用のQ&Aも用意しています。 何か疑問や困ったことがあれば、こちらで質問してみてください!
最後に、クリアコードはFluentdのサポートサービスを行っています。 Fluent Packageへのアップデート支援や影響のあるバグ・脆弱性のレポートなどのサポートをいたしますので、詳しくはFluentdのサポートサービスをご覧いただき、お問い合わせフォームよりお気軽にお問い合わせください。
-
慣習的にInputプラグインは
in_というプレフィックスを付けて呼ぶことが多いですが、@typeを指定するときはプレフィックスを付けないことが一般的です。なぜなら、sourceセクション内に設定していることでInputプラグインであることが自明になっているからです。 ↩