Querier

Cloud Loggingで収集したログをCloud Pub/SubとCloud Functionsを使ってSlackに通知する

2022.08.22に公開 | 2022.08.22に更新

Querier運営

@querier_io@querierinc

「Querier(クエリア)」は社内向け管理画面を圧倒的な速さで、かつビジネスのスケールに合わせて柔軟に構築することができるローコードツールです。

管理画面の構築もWeb上で完結
エンジニアのためのローコードツール

Querierについて詳しく見る

みなさんこんにちは、Querier開発チームです。
アプリケーションのエラーを監視したいという要件はどの開発チームでもあると思います。
今回は、

Cloud Logging → ログルーター → Cloud Pub/Sub → Cloud Functions → Slack

という構成でCloud LoggingのエラーログをSlackに通知してみます。

Cloud Loggingでログを収集する

Zapdriverを使うと、Cloud Loggingに最適化したログを残してくれます。

package main

import (
	"log"
	"runtime"

	"github.com/blendle/zapdriver"
	"go.uber.org/zap"
)

var l *zap.Logger

// Zapdriverを使ってLoggerを初期化する
func init() {
	var err error
	l, err = zapdriver.NewProductionWithCore(zapdriver.WrapCore(
		zapdriver.ServiceName("<app_name>"), // 任意のアプリケーション名
	))
	if err != nil {
		log.Fatal(err)
	}
}

// ログを残す
func main() {
	err := fn()
	if err != nil {
		l.Error(
			err.Error(),
			zapdriver.ErrorReport(runtime.Caller(0)),
		)
	}
}

Cloud Loggingのログルーターを介してCloud Pub/Subにシンクする

① GCPコンソールの左タブの「ロギング」→「ログルーター」に行きます。
② 「シンクを作成」を押します。
③ 「シンク名」を入力します。
④ 「シンクの宛先」は「Cloud Pub/Sub トピック」を、「Cloud Pub/Sub トピックを選択」は「新しい Cloud Pub/Sub トピックを作成する」を選択し、新しいトピックを作成します。
⑤ 「シンクに含めるログの選択」では、任意のフィルタを記述します。例として、Cloud Runのエラーログのみを通知する例を下に記しておきます。

severity=ERROR
resource.type="cloud_run_revision"

⑥ 4は一旦省略でOKです

これで、フィルタにマッチするログを認識してCloud Pub/Subのトピックにパブリッシュされます。
以上でログルーターの設定は終了です。また、Cloud Pub/Sub側の設定も特段必要ありません。

Cloud Functionsの関数を記述する

今回はGoで関数を記述してみます。

package function

import (
	"context"
	"encoding/json"

	"github.com/slack-go/slack"
)

// ログルータを介してパブリッシュされたデータが入ってくる構造体
type PubSubMessage struct {
	Data []byte `json:"data"`
}

// Cloud Loggingで収集するデータ構造をGoの構造体として定義する
type ErrLog struct {
	InsertID string `json:"insertId"`
	Resource struct {
		Type   string `json:"type"`
		Labels struct {
			ServiceName       string `json:"service_name"`
			ProjectID         string `json:"project_id"`
			Location          string `json:"location"`
			RevisionName      string `json:"revision_name"`
			ConfigurationName string `json:"configuration_name"`
		} `json:"labels"`
	} `json:"resource"`
	Timestamp string `json:"timestamp"`
	Severity  string `json:"severity"`
	Labels    struct {
		InstanceID string `json:"instanceId"`
	} `json:"labels"`
	LogName          string `json:"logName"`
	ReceiveTimestamp string `json:"receiveTimestamp"`
	JSONPayload struct {
		Timestamp      string `json:"timestamp"`
		Stacktrace     string `json:"stacktrace"`
		Caller         string `json:"caller"`
		ServiceContext struct {
			Service string `json:"service"`
		} `json:"serviceContext"`
		Message string `json:"message"`
	} `json:"jsonPayload"`
	Context struct {
		ReportLocation struct {
			FilePath     string `json:"filePath"`
			FunctionName string `json:"functionName"`
			LineNumber   string `json:"lineNumber"`
		} `json:"reportLocation"`
	} `json:"context"`
	SourceLocation struct {
		File     string `json:"file"`
		Line     string `json:"line"`
		Function string `json:"function"`
	} `json:"sourceLocation"`
}

const webhookURL = ""

func Fn(ctx context.Context, m PubSubMessage) error {
	l := &ErrLog{}
	if err := json.Unmarshal(m.Data, l); err != nil {
		return err
	}

	return slack.PostWebhook(webhookURL, &slack.WebhookMessage{
		Username: "<user_name>",
		Channel:  "<channel_name>",
		Text:  l.JSONPayload.Message,
		Attachments: []slack.Attachment{
			{
				Color: "danger",
				Fields: []slack.AttachmentField{
					{
						Title: "ProjectID",
						Value: l.Resource.Labels.ProjectID,
					},
					{
						Title: "Service",
						Value: l.Resource.Labels.ServiceName,
					},
					{
						Title: "Severity",
						Value: l.Severity,
					},
					{
						Title: "Timestamp",
						Value: l.JSONPayload.Timestamp,
					},
					{
						Title: "Stacktrace",
						Value: l.JSONPayload.Stacktrace,
					},
				},
			},
		},
	})
}

Cloud Buildを使って関数をデプロイする

steps:
- name: 'gcr.io/cloud-builders/gcloud'
  args:
  - functions
  - deploy
  - <Cloud Functionsの名前>
  - --runtime
  - go116
  - --region
  - asia-northeast1
  - --entry-point
  - Fn
  - --trigger-topic
  - <Cloud Pub/Subのトピック名>

以上でエラーログを収集し、Slackに通知をするセッティングは完了です。
Cloud Loggingのみでは、Slackに通知することができず、GCP内の複数のサービスを使うことによって実現することができます。
複数サービスを連携しなければいけないので一見複雑そうに見えますが、それぞれのサービスではそこまで難しいことはやっていないので、ぜひやってみてください!

「Querier(クエリア)」は社内向け管理画面を圧倒的な速さで、かつビジネスのスケールに合わせて柔軟に構築することができるローコードツールです。

最新の記事

バリデーション機能が大幅に強化されました!

みなさんこんにちは、Querier開発チームです。今回のアップデートで各種インプット系コンポーネントのバリデーション機能が大幅に強化されましたので、こちらで解説させていただきます。

more

管理画面の構築もWeb上で完結
エンジニアのためのローコードツール

Querierについて詳しく見る