notebook

都内でWEB系エンジニアやってます。

CloudLoggingに流れたログをSlack通知する

すでにいろいろと記事も出ているが自分で触って試してみたため残しておく

やったことはLogging -> 集約シンク -> Pub/Sub -> Functions -> Slack

でWARNING以上のログをSlackに通知する

集約シンク

AWSでいうCloudWatchLogsのロググループに対するkinesis data firehoseと同じような位置付けかな

GCS、BigQuery、Pub/SubへのExportが可能

ログのフィルタ対象が複数あり組織、フォルダ、プロジェクトの順番で対象が狭まっていく

なのでプロジェクトをまたいだログを対象とすることもできる

今回はプロジェクトに対してのフィルタリングで行う

クエリ

クエリの詳細に関しては下記のドキュメント参照

Logging のクエリ言語  |  Google Cloud

cloud.google.com

まずは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_identitytrueにすると専用のサービスアカウントが発行される

こんな感じ

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}\`\`\``
            }
          }
        ]
      }
    ]
  });
};

f:id:swfz:20210414194816p:plain

無事通知がされた

リポジトリ

今回のサンプルは下記にFunctions付きで置いた

terraform-sample/google/logging-to-slack-notification at master · swfz/terraform-sample

github.com

所感

今回はWorkflowsしか使っていなかったのでresource.typeで指定したが

個人でやるならある程度のカバー範囲あったほうが良いなと思っていたりするのでresource.type以外でもフィルタリングできたほうが良いかなと感じた

なにはともあれサクッと疎通させるところまでは確認できた

調べる中でとりあえずBigQueryへにエクスポートしておけば後で調査しやすそうだなと思ったので積極的に使っていきたい

ドキュメントはこの辺

エクスポートしたログを使用する  |  Cloud Logging  |  Google Cloud

cloud.google.com