【Golang】Sentry (APMツール) の導入

# 概要
GoのAPIプロジェクト上に、Sentry(APMツール)を導入します。

# 環境構築
## 前提
Goプロジェクト上で動作するため、環境を用意します。
```zsh:shell
go mod init
```

## アカウント作成
https://sentry.io/welcome/

ホームページよりSentryのアカウントを作成します。
今回はTRIALで進めます。

## Welcome to Sentry
今回は、SDKを使用するため「Install Sentry」を選択します。
![スクリーンショット 2023-01-30 17.36.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/51b29a7a-809b-b5f9-c663-f864b3093b83.png)


## Select the platforms you want to monitor
Goを選択します。
![スクリーンショット 2023-01-30 17.37.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/8a717582-00bc-a64d-b82f-7166762f92a6.png)


## Prepare the Go SDK
SDKをインストールします。
```zsh:shell
go get github.com/getsentry/sentry-go
```

動作確認を行うため、下部のコードを`main.go`を作成し記述します。
Dsnは各自のURLを記述してください。

```go:main.go
package main

import (
	"log"

	"github.com/getsentry/sentry-go"
)

func main() {
	err := sentry.Init(sentry.ClientOptions{
		Dsn: "https://",
		// Set TracesSampleRate to 1.0 to capture 100%
		// of transactions for performance monitoring.
		// We recommend adjusting this value in production,
		TracesSampleRate: 1.0,
	})
	if err != nil {
		log.Fatalf("sentry.Init: %s", err)
	}
	// Flush buffered events before the program terminates.
	defer sentry.Flush(2 * time.Second)

	sentry.CaptureMessage("It works!")
}
```
![スクリーンショット 2023-01-30 18.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/c28d8b59-6304-746e-0e26-4f223fb524ba.png)

## 接続確認
先程の`main.go`を実行します。
```zsh:shell
go run main
```

IssuesにてCaptureMessageが確認できます。
![スクリーンショット 2023-01-31 15.42.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/71873036-b9c4-12b5-0326-7e3801d7ad10.png)

## APM環境構築
APIのトランザクションを監視するため、先程の`main.go`にWebサーバー部分を追記します。
今回はGoの標準ライブラリである `net/http` を使用します。

https://docs.sentry.io/platforms/go/guides/http/

ドキュメント通りに進めます。

`net/http`用のSDKをインストールします。
```zsh:shell
go get github.com/getsentry/sentry-go/http
```

ドキュメントのUsageを参考にコードに追記し、実行します。
```go:main.go
package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/getsentry/sentry-go"
	sentryhttp "github.com/getsentry/sentry-go/http"
)

type handler struct{}

func (h *handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	if hub := sentry.GetHubFromContext(r.Context()); hub != nil {
		hub.WithScope(func(scope *sentry.Scope) {
			scope.SetExtra("unwantedQuery", "someQueryDataMaybe")
			hub.CaptureMessage("User provided unwanted query string, but we recovered just fine")
		})
	}
	rw.WriteHeader(http.StatusOK)
}

func enhanceSentryEvent(handler http.HandlerFunc) http.HandlerFunc {
	return func(rw http.ResponseWriter, r *http.Request) {
		if hub := sentry.GetHubFromContext(r.Context()); hub != nil {
			hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt")
		}
		handler(rw, r)
	}
}

func main() {
	err := sentry.Init(sentry.ClientOptions{
		Dsn: "https://",
        EnableTracing: true,
		// Set TracesSampleRate to 1.0 to capture 100%
		// of transactions for performance monitoring.
		// We recommend adjusting this value in production,
		TracesSampleRate: 1.0,
	})
	if err != nil {
		log.Fatalf("sentry.Init: %s", err)
	}
	// Flush buffered events before the program terminates.
	defer sentry.Flush(2 * time.Second)

	sentry.CaptureMessage("It works!")

	sentryHandler := sentryhttp.New(sentryhttp.Options{
		Repanic: true,
	})

	http.Handle("/", sentryHandler.Handle(&handler{}))
	http.HandleFunc("/foo", sentryHandler.HandleFunc(
		enhanceSentryEvent(func(rw http.ResponseWriter, r *http.Request) {
			panic("y tho")
		}),
	))

	fmt.Println("Listening and serving HTTP on :3000")

	if err := http.ListenAndServe(":3000", nil); err != nil {
		panic(err)
	}
```

実行後、トランザクションを確認したため、それぞれにアクセスします。

http://localhost:3000

http://localhost:3000/foo

# 監視
リクエストの詳細,パフォーマンスはPerformanceにて確認できます。

Performanceでは、自分が監視できるすべてのリクエストを一覧として見ることができます。
各エンドポイントの詳細を見たい場合は、画面下部から選択します。
![スクリーンショット 2023-01-31 19.11.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/de78220c-5506-f97a-f9f9-d1d70d65caef.png)

## Transaction Summary
各エンドポイントのごとの詳細を確認できます。
リクエスト単位で詳細を見たい場合は、EVENTを選択します。

![スクリーンショット 2023-01-31 19.13.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/501d4a80-0183-2b02-4b4f-07c7a29af8cd.png)

## Event Details

リクエストの詳細を見ることができます。
ボトルネックとなっている箇所に関して、炎のマークにて確認できます。
今回は、`main.go`のみのAPIでしたが、外部APIやDBを利用するプロジェクトだと、そちらも確認することができます。


![スクリーンショット 2023-01-31 19.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433195/a0f491d3-b99e-b285-9297-e1d5b45b8f7e.png)

# さいごに
Go+Sentryの導入記事が少なかったため、作成しました。
日々の開発のお役に立てればと思います。

これ以外にもSentryは様々な監視を行えます。ぜひ試してみてください!!

(今回は標準ライブラリを使用しましたが、ドキュメントを見る限り`gin`や`echo`と言ったフレームワークにも対応しています。
しかし、接続は可能でしたが、APIのパフォーマンスを監視することができませんでした。
そのため、できた方がいらっしゃいましたら、コメントに教えていただけると幸いです。)