OpenTelemetry ログのフィルタリング

OpenTelemetry コレクターでは、収集したログ・データをさまざまな方法でフィルタリングすることができます。 この資料では、特定の一般的なシナリオでログをフィルタリングする方法の例を示します。 これらの機能は OpenTelemetry Collector Contrib リポジトリーにのみ組み込まれているため、これらの例ではそのバージョンが必要です。 詳細については、 OpenTelemetry コレクター のコントリビューションに関するドキュメントを参照してください。

ログの内容でフィルタリングする

フィルター・プロセッサーは、ログ・メッセージの内容に適用される正規表現を受け入れます。 指定された正規表現に一致するログ・メッセージはドロップされ、相手側の受信側に転送されることはありません。

これらの例はすべて、同様のパターンに従っています。 filter セクションを opentelemetry-collector の構成ファイルに追加し、そのフィルターを同じファイル内の logs/processors パイプラインに含める必要があります。 Helm を使用してコレクターをインストールする場合、構成はインストール時に values.yaml ファイルの config: セクションに入ります。

特定の部分文字列を含むログメッセージを除外する

タイム・スタンプ、ログ・レベル、および以下の例のようなメッセージを含むログ・ファイルについて考えてみます。

2024-02-09 13:00:51 ERROR This is a test error message
2024-02-09 13:02:49 ERROR This is a test error message containing a secret

これらのログは、以下のようなファイル・ログ受信側構成を使用することで、より一般的に一致させることができます。

receivers:
  filelog/simple:
    include: [ /tmp/foo.log ]
    operators:
      - type: regex_parser
        regex: '^(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (?P<sev>[A-Z]*) (?P<msg>.*)$'
        timestamp:
          parse_from: attributes.time
          layout: '%Y-%m-%d %H:%M:%S'
        severity:
          parse_from: attributes.sev

指定された正規表現は、指定されたキャプチャー・グループにタイム・スタンプと重大度を割り当てます。 タイム・スタンプ layout は、コレクターがフォーマットを認識できるように定義されています。これにより、ログ・メッセージからの正しいタイム・スタンプを、コレクターがサーバーに送信するレコードのタイム・スタンプとして使用できます。 重大度は変更されずに送信されます。

この受信側を logs/receivers パイプラインに追加する必要があります。

  pipelines:
    logs:
      receivers:
        - filelog/simple

この構成では、正しくフォーマットされたすべてのメッセージがログからサーバーに報告されます。

しかし、ログメッセージの一つに秘密が隠されている。 シークレットを含むメッセージを除外するには、構成の processors セクションにフィルターを追加します。 フィルターには、単語「.」を含む任意の文字列に一致する secret正規表現を含める必要があります。 次の例では、OTTL の IsMatch(...)関数を使用して、「.」という文字を含む任意の文字列 secretに一致させます。

processors:
  filter/remove_secret:
    error_mode: ignore
    logs:
      log_record:
        - 'IsMatch(body, ".*secret.*")'

以下のフィルターを processors パイプラインに追加します。

pipelines:
  logs:
    receivers:
      - filelog/simple
    processors:
      - filter/remove_secret

この構成が適用されると、ログ行の例からの最初のメッセージのみが報告されますが、「secret」という語を含む 2 番目のメッセージは除去されます。

特定のサービスからのsyslogメッセージを除外する

Linux® システム上の syslog について検討します。 このシステムで誰かが Gnome デスクトップを使用している場合は、ノイズ・ログが作成される可能性があります。

Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 31 with keysym 31 (keycode a).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 32 with keysym 32 (keycode b).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 33 with keysym 33 (keycode c).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 34 with keysym 34 (keycode d).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 35 with keysym 35 (keycode e).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 36 with keysym 36 (keycode f).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 37 with keysym 37 (keycode 10).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 38 with keysym 38 (keycode 11).
Feb  9 09:10:00 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b org.gnome.Shell.desktop[4771]: Window manager warning: Overwriting existing binding of keysym 39 with keysym 39 (keycode 12).
Feb  9 09:10:26 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b systemd[1]: fprintd.service: Succeeded.

最初の例と同様に、 filelog 受信側は、このログをモニターし、正規表現を使用してタイム・スタンプをマップすることができます。

receivers:
  filelog/syslog:
    include: [ /var/log/syslog ]
    operators:
      - type: regex_parser
        regex: '^(?P<time>[A-Za-z]{3}[ ]+\d{1,2} \d{2}:\d{2}:\d{2}) (?P<msg>.*)$'
        timestamp:
          parse_from: attributes.time
          layout: '%b %e %H:%M:%S'

タイムスタンプは最初の例とは異なって見えますが、正しい正規表現とレイアウト定義があれば、理解することができます。 このログには重大度が含まれていません。 受信側は、常に logs/receivers パイプラインに含まれている必要があります。

 pipelines:
   logs:
     receivers:
       - filelog/simple
       - filelog/syslog

サーバー・システムでは、システム・プロセスをモニターする一方で、デスクトップ環境で生成されたログを除外することができます。 正規表現フィルターは、特定のサービスからのすべてのメッセージをブロックできます。

processors:
  filter/remove_gnomeshell:
    error_mode: ignore
    logs:
      log_record:
        # message body will have the timestamp stripped off by the regex_parser, so it looks like:
        #  "hostname service[pid]: message"
        # Regex matches this as:
        #   [^ ]+                       <hostname> One or more non-whitespace characters, followed by
        #                               A space, followed by
        #   org.gnome.Shell.desktop     <service name> followed by
        #   [                           followed by
        #   [0-9]+                      <pid> one or more numeric digits, followed by
        #   ]:                          followed by
        #   .*                          <message> the rest of the log message
        - 'IsMatch(body, "[^ ]+ org.gnome.Shell.desktop\\[[0-9]+\\]:.*")'

フィルターを logs/processors パイプラインに追加します。

logs:
  receivers:
    - filelog/simple
    - filelog/syslog
  processors:
    - filter/remove_secret
    - filter/remove_gnomeshell

これで、サービス・フィールドが org.gnome.Shell.desktop であるすべてのログ・レコードがドロップされます。

インフラストラクチャデータによるログのフィルタリング

フィルタプロセッサは、ペイロードのセクション resource attributes に指定された情報に基づいてフィルタリングを行うことができます。 このプロセッサを使用すると、たとえば特定の Kubernetes ポッド、 Kubernetes ネームスペース全体、あるいは特定のホストからのメッセージをすべて除外することが可能になります。 サポートされているインフラストラクチャ・データの詳細については、 「インフラストラクチャ・データ」 を参照してください。

ログの重大度でフィルタリングする

コンテナが、さまざまな重大度レベルのログを生成するように設定されているケースがあるかもしれません。 そのような場合、コレクターに不要なデータが殺到するのを防ぐために、ERRORおよびFATALメッセージ以外のすべてのメッセージを除外することをお勧めします。

次のログ例を考えてみましょう:

[15:52:30 DEBUG] Some debug message.
[15:52:30 INFO] Some info message.
[15:52:30 ERROR] Some error message.
[15:52:30 FATAL] Some fatal message.

DEBUGおよびINFOログを重大度でフィルタリングするには、まず各ログレコードのフィールド severity_text を設定する必要があります。 これは、以下のサンプル transform プロセッサを使用することで実現できます:

transform/set_log_severity:
  log_statements:
    - context: log
      statements:
        - set(severity_text, "Debug") where IsMatch(body.string, "\\[[0-9]{2}:[0-9]{2}:[0-9]{2} DEBUG\\]")
        - set(severity_text, "Info")  where IsMatch(body.string, "\\[[0-9]{2}:[0-9]{2}:[0-9]{2} INFO\\]")
        - set(severity_text, "Error") where IsMatch(body.string, "\\[[0-9]{2}:[0-9]{2}:[0-9]{2} ERROR\\]")
        - set(severity_text, "Fatal") where IsMatch(body.string, "\\[[0-9]{2}:[0-9]{2}:[0-9]{2} FATAL\\]")
注: これらのステートメントは、各ログレコードに対して順番に実行されるため、その順序が重要です。 正規表現が複数のパターンに一致する場合、最後の文が優先されます。 どの正規表現もパターンに一致しない場合、 severity_textInstana は空の文字列に設定され、はこれを深刻度レベルとして解釈します。 None

ログレコードにこの severity_text 設定が指定されている場合、以下のサンプル filter プロセッサを使用することで、深刻度が「ERROR」未満のログや、深刻度が指定されていないログを無視することができます。

filter/remove_unnecessary_logs:
  logs:
    log_record:
      - IsMatch(severity_text, "^(|Debug|Info)$")

この例では、 OTTLの IsMatch(...)関数を使用して、指定された正規表現が空の文字列、および「 Info log」 Debugの深刻度と一致するログを除外しています。

あるいは、 `not ` キーワードを使用することで、ログの重大度に基づいて除外するのではなく、含めたいログを指定することもできます。 これにより、深刻度レベル Error または に一致しないログが除外 Fatalされるようになります。

filter/remove_unnecessary_logs:
  logs:
    log_record:
      - not IsMatch(severity_text, "^(Error|Fatal)$")

この service/pipelines/logs/processors セクションに、新しいフィルターを追加してください:

processors:
  - resourcedetection
  - transform/set_log_severity
  - filter/remove_unnecessary_logs
  - batch

リソース属性によるログのフィルタリング

以下は、ログのフィルタリングに使用できるリソース属性の例を網羅したものではありませんが、その一部を挙げたものです:

k8sattributes プロセッサおよび resourcedetection プロセッサによって収集されるサンプル属性:

  • resource.attributes["k8s.pod.name"]: ログメッセージを生成した Kubernetes ポッドの名前
  • resource.attributes["k8s.container.name"]: ログメッセージを生成した基となるコンテナの名前
  • resource.attributes["k8s.namespace.name"]: ログメッセージを生成したPodが含まれるネームスペースの名前
  • resource.attributes["k8s.deployment.name"]: ログメッセージを生成したポッドを管理する Kubernetes デプロイメントオブジェクトの名前
  • resource.attributes["k8s.node.name"]: ログメッセージを生成したポッドが実行されている Kubernetes ノードの名前
  • resource.attributes["k8s.pod.hostname"]: ログメッセージを生成したプロセスが実行されているホストの名前。 コレクターがコンテナ内で実行されている場合、ホスト名は通常、コンテナ名であり、実際の基盤となるホストの名前ではありません
  • resource.attributes["os.type"]: ログメッセージを生成したプロセスが実行されているホストのオペレーティングシステムの種類

k8sattributes プロセッサリソースプロセッサ、および変換プロセッサを活用することで、マッピング resource.attributes 内のユーザー定義属性を動的に設定し、カスタマイズ可能なログフィルタリングに利用することができます。

これらの例はすべて、同様のパターンに従っています。 filter セクションを opentelemetry-collector の構成ファイルに追加し、そのフィルターを同じファイル内の logs/processors パイプラインに含める必要があります。 Helm を使用してコレクターをインストールする場合、構成はインストール時に values.yaml ファイルの config: セクションに入ります。

特定の Kubernetes コンテナからのログメッセージを除外する

calico-nodeという Kubernetes コンテナーからのすべてのログ・メッセージをフィルターで除外するとします。 構成の processors セクションで、以下のようなブロックを追加します。

filter/remove_calico:
  error_mode: ignore
  logs:
    log_record:
      - resource.attributes["k8s.container.name"] == "calico-node"

次に、 service/pipelines/logs/processors セクションに新しいフィルターを含めます。

processors:
  - resourcedetection
  - transform/set_log_severity
  - filter/remove_calico
  - batch

新規フィルターが最後にリストされます。 表示されている他のプロセッサーはコンテキスト専用であり、フィルター・プロセッサーが機能するためには必要ありません。

すべての Linux® システムからログメッセージを除外する

Linux®ベースのシステムからのすべてのログ・メッセージをブロックする場合は、構成の 'processors セクションで以下を追加できます。

filter/remove_linux:
  error_mode: ignore
  logs:
    log_record:
      - resource.attributes["os.type"] == "linux"

同様に、前の例を基に作成した service/pipelines/logs/processors セクションに、新しいフィルターを含めます。

processors:
  - resourcedetection
  - transform/set_log_severity
  - filter/remove_calico
  - filter/remove_linux
  - batch

特定のラベルやアノテーションを持つ特定の Kubernetes ポッドまたはデプロイメントからのログメッセージを除外する

次のような Kubernetes のデプロイメントがあり、すべてのログメッセージを除外したいラベルやアノテーションが設定されているとします:

apiVersion: apps/v1
kind: Deployment
metadata:
  [...]
spec:
  [...]
  template:
    metadata:
      labels:
        some-keyword-label: "ABCD-label-substring-ABCD"
      annotations:
        some-keyword-annotation: "ABCD-annotation-substring-ABCD

some-keyword-label でフィルタリングしたい場合は、 some-keyword-annotation以下の例に示すように プロセッサ k8sattribute を拡張し、すべてのポッドのラベルとアノテーションを取得することができます:

processors:
  k8sattributes:
    [...]
    extract:
      metadata:
        [...]
      ## Note: The '$$1' is a placeholder for the label or annotation name and will be used in the 'resource.attributes' mapping.
      labels:
        - tag_name: $$1
          key_regex: (.*)
          from: pod
      annotations:
        - tag_name: $$1
          key_regex: (.*)
          from: pod

次の例を参考にして、ラベルと some-keyword-annotationsome-keyword-label 注釈を抽出してください:

processors:
  k8sattributes:
    [...]
    extract:
      metadata:
        [...]
      ## Note: In this example same label or annotation names are used as the ones to be extracted.
      ## The 'tag_name' corresponds to the key in the 'resource.attributes' mapping.
      ## The 'key' corresponds to the label or annotation you want to extract.
      labels:
        - tag_name: some-keyword-label
          key: some-keyword-label
          from: pod
      annotations:
       - tag_name: some-keyword-annotation
         key: some-keyword-annotation
         from: pod

必要なラベルやアノテーションを抽出するように k8sattributes プロセッサを設定したら、`` または `` filter_by_keyword filter プロセッサを使用して、`` some-keyword-label ラベルまたは `` some-keyword-annotation アノテーションを持つポッドからのログメッセージを除外することができます:

filter/keyword_filter:
  logs:
    log_record:
      ## Note: You can add as many filters as you would like if there are multiple labels/annotations.
      - IsMatch(resource.attributes["some-keyword-label"], ".*(label-substring).*")
      - IsMatch(resource.attributes["some-keyword-annotation"], ".*(annotation-substring).*")

この service/pipelines/logs/processors セクションでは、前の例を基に、新しいフィルターを追加します:

processors:
  - resourcedetection
  - filter/filter_by_keyword
  - batch

新規フィルターが最後にリストされます。 表示されている他のプロセッサーはコンテキスト専用であり、フィルター・プロセッサーが機能するためには必要ありません。

ログから機密情報を削除する

ログ・メッセージには、個人情報 (PII) や、非公開にしてサーバーに送信したり保存したりしないようにする必要があるその他の機密データが含まれることがあります。 この情報には、パスワード、クレジット・カード番号、その他の任意の数の情報が含まれる場合があります。 transform プロセッサーは、正規表現を使用してこのような情報を検出し、それを別のものに置き換えることができます。

これらの例は、同様のパターンに従っています。 transform セクションを opentelemetry-collector の構成ファイルに追加し、その変換を同じファイル内の logs/processors パイプラインに組み込む必要があります。 Helm を使用してコレクターをインストールする場合、構成はインストール時に values.yaml ファイルの config: セクションに入ります。

ログメッセージからパスワードを削除する

認証障害が発生するたびに、アプリケーションが指定されたパスワードをログに記録するとします。 ログ・レコードは次のようになります。

2024-02-14 19:40:31 WARNING failed login for user bob, password=bobo

ログイン試行が失敗したことを認識することは重要ですが、使用されたパスワードをログに記録することは不適切です。

アプリケーションは、既知の形式でパスワードをログに記録します。 パターンは常に password=xyzです。 正規表現は、そのパターンを検出し、それを別のものに置き換えることができます。

transform/redact_password:
  log_statements:
    - context: log
      statements:
        # Any log messages containing "password=xxx" or "passwd=yyy" will be matched.
        # Regex matches these as:
        #   passw                     The literal string 'passw', followed by
        #   (?:or)??                  The literal string "or" 0 or 1 times (this allows either password or passwd)
        #   d=                        The literal string "d=", followed by
        #   [^\s]*                    Any non-whitespace characters (the password), followed by
        #   (\s?)*                    0 or more whitespace characters, marking the end of the password.
        - replace_pattern(body, "passw(?:or)??d\\=[^\\s]*(\\s?)", "password=REDACTED")
        - replace_pattern(attributes["msg"], "passw(?:or)??d\\=[^\\s]*(\\s?)", "password=REDACTED")

replace_pattern ディレクティブは、メッセージ body に対して 1 回、および msg 属性に対して 1 回出現します。 オープン・テレメトリー・コレクターは、メッセージの内容を両方の場所に書き込むため、両方を更新する必要があります。

次に、変換を logs/processors パイプラインに追加します。

logs:
  receivers:
    - otlp
    - filelog/simple
  processors:
    - transform/redact_password

変換により、初期ログ・メッセージは次のようになります。

2024-02-15 15:45:37 WARNING failed login for user bob, password=REDACTED

ログメッセージからホスト名を削除する

別のアプリケーションが syslog にホスト名を書き込むとします。

Feb 15 15:52:36 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b service[45568]: This is a test error message from where.ever.ibm.com

ホスト名が機密と見なされる場合、それらのホスト名がログに追加されないようにブロックすることができます。 すべてのホストが同じドメイン (この例では .ibm.com) にある場合、正規表現はパターンを認識し、名前を隠すことができます。

transform/remove_hostnames:
  log_statements:
    - context: log
      statements:
        # Any log message containing a hostname ending in ".ibm.com" will have the hostname removed.
        # Regex matches as:
        #   ([a-zA-Z0-9-_\.]+)       One or more letters, digits, dashes, underscores, or dots, followed by
        #   \.ibm\.com              The literal string ".ibm.com"
        - replace_pattern(body, "([a-zA-Z0-9-_\\.]+)\\.ibm\\.com", "<hidden hostname>")
        - replace_pattern(attributes["msg"], "([a-zA-Z0-9-_\\.]+)\\.ibm\\.com", "<hidden hostname>")

この場合も、ログ・メッセージ・テキストが両方の場所で発生するため、 body 属性と msg 属性の両方が再書き込みされます。

ここでも、変換をパイプラインに追加します。

logs:
  receivers:
    - filelog/syslog
  processors:
    - transform/redact_password
    - transform/remove_hostnames

その後、メッセージは次のように再書き込みされます。

Feb 15 15:52:36 li-8dc514cc-2e0d-11b2-a85c-f1d7ce42b83b service[45568]: This is a test error message from <hidden hostname>