Datadogダッシュボードでパラメータを含むパスのクエリ指定地獄から脱却する〜その①〜

# はじめに

Datadogダッシュボードのウィジェットにおいて、アプリケーションのログベースでAPI Count数等を表示させる際、DatadogのCategory Processorを使って楽しようと模索した方法をまとめます。

# この記事の背景

一体どういうこと？というのを少し補足します。
例えばあるサービスのシステムの概況を把握するため、Datadogのダッシュボードを使っています。

Datadogにはアプリケーションサーバのログを流しており、そのログをベースに各パス毎のカウント数(リクエスト数）やduration等をウィジェットとして表示したいとしましょう。

存在するアプリケーションのパスがパラメータを含まないものであれば話は簡単で、path毎にgroup byする以下のようなクエリを書けば良いかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413013/5389beaa-96e3-7ffc-3ffc-b3eea69687bd.png)

ですが、パスにパラメータを含む場合にこれをやるとどうなるかというと、それぞれがユニークパスとして認識されてしまい、結局どのパスなのか見通しが悪くなります。

なので以下のような指定をせざるを得ない状況があったのですが、これが結構地獄でした。
![スクリーンショット 2022-12-16 午後5.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413013/b6c86b35-4095-5e54-2cee-e12bb01e7e57.png)

これをダッシュボードに多数存在するウィジェット毎に指定して回るのが面倒なのと、コード管理していてもウィジェット毎にミスコンフィギュレーションが起こりやすいという課題がありました。

# どうするか
今回はDatadogが提供しているCategory Processorというものを使って、ログに新たにフィールドを追加するアプローチを試しました。
（ログはDatadogに流しているCloudWatchログを利用して以降の作業をしましたが、流し込み方については本記事では触れません。）

行った作業としては非常にシンプルで、以下3つです。

1. Pipelineの追加
1. Category Processorの追加
1. ダッシュボードに存在するウィジェットのクエリ定義修正

## 1.Pipelineの追加
まずは以下のようにPipelineを追加します。

Datadogにログを送るとまず最初にパイプラインを通るのですが、ここで様々な処理を加えることが可能です。

**Logs** > **Configuration** > **PIPELINES**の**Add a new pipeline**から新しくパイプラインを追加します。

ひとまず以下を指定しましたが、複数AWSアカウントや複数アプリケーションのログを連携している場合はパイプライン側でアカウントや対象アプリケーションログなどを絞ると良さそうです。（後続の2で設定するプロセッサー側では絞れなかったため）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413013/2745cd73-c898-b2fc-39bb-a6e4f44e00c2.png)

## 2.Category Processorの追加

追加したパイプライン対して**Add Processor**からCategory Processorを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413013/0432f2b4-f060-6d8a-781d-6ce684c365ed.png)

この設定により、MATCHING QUERYで指定した条件に一致したログには③Set target category attribuuteで指定したアトリビュートが追加されます。

▼例（大分省略しています）

`categorized_path`というアトリビュートが追加されています

```json

{
	"id": "*********",
	"content": {
		"timestamp": "2022-12-16T12:21:57.379Z",
		"tags": [
			"source:cloudwatch",
			"forwardername:datadog-log-for-forwarder",
			"sourcecategory:aws",
		],
		"host": "/eks/test/app-server",
		"service": "cloudwatch",
		"attributes": {
			"duration": 1,
			"path": "/measurements/abcdefg/succeeded",
			"categorized_path": "/measurements/id/succeeded",
			"code": 200,
			"time": "2022-12-16T12:22:06.063435255Z"
		}
	}
}
```

## 3.ダッシュボードに存在するウィジェットのクエリ定義修正

最終的にはダッシュボードのウィジェットでのクエリ定義を楽にしたいので、追加されたアトリビュートでgroup by指定をします。

![image (37).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413013/4fdc0e0c-e359-fcba-5b30-c56c2729f6c4.png)

最終的には、以下の様に追加されたアトリビュートの値で表示されるようになります。
![スクリーンショット 2022-12-16 午後9.49.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413013/79fea8e0-4646-8f19-6304-b05d7ebcbb9a.png)


# おまけ： 1~3の作業をTerraformで書くとどうなるか

実はモニターやダッシュボードはTerraformでコード化してきているのですが、この辺の設定はコード化できていなかったので試しにやってみました。

リソースは主に[datadog_logs_custom_pipeline (Resource)](https://registry.terraform.io/providers/DataDog/datadog/latest/docs/resources/logs_custom_pipeline)を使うと良さそうです。

パス表記の/test/*/succeededでスラッシュをエスケープするためのバックスラッシュをさらにエスケープしなければならなかったのですが、そのことに気づくまでに少々詰まってしまいました。

```tf
terraform {
  required_providers {
    datadog = {
      source  = "DataDog/datadog"
      version = "3.18.0"
    }
  }
}

# ---- omit

resource "datadog_logs_custom_pipeline" "AWS_CloudWatch_AppServer" {
  filter {
    query = "source:cloudwatch"
  }
  name       = "AWS CloudWatch App Server"
  is_enabled = true
  processor {
    category_processor {
      target = "category_processor_path"
      category {
        name = "/test/id/succeeded"
        filter {
          query = "@path:\\/test\\/*\\/succeeded"
        }
      }
      category {
        name = "/test/id/failed"
        filter {
          query = "@path:\\/test\\/*\\/failed"
        }
      }
      name       = "category processor path"
      is_enabled = true
    }
  }
}

# Using data source to set pipeline order
resource "datadog_logs_pipeline_order" "lpo" {
  name = "lpo"
  pipelines = [
    datadog_logs_integration_pipeline.AWS_ELB_Access.id,
    datadog_logs_integration_pipeline.AWS_Lambda.id,
    datadog_logs_custom_pipeline.AWS_CloudWatch_AppServer.id #ここも追加する
  ]
}

```

# おわりに
ウィジェット毎にパスを指定する苦行は免れましたが、APIが増えた時にCategory Processorにパスを追加する作業は依然として残りそうです。なので、やはりCategory Processorは対象が少ない場合には使いやすくて良さそうです。

Datadogでは他にもGrok Parserが提供されており、こちらを使えばその問題を解消できるのではと思っているので、時間を見つけてまた検証してみたいと思います。

とはいえこれまでCatgory Processorは使ったことがなかったので、そういう意味では良い機会になりました。

# 参考
https://www.datadoghq.com/ja/blog/how-to-categorize-logs/#categorize-logs-based-on-url-patterns

https://registry.terraform.io/providers/DataDog/datadog/latest/docs/resources/logs_custom_pipeline