公開日:
Sentryではどのユーザーでエラーが発生したのかを送信することができる。
echoを使ってWebサーバーを立ち上げる構成の場合、Sentryが提供しているsentry-go/echo を使った方が、安全かつ手軽にユーザー情報をSentryに送信できることがわかったので、忘れないようにメモしておく
普段Sentryに通知するメソッドを呼び出すだけでは意識することはないが、SentryにはHubとScopeという概念がある。
https://docs.sentry.io/platforms/go/enriching-events/scopes に詳細な説明がある
ConfigureScope, SetUserメソッド を使うとユーザー情報を送信できる。
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(sentry.User{Email: "sano11o1@example.com"})
})
concurrency に記載があるが、複数の非同期処理で1つのHubに対してScopeを設定する場合、競合が発生する可能性がある。
競合が発生すると、本来エラーが起きたはずのユーザーとは異なるユーザー情報が送信されるので、非常にまずいことになる。
echoは非同期でリクエストを捌くので、例えば以下のようなコードではユーザー情報の競合が発生しうる。
import (
"fmt"
"net/http"
"github.com/getsentry/sentry-go"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
if err := sentry.Init(sentry.ClientOptions{
Dsn: "",
TracesSampleRate: 1.0,
}); err != nil {
fmt.Printf("Sentry initialization failed: %v\n", err)
}
app := echo.New()
app.Use(middleware.Logger()) // JWTからユーザーを特定し、contextにuser_idを格納するMiddlewareと仮定
app.GET("/", func(ctx echo.Context) error {
// ハンドラの様々な処理
// エラーが発生したらユーザー情報と共にSentryに送信
if err != nil {
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(
sentry.User{
ID: c.Value(UserIDKey).(*model.UserID),
}
)
})
sentry.CaptureException(err)
return err
}
return ctx.String(http.StatusOK, "Hello, World!")
})
app.Logger.Fatal(app.Start(":3000"))
concurrency にもあるように、リクエスト毎にHubを複製するのが良い。
sentry-go/echo が提供するMiddlewareは内部的にHubの複製をやってくれる。
具体的には以下のコード でHubを複製していることがわかる。
func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
hub := sentry.GetHubFromContext(ctx.Request().Context())
if hub == nil {
hub = sentry.CurrentHub().Clone()
}
// 後続の処理
}
Middlewareはリクエスト毎に呼ばれるので、複数のリクエストが共通のHubを変更することはない。
Hubはecho.Contextに格納されるので、エラーを通知する処理ではecho.ContextからHubを取り出すことになる。
最終的には以下のようなコードになる。
sentryHub := sentryecho.GetHubFromContext(c)
sentryHub.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(sentry.User{
ID: c.Value(UserIDKey).(*model.UserID),
})
})
sentryHub.CaptureException(err)