Grafana -> Slack通知のダイエット

<section style="text-align: center;">

![20221021.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920567/c3f812a8-d22b-3f07-fd36-5973bade5008.jpeg "バナー背景は「Dieting notifications from Grafana to Slack」でStableDiffusionさんに発注")

</section>

# 概要

https://grafana.com/docs/grafana/latest/alerting/

デフォルトのテンプレでは過分に情報が含まれますので、後続オペレーションをシンプルにするため、必要な情報のみに抜粋してみました:hatched_chick:

:::note
本記事の趣旨に外れるため、docker等にてGrafana構築済み、ならびに、P8s等のデータソース参照済みで、かつ、Slack 側もWebhook API設定済み前提で話は進みます。あらかじめご了承ください:bow:
:::

<section style="text-align: center;">

![20221021c.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920567/b759a1c5-c0cd-0b12-ae6b-36e240f5c4c1.jpeg)

</section>

# 手順

### 1. デフォルトテンプレートを確認

https://github.com/grafana/grafana/blob/main/pkg/services/ngalert/notifier/channels/default_template.go

いずれも詳細は未確認ですが、Teams向け以外の特別なテンプレは見当たりませんでしたので、恐らくSlackも下記 ``default.title`` と ``default.message`` を利用している模様です。

★タイトル部分
```go
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}

{{ define "default.title" }}{{ template "__subject" . }}{{ end }}
```

★本文（観測データ）部分
```go
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}

{{ define "__text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}{{ if gt (len .GeneratorURL) 0 }}Source: {{ .GeneratorURL }}
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: {{ .SilenceURL }}
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }}
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }}
{{ end }}{{ end }}{{ end }}
```

★本文（定型文）部分
```go
{{ define "default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
```

### 2. カスタムラベル作成

https://grafana.com/docs/grafana/latest/alerting/fundamentals/annotation-label/how-to-use-labels/

本文（観測データ）部分で提示したい情報、具体的には `` $value `` から限定したい情報を、`` Edit alert rule `` 画面の `` Custom Labels `` に登録していきます。

:::note
v9.1.6では項目 ``4 Notifications`` にありますが、バージョンによって異なってくると思われます:bow:
:::

<section style="text-align: center;">

![20221021a.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920567/30a2bcd3-15e2-f621-8c53-aaed3ac80e9e.jpeg)
![20221021b.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920567/ef1cf96a-7098-b92a-729b-1d98d0a35b00.jpeg)

</section>

上記画像例は複合機の用紙残量リマインダーのため、以下の「プリンターのIPアドレス」「用紙残量」「対象トレイ」を設定してみました。

:::note
最上段の `` CP = Soumu `` は後ほど `` Notification policies `` にて通知先の振り分けに利用します。
:::

```go
IP          = {{ $value | reReplaceAll `^.*instance=(.*),\sjob=.*$` " $1 " }}
Paper Stock = {{ $value | reReplaceAll `^.*value=(.*)\s.*$` " $1 " }}
Tray        = {{ $value | reReplaceAll `^.*prtInputIndex=(.*)\}\s.*$` " $1 " }}
```

`` reReplaceAll `` はGoTemplate関数で、第2引数部分を第1引数部分に置き換えます。以下にて（恐らく動作確認済みの）Grafanaで利用できるGoTemplate関数が紹介されていますので、よろしければご参照ください。

https://grafana.com/docs/grafana/latest/alerting/fundamentals/annotation-label/template-functions/

なお、第1引数部分のregexはデータソース元で `` $value `` をどのように格納しているかによって異なってきますので、あくまで一例としてお考え下さい。（`` IP `` を例に挙げると、IPアドレス情報が格納されている `` instance= `` 直後の文字のみを抽出しています。）

### 3. オリジナルテンプレート作成

https://grafana.com/docs/grafana/latest/alerting/contact-points/message-templating/

★タイトル部分
```go
{{ define "titleSlack.text" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }}{{ end }}
```

複合機に関する通知で、対象の通知先へ送付されるものは用紙残量リマインダー1種類のため、タイトルから不要な識別子を除外

★本文（観測データ）部分
```go
{{ define "bodySlack.text" }}{{ range . }}
Scores:
{{ range .Labels.SortedPairs }}{{ if and (ne .Name "alertname") (ne .Name "grafana_folder") (ne .Name "CP") (ne .Name "Paper Stock") }} - {{ .Name }} = {{ .Value }}
{{ end }}{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }}
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }}
{{ end }}{{ if gt (len .Annotations) 0 }}Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}{{ end }}{{ end }}{{ end }}
```

`` Labels `` から不要な情報を and＋not equal条件 `` (ne .Name "hogehoge") `` で次々と除外していきます。

★本文（定型文）部分
```go
{{ define "messageSlackALVIX.text" }}
{{ if gt (len .Alerts.Firing) 0 }}
「RICOH IM C4500」にて用紙残量20%以下のトレイがあります:printer:
:man-bowing:適宜ご確認願います:man-bowing:

◆使用説明書：給紙トレイに用紙をセットする
https://manuals.ricoh.com/mfp/p_manual/IMC6000JPN/ja/intro/int/tray.htm
{{ template "bodySlack.text" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}

{{ end }}{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
:woman-gesturing-ok:「RICOH IM C4500」の用紙残量不足は解消されました:man-gesturing-ok:
ご対応お疲れ様です:beers:
{{ template "bodySlack.text" .Alerts.Resolved }}{{ end }}{{ end }}
```

本例ではITリテラシー習熟が比較的のんびりされている方々への通知ですので、用紙交換方法のリンクを張ったり、絵文字を使ったりしています。

なお、さらっと説明を飛ばしていますが、わざわざ作ったカスタムラベル `` Paper Stock `` も本文（観測データ）部分から省いています。

<section style="text-align: center;">

![20221021d.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920567/e9408f6b-d37b-b387-cd64-40a6cb49c8e8.jpeg)

</section>

Slackの少なく表示する状態では`` Labels (Scores) `` 中2項目しか表示されなかったため、詳細な用紙残量は不明でも、対象機器と対象トレイさえ判別できればアクション可能だと、運用の中で思えたからです。（この記事を書いたことで無駄なラベリングが残っていることに気づくことができました:sweat_drops:）

### 4. 通知先の限定

https://grafana.com/docs/grafana/latest/alerting/notifications/

本項目は、オマケというか、本旨からはズレているのですが、手順2にて作成したカスタムラベルで通知先を制御する件にて、少しお話がございます。

<section style="text-align: center;">

![20221021e.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920567/abe0f9f3-a53a-eb98-8c3c-f7be6f7d47a0.jpeg)

</section>

上記画像の `` Continue matching subsequent sibling nodes `` をOFFにすると、条件に一致する通知がどこへも飛ばなくなります。

英文的に、このチェックが外れていると「対象通知のこの宛先への通知のみは処理し、後続処理はドロップ」なら分かるのですが、「対象通知をドロップし、後続処理は続行」という動きになるのは、英文慣れない私にはとても違和感があります:sweat_drops:

# ご一読ありがとうございます

Grafana v8.x以降から、新フォーマット形式での通知が導入されたそうですが、まだあまり慣れておりません。

https://grafana.com/docs/grafana/latest/alerting/migrating-alerts/

つたない技術力と文章力な内容で誠に恐縮でしたが、最後までお読みいただいたことを、心より感謝申し上げます。