PagerDuty の Event Orchestration の正規表現でつまずいた

[PagerDuty Advent Calendar](https://qiita.com/advent-calendar/2024/pagerduty) の5日目の記事です。

私は Professional Services Consultant として 9月に PagerDuty に入社しました。

PagerDuty は UI も比較的シンプルで、ドキュメントも整備されており、数ある SaaS の中ではかなり習熟しやすい部類だなと個人的には思っています。とは言え製品習熟にあたってつまずいたポイントがいくつかあり、本日はその中で Event Orchestration の正規表現のお話をしようと思います。

# そもそも Event Orchestration って

具体的にどこでつまずいたかに入る前に、Event Orchestration がどういう機能なのか、簡単に説明します。詳細は以下の記事をご覧いただいた方が良いですが、超ざっくりは、"監視ツールから飛んできたアラート / イベントの内容に応じて、実施するアクションを制御する機能" です。

https://qiita.com/ymotomu/items/0d3ac9a83b4a927aee1c

"実施するアクションを制御" って何… というツッコミが入りそうですが、例えば以下のようなことが自動でできます。
- 起票されるインシデントに対応方法のノートを追記する
- Severity や Urgency を変更する
- インシデントの通知先を変更する
- そもそもインシデント起票しない（＝通知しない）

このいろいろできる Event Orchestration なのですが、正規表現を使ってアラートの中の特定の文字列を取り出して加工する、ということができます。メール形式のアラートを PagerDuty に連携する場合によく使います。

上記の記事の記載を丸パクリですが、例えば PagerDuty に送信するアラートのメールが以下だとすると、
```
Subject: High API auth errors: 2% - prod-apigw-d01
To: (省略)
From: (省略)
cc: 

High API auth errors

Timestamp: 2023-12-30T04:24:15.041Z

Event ID: ABF023995
Auth Error Rate: 27
Server: prod-apigw-d01
Priority: 1

Event: trigger Severity: warning Source: prod-apigw-d01 Category: API Gateway User: N/A
```
正規表現を使って "Server:" や "Severity:" 以降の文字列を抜き出し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/615a0b86-14ea-d518-a3da-16483046d1e3.png)

custom_details というフィールドに入れることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/5ac607d3-2783-6796-33ac-49e8f8473dd0.png)

すると起票されたインシデントに以下のように表示され、見やすくなりますし取り出した値(`event.custom_details.Server` / `event.custom_details.Severity`)を使ったルールを別途書くこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/b69e3ef7-6a46-a3c0-2254-4b85fb80073c.png)

メールは json のように構造化されたデータではないので、メールでアラート連携されるお客様は "正規表現を使って必要な情報を取り出す" ということを非常によく行います。

# 正規表現でつまずいた例
メールアラート連携する場合に非常によく使う正規表現なのですが、「正規表現がマッチしねー」という現象がよく起こります。

例えばメールの中に以下のような文字列があり、
```
Server
Severity
Description
dc03host025
warning
CPU usage exceeds 80%
```

後半3行を取り出して、以下のように custom_details に入れたいとします。
```
"event.custom_details.Server": "dc03host025"
"event.custom_details.Severity": "warning"
"event.custom_details.Description": "CPU usage exceeds 80%"
```

前半3行の文字列 (Server, Severity, Description) が固定という前提なら、以下の正規表現でそれぞれ取り出せるかなぁと思いました。
`Description\n(.*)\n`
`Description\n.*\n(.*)\n`
`Description\n.*\n.*\n(.*)`

https://regex101.com で実験してみるとうまくいきそうです。でも Event Orchestration で動かすとうまくいきませんでした。なんで？？？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/d77aeb70-a75a-0e9e-b755-dcb03bbb650a.png)



# ソリューション
https://regex101.com でまず実験してみるのは良いのですが、Event Orchestration と動作を一致させるには、設定が必要です。具体的には以下です。
- FLAVOR を Golang にする
- REGEX FLAGS は multi line, insensitive, single line を選ぶ

![スクリーンショット 2024-12-12 11.13.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/086ed9cd-c302-8b02-b135-0d4a2027d2fa.png)

この設定をすると、元々の正規表現だと、マッチする範囲が意図からずれていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/bb3a44c8-3e0c-b58b-1ac4-e2bf7891a95b.png)


今回のケースで特に効くのが single line の REGEX FLAG で、このフラグを立てると、`.` は改行含め何でもマッチするようになります。したがって、今回のように一行だけ値を取り出したい場合は、`(.*)` ではなく、`([^\n]*)` がおすすめです。

従って、今回のケースでは、以下のような正規表現になります。(「multi line のフラグ立ててるんだから、`^` と`$`を使えよ」って言われそうですが)
`Description\n([^\n]*)\n`
`Description\n[^\n]*\n([^\n]*)\n`
`Description\n[^\n]*\n[^\n]*\n([^\n]*)`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958432/4d16181c-a2a6-539e-ff1a-df45a9f1fa31.png)

「正規表現がマッチしねー」問題は、この対応で半分くらい(?)解決するかと思います。

# 最後に
実はもう1つハマったポイントがあるのですが、長くなるので今回はここまでとし、第2弾はまた別の機会に投稿します。

なお、本投稿の内容は以下に詳しく記載されているので、ご興味のある方はご参照ください。
https://support.pagerduty.com/main/lang-ja/docs/regular-expressions#test-your-regex