すでにいろいろと記事も出ているが自分で触って試してみたため残しておく
やったことはLogging -> 集約シンク -> Pub/Sub -> Functions -> Slack
でWARNING以上のログをSlackに通知する
集約シンク
AWSでいうCloudWatchLogsのロググループに対するkinesis data firehose
と同じような位置付けかな
GCS、BigQuery、Pub/SubへのExportが可能
ログのフィルタ対象が複数あり組織、フォルダ、プロジェクトの順番で対象が狭まっていく
なのでプロジェクトをまたいだログを対象とすることもできる
今回はプロジェクトに対してのフィルタリングで行う
クエリ
クエリの詳細に関しては下記のドキュメント参照
まずはLoggingでクエリして対象のログをフィルタリングする
下記はWorkflowsの画面からLoggingへのリンクを踏んだときのクエリ内容
(protoPayload.resourceName="projects/sample-project-111111/locations/us-central1/workflows/log-output" resource.type="audited_resource" resource.labels.service=("workflowexecutions.googleapis.com" OR "workflows.googleapis.com")) OR (resource.type="workflows.googleapis.com/Workflow" resource.labels.workflow_id="log-output" resource.labels.location="us-central1") severity>=DEFAULT
Workflowsのログクエリ
直近でWorkflowsの素振りをしていたのでWorkflowsのログを絞る
resource.type = workflows.googleapis.com/Workflow AND severity >= WARNING
Terraformで作成する
せっかくなのでTerraformで書いてみる
resource "google_pubsub_topic" "logging" { name = "sample-log-topic" } resource "google_logging_project_sink" "sample_sink" { name = "to-pubsub" destination = "pubsub.googleapis.com/projects/${data.google_client_config.current.project}/topics/${google_pubsub_topic.logging.name}" filter = "resource.type = workflows.googleapis.com/Workflow AND severity >= WARNING" unique_writer_identity = true }
これだけで実現できるかと思ったがダメだった
- Pub/Sub発行
試しに手動でpublishしてみる
gcloud pubsub topics publish sample-log-topic --message='{"eee":"eee"}'
これは普通にSlackに通知が来た
とういことでシンク→Pub/Subへ送る部分が問題ありそう
また権限周りだろうということで調べてみる
集約シンクのサービスアカウント
GUIから確認するとログルーター
→ ログルーターのシンク
→ シンクの詳細
で書き込みIDというものがある
Terraformでunique_writer_identity
をtrue
にすると専用のサービスアカウントが発行される
こんな感じ
serviceAccount:p111111111111-222222@gcp-sa-logging.iam.gserviceaccount.com
BigQueryをdestinationにする場合、プロジェクト間でログを共有する場合などはこの設定をtrueにすると必要があるとのこと
で、これがログの書き込み時に使用されるサービスアカウントなので必要な権限を付け加えればPub/Subへ通知が行くようになるはず
- Pub/Subパブリッシャー
- ログ書き込み
の権限を付ける
集約シンクのサービスアカウントはTerraform上だとgoogle_logging_project_sink.${name}.writer_identiry
で参照できるので次のようにして権限を付与する
locals { sa_roles = [ "roles/pubsub.publisher", "roles/logging.logWriter", ] } resource "google_project_iam_member" "service_account_role" { count = length(local.sa_roles) role = element(local.sa_roles, count.index) member = google_logging_project_sink.sample_sink.writer_identity }
これで無事Pub/Subにメッセージが流れるようになった
Functionsに流れるパラメータ
無事Functionsまでメッセージが流れるようになったので通知部分を実装する
とりあえずPub/Subへ来たパラメータを JSON.stringifyでそのまま送ったら次のようなテキストが出力される
{"insertId":"4g9clwfy0qa0s","labels":{"revision_id":"000001-4d1"},"logName":"projects/sample-project-111111/logs/Workflows","receiveTimestamp":"2021-04-04T07:57:29.592289242Z","resource":{"labels":{"location":"us-central1","resource_container":"612153881425","workflow_id":"log-output"},"type":"workflows.googleapis.com/Workflow"},"severity":"CRITICAL","textPayload":"ERROR {\"body\":{\"completed\":false,\"id\":1,\"title\":\"delectus aut autem\",\"userId\":1},\"code\":200,\"headers\":{\"Access-Control-Allow-Credentials\":\"true\",\"Age\":\"6303\".......,"timestamp":"2021-04-04T07:57:29.592289242Z"}
あとはこの辺をよしなにやってあげればよい
SlackのAttachment周りはこだわりだすと時間が…
とりあえずで下記のようなスクリプトを書いてFunctionsにデプロイした
- index.js
const { IncomingWebhook } = require('@slack/webhook'); exports.slackNotification = async (event, context) => { const data = Buffer.from(event.data, 'base64').toString(); const params = JSON.parse(data); const url = process.env.SLACK_WEBHOOK_URL; const webhook = new IncomingWebhook(url); const colors = { INFO: '#2EB886', WARNING: '#DAA038', CRITICAL: '#A30100', } const color = colors[params.severity]; await webhook.send({ attachments: [ { color: color, blocks: [ { type: "section", text: { type: "mrkdwn", text: `*LogName: ${params.logName}*` } }, { type: "divider" }, { type: "section", text: { type: "mrkdwn", text: `*Labels: ${JSON.stringify(params.resource.labels)}*`, } }, { type: "section", text: { type: "mrkdwn", text: `\`\`\`${params.textPayload}\`\`\`` } } ] } ] }); };
無事通知がされた
リポジトリ
今回のサンプルは下記にFunctions付きで置いた
terraform-sample/google/logging-to-slack-notification at master · swfz/terraform-sample
所感
今回はWorkflowsしか使っていなかったのでresource.type
で指定したが
個人でやるならある程度のカバー範囲あったほうが良いなと思っていたりするのでresource.type
以外でもフィルタリングできたほうが良いかなと感じた
なにはともあれサクッと疎通させるところまでは確認できた
調べる中でとりあえずBigQueryへにエクスポートしておけば後で調査しやすそうだなと思ったので積極的に使っていきたい
ドキュメントはこの辺