New Relic Now+ Learn about New Relic’s most transformative platform update yet with 20+ product launches.
Join Live Event

New Relic Infrastructure Agentはログ転送の機能を持っているため、シンプルにファイルをtailして更新した行をNew Relic Logsに送ったり、syslogやEvent Logを転送したりすることは数行の設定で可能です。このログ転送機能はFluent Bitを利用しているため、Fluent Bitの設定ファイルそのものを指定することで、Fluent Bitの機能を活用することができます。例えば1つのエントリが複数行にわたるようなログを転送したり、ログをパースし構造化した上で属性を追加したり、Luaスクリプトによりログを加工したりすることもできます。この記事では、fluentbitの機能を活用したログ転送のサンプルを紹介したいと思います。

対象となるログのエントリは以下のような複数行に渡るログです。スタックトレースを含んだ例外などを扱う場合を想定しています。それぞれのログのエントリは日時を含むメッセージで始まっていますが、複数のフォーマットが混在しています。もちろん、ログの出力フォーマットを変更した方がシンプルに解決できますが、すぐに変更できない場合にfluentbitの機能を活用して解決するケースを考えています。

2021-02-15 10:13:10 致命的なエラーが発生しました

RuntimeException at NewRelicLab.ExampleApp.Main

  [Thread-1] at XXX.YYY.ZZZ



15-Feb-2021 10:13:15.123 INFO 情報

起動完了

まず必要な設定ファイルを紹介します。まずはInfrastructure Agentのログ転送設定用のフォルダ(Linuxの場合 /etc/newrelic-infra/logging.d/、Windowsの場合 C:\Program Files\New Relic\newrelic-infra\logging.d\)に以下の設定ファイルを配置します。設定ファイル名およびnameキーの値は任意で、Windowsの場合は記載のパスをWindowsのものに置き換えてください。

logs:

  - name: external-fluentbit-config-and-parsers-file

    fluentbit:

      config_file: /etc/newrelic-infra/logging.d/fluentbit.conf

      parsers_file: /etc/newrelic-infra/logging.d/parsers.conf
そしてここで指定している2つのconfファイルが、Fluent Bitの設定ファイル形式のものです。参照するLuaスクリプトとあわせてGistに公開しています。
function append_tag(tag, timestamp, record)
new_record = record
new_record["tag"] = tag
return 1, timestamp, new_record
end
view raw append_tag.lua hosted with ❤ by GitHub
# Global configuration
# https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/configuration-file
[SERVICE]
Log_File /var/log/newrelic-infra/fluentbit.log
Log_Level info
# 複数行のログエントリを持つ1つのファイルをtailします。
# https://docs.fluentbit.io/manual/pipeline/inputs/tail
# Path_Key: ログメッセージをソースファイル名で装飾することを有効にします。 ここではNew Relicのlogs属性の標準に合わせてキャメルケースを使用しています。
# Parser_Firstline: 最初の行かどうかを検出する正規表現で、行全体にマッチしなければなりません。
# Key: ログが解析されなかった場合、デフォルトの 'log' 属性を NR フレンドリーな 'message' に変更します。
[INPUT]
Name tail
Path /home/azureuser/logs/*.log
Path_Key filePath
Multiline On
Parser_Firstline first_log_firstline
Tag first_log
Key message
# このブロックは,追加のパーサーを実行して属性を分解し,タイムスタンプを読み込みます.
# https://docs.fluentbit.io/manual/pipeline/filters/parser
# Match: 同じタグを持つ[INPUT]を検索します。
# Key_Name. 最初の行のパーサから読み込む属性です。
# Parser: 実行するパーサーを1つ以上指定します。
# Reseve_Data: [INPUT]で指定したfilePathを保持します。
[FILTER]
Name parser
Match first_log
Key_Name log
Parser first_log_1
Parser first_log_2
Reserve_Data On
# Luaスクリプトを実行してこの設定ファイルのtagをログのtagフィールドに追加します
# https://docs.fluentbit.io/manual/pipeline/filters/lua
# https://github.com/fluent/fluent-bit/blob/master/scripts/test.lua
# Match: 処理したい対象のTagを指定します。
# script: 実行するLuaスクリプトのファイルパス。相対パスの基準はこのファイルではないため、絶対パスでの指定を推奨。
# call: Luaスクリプト内にある実行したい関数。
# time_as_table: 秒が小数を含む場合、精度を維持するためにOn。
[FILTER]
Name lua
Match first_log
#Match second_log
script /etc/newrelic-infra/logging.d/append_tag.lua
call append_tag
#call cb_use_system_time
time_as_table On
Reserve_Data On
# すべてのログに共通の属性を追加
# https://docs.fluentbit.io/manual/pipeline/filters/record-modifier
# Match: 処理したい対象のTagを指定します。
# Record: 属性として追加したいKey Valueのペア
[FILTER]
Name record_modifier
Match *
Record hostname ${HOSTNAME}
Record environment production
Reserve_Data On
# パースしたログをNew Relicに送信するだけでなく、ローカルに保存します。
# トラブルシューティング目的でのみ推奨され、運用環境では使わないでください。
[OUTPUT]
Name file
Match *
Path /home/azureuser/debug
view raw fluentbit.conf hosted with ❤ by GitHub
logs:
- name: external-fluentbit-config-and-parsers-file
fluentbit:
config_file: /etc/newrelic-infra/logging.d/fluentbit.conf
parsers_file: /etc/newrelic-infra/logging.d/parsers.conf
view raw logs.yaml hosted with ❤ by GitHub
# https://docs.fluentbit.io/manual/pipeline/parsers/regular-expression
[PARSER]
Name first_log_firstline
Format regex
Regex /^(?<log>[\s\S]*?(?:\d{2}-[A-Z][a-z]+-\d{4} \d{2}:\d{2}:\d{2}.\d{3}|\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})[\s\S]*)$/
[PARSER]
Name first_log_1
Format regex
Regex /^(?<message>[\s\S]*?(?<time>\d{2}-[A-Z][a-z]+-\d{4} \d{2}:\d{2}:\d{2}.\d{3}) (?<severity>[A-Z]+) [\s\S]*)$/
Time_Key time
Time_Format %d-%b-%Y %H:%M:%S.%L
Time_Offset +0900
[PARSER]
Name first_log_2
Format regex
Regex /^(?<message>[\s\S]*?(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})[\s\S]*)$/
Time_Key time
Time_Format %Y-%m-%d %H:%M:%S
Time_Offset +0900
view raw parsers.conf hosted with ❤ by GitHub

設定ファイルを保存後、Infrastructure Agentを再起動し、ログを追記するとNew Relic Logsに転送されクエリできるようになります。

次に、設定ファイルの説明もかねてFluent Bitがどのようにログをパースし解析するかを説明します。

  1. [INPUT]セクションでtail対象となる読み込むファイルを指定しています。
    1. Parser_Firstline は[PARSER]セクションのfirst_log_firstlineを参照します。
    2. Tagで指定しているfirst_logはあとで利用するため、名前を変える場合は注意が必要です。
  2. [PARSER]セクションのfirst_log_firstlineが実行されます。
    1. 複数行のログの場合、このフェーズでは各行はまだ分離されています。2行目以降は定義された最後の名前つきキャプチャグループに自動的に追加されるため、 このパーサー内でtimeやmessageといった追加の属性を抽出させることもできますが、厳密に最初の行を識別するためだけのものにすることをお勧めします。
    2. この例では、複数行にわたるログの最初の行を判定するために、正規表現はパイプ(|)で区切られた2つのタイムスタンプフォーマットにマッチさせています。ログのフォーマットによっては日時ではなく別の正規表現が適切かもしれません。
    3. ログメッセージのすべてをマッチさせるため、[\s\S]*?を使い、ログキャプチャグループ(?<log>...).に格納しています。行頭と行末を一致させるために^と$を使うことをお勧めします。
  3. parserという名前の[FILTER]セクションが実行されます。
    1. [INPUT]セクションでfirst_logとタグづけされたものを見つけます。1つのフィルタで複数のタグにマッチされることもできますが複雑になります。
    2. 次にログの属性を読み込みます。属性がないログに対してはなにもしません。
    3. [PARSER]で指定されたfirst_log_1もしくはfirst_log_2が見つかるまでログに対してパーサーを実行します。
      1. パーサーfirst_log_1もしくはfirst_log_2のどちらかに一致したらパース処理が実行されます。
      2. キャプチャグループtimeを含む複数のキャプチャグループにわけれます。
      3. Time_Keyで指定されたキャプチャグループ(ここではtime)を読み込み、Time_Keyに従い、Time_OffsetだけUTCとずれた日時をUnix時間に変換します。
      4. Time_Offsetはログメッセージの日時にタイムゾーンの指定がないものの、特定のタイムゾーンの時間を表しているときに使えます。ログメッセージにタイムゾーンの指定がある場合はTime_Formatで%zが利用できます。
  4. luaという名前の[FILTER]セクションが実行されます。
    1. [INPUT]セクションでfirst_logとタグづけされたものを見つけます。
    2. scriptで指定されたLuaスクリプトファイルのcallで指定された関数を実行します。このLuaスクリプトはFluent Bitのサンプルとして提供されているもので、[INPUT]セクションで指定されたタグをログのtag属性として追加します。逆に言うと、[INPUT]タグで指定したタグは、デフォルトではログに追加されず、Fluent Bit内部でのみ利用されます。
    3. 時刻の秒が小数の場合(Time_Formatで%Lを利用します)、time_as_tableを指定します。指定しない場合小数部分の精度が落ち、ずれた時刻のログとして保存されることがあります。この設定はFluent Bit 1.6.0で導入されたため、Infrastructure Agent 1.13.2以降を利用する必要があります。
  5. record_modifierという名前の[FILTER] セクションが実行されます
    1. Match *は、すべての[INPUT]ファイルに対して実行されることを意味します。
    2. ホスト名のような共通の属性を、ソースの形式に関係なく、すべてのレコードに追加します。
  6. file [OUTPUT]が実行されます
    1. 運用環境ではおすすめできませんが、設定のデバッグ目的などに利用できます。デバッグ目的では、[SERVICE]タグのログ出力も利用してください。
  7. ログレコードをNew Relicに送信します。New Relic Logsへの送信はInfrastructure Agentにより自動的に設定されているため、追加の設定は不要です。

 

このように、New Relic Infrastructure Agentのログ転送ではFluent Bitの機能を活用できます。Fluent Bitのすべての機能はドキュメントを参照してください。Infrastructure Agentと利用することで配布や再起動をまとめて管理することができます。