10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GCP】Opsエージェントで複数パイプラインを実装し、Loggingに送信する

Last updated at Posted at 2025-10-02

はじめに

初めまして、tunamayoです。

GCEに保存された2つのログファイルから、内容を構造化してCloud Loggingに送信できるようOpsエージェントを構成してみました。

Ops エージェントの設定に関する記事は少ない上、複数のパイプラインを構成している例はあまりみたことがないので、備忘録 & もしや誰かの参考になるかも?と思い記載しました。

目的

  • GCEインスタンスからLoggingに構造化したログを送信し、確認を容易にする

GCEインスタンス上で動作するアプリケーションのログが /usr/local/myApp/log

  1. myapp-01.log
  2. myapp-02.log

に書き出されており、内容を見るにはインスタンスにsshする必要がありました。
(問題点:各ファイルをTerminalで見るのは煩雑で、コマンドの誤操作リスクも増える)

↑のログがLog Exprolerから確認できるようになれば、問題点が解消できそうです。

前提 : Ops エージェントとは

GCEインスタンスからテレメトリーを収集するエージェント
➡︎これを設定すると、GCEインスタンスからLoggingへのログ送信が可能になります。

又、Ops エージェントはログを加工する機能(processor)を持っており、目的に応じてログを構造化した上でCloud Loggingに送信することができます。

config.yaml

Ops エージェントの設定は/etc/google-cloud-ops-agent/config.yamlで構成します。

# 基本構成
logging:
  receivers: # ログを収集する対象ファイルを定義
    syslog:
      type: files
      include_paths:
      - /var/log/messages
      - /var/log/syslog

  processors: # ログをパースする場合に処理を定義
    log_parser:
      type: parse_regex
      regex: "^(?<msg>.*)$"
  service:
    pipelines: # パイプラインを定義
      default_pipeline:
        receivers: [hostmetrics]
        processors: [metrics_filter]

処理の全体的な流れはpipelines:で定義されます。

Ops エージェントを実行すると、

  1. service配下のpipelines:で定義したパイプラインが走る
  2. パイプラインに設定したreceiversが呼ばれ、指定パスからログを取得
  3. パイプラインに設定したprocessorsが呼ばれ、↑で取得したログを加工
  4. Loggingに送信される

といった流れで処理が進みます。

実装

今回は2つの.logファイルをLoggingに送信するため、pipelineを2つ用意します
(各ファイルは内容のフォーマットが異なるので、processorもそれぞれに合わせ複数用意。
 processorに記載した順序で段階的に加工し、各ファイルのログを構造化します)

パイプライン仕様

  1. parse_myapp_01_logs : myapp-01.logをパースするパイプライン
  2. parse_myapp_02_logs : myapp-02.logをパースするパイプライン
logging:
  receivers:
    # myapp-01.logのレシーバ
    myapp_01_logs:
      type: files
      include_paths:
        - /usr/local/myApp/log/myapp-01.log
    # myapp-02.logのレシーバ
    myapp_02_logs:
      type: files
      include_paths:
        - /usr/local/myApp/log/myapp-02.log

  processors:
    # myapp-01.logのpaese
    ## 1.正規表現でパースして構造化
    parse_myapp_01:
      type: parse_regex
      field: message
      regex: "^\[(?<time>[^\]]+)\] ip\[ *(?<ip>[^\]]*)\] op\[ *(?<op>[^\]]*)\] cid\[ *(?<cid>[^\]]*)\] sid\[ *(?<sid>[^\]]*)\] - (?<method>(?:START )?[A-Z]+) (?<url>[^:]+):(?: code=(?<status_code>\d+), time=(?<response_time>\d+)ms,)? (?<param>.*)$"
      time_key: time
      time_format: "%Y-%m-%d %H:%M:%S,%LJST"
    
    ## 2.フィールドの型を指定
    conv_int:
      type: modify_fields
      fields:
        jsonPayload.ip:
          default_value: ""
        jsonPayload.op:
          default_value: ""
        jsonPayload.cid:
          default_value: ""
        jsonPayload.sid:
          default_value: ""
        jsonPayload.status_code:
          default_value: 0
          type: integer
        jsonPayload.response_time:
          default_value: 0
          type: integer

    # myapp-02.logのparse
    ## 1.複数行exeptionを結合
    parse_myapp_02_multiline:
      type: parse_multiline
      match_any:
      - type: language_exceptions
        language: java
    
    ## 2.text+json混合ログを分離
    parse_myapp_02_mixed_json_extract:
      type: parse_regex
      regex: '^(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})\s+(?<level>[A-Z]+)\s+(?:\[(?<thread>[^\]]+)\]\s+)?(?<class>[\w\.$]+):?\s+(?<message>.*?)\n(?<json_content>{.*})'
      time_key: time
      time_format: "%Y-%m-%d %H:%M:%S,%LJST"

    ## 3.分離したjson_contentをparse
    parse_myapp_02_mixed_json_parse:
      type: parse_json
      field: json_content
    
    ## 4.標準的な単一行ログを解析
    parse_myapp_02_oneline_corrected:
      type: parse_regex
      regex: '^(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})\s+(?<level>[A-Z]+)\s+\[(?<thread>[^\]]+)\]\s+(?<class>[\w\.$]+):?\s+(?<message>.*)$'
      time_key: time
      time_format: "%Y-%m-%d %H:%M:%S,%LJST"

    ## 5.単一行のjsonをparse
    parse_myapp_02_oneline_json:
      type: parse_json

    ## 6.フィールドをlogEntryの標準形に整形
    standardize_fields:
      type: modify_fields
      fields:
        severity:
          ### levelをトップレベルのseverityとして移動/変換
          move_from: jsonPayload.level
          map_values:
            "WARN": "WARNING"
            "ERROR": "ERROR"
            "INFO": "INFO"
            "DEBUG": "DEBUG"

  service:
    pipelines:
      # 1つ目のパイプライン(myapp-01.log用)
      parse_myapp_01_logs:
        receivers:
          - myapp_01_logs
        processors:
          - parse_myapp-01
          - conv_int_myapp_01
      # 2つ目のパイプライン(myapp-02.log用)
      parse_myapp_02_logs:
        receivers:
          - myapp_02_logs
        processors:
          - parse_myapp_02_log_multiline
          - parse_myapp_02_mixed_json_extract
          - parse_myapp_02_mixed_json_parse
          - parse_myapp_02_oneline_corrected
          - parse_myapp_02_oneline_json
          - standardize_fields

設定反映(コマンド)

/etc/google-cloud-ops-agent/config.yamlを更新したら、
Ops エージェントを再起動し設定を反映します。

systemctl restart google-cloud-ops-agent.service

結果

Cloud LoggingのLog Exprolerで myapp-01.log / myapp-02.logが表示されるようになりました
構造化も、processorで設定した通りできている模様です

myapp-01.log myapp-02.log
image.png image.png

まとめ

結果:2つのログファイルから、それぞれの内容をCloud Loggingに送信できました。
「Log ExprolerからGCEのログファイル内容を見られるようにする」という目標は、達成できたと思います!
(ログの構造化については、ファイル毎に内容/フォーマット/パターンが異なり、processorの構成に苦労しました😇。まだ完璧な構造化とは言えないので、今後修正したいです)

参考

10
2
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
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?