errorswithstack
はGoa v3 のプラグインで、オリジナルのサービスエラーにスタックトレースを追加します。このプラグインは github.com/cockroachdb/errors/withstack
に依存しています。
github.com
プラグインを有効にするには、下記のように errorswithstack を import します。
import (
_ "github.com/tchssk/goaplugins/v3/errorswithstack"
. "goa.design/goa/v3/dsl"
)
コード生成への影響
プラグインを有効化すると goa
ツールの gen
コマンドの挙動が変わります。
gen
コマンドの出力は次のように変更されます:
すべてのエラー初期化用ヘルパー関数は、 WithStackDepth()
を使ってオリジナルのサービスエラーにスタックトレースを追加するよう変更されます。
func MakeInternalError(err error) *goa.ServiceError {
- return goa.NewServiceError(err, "internal_error", false, false, true)
+ return goa.NewServiceError(withstack.WithStackDepth(err, 1), "internal_error", false, false, true)
}
Goa エンドポイントミドルウェアを使ってエラーをキャプチャーすることができます。 GetOneLineSource()
を使って呼び出し元を抽出することができます:
func ErrorLogger(logger *log.Logger) func(goa.Endpoint) goa.Endpoint {
return func(e goa.Endpoint) goa.Endpoint {
return goa.Endpoint(func(ctx context.Context, req any) (any, error) {
res, err := e(ctx, req)
if err != nil {
file, line, _, ok := withstack.GetOneLineSource(err)
if ok {
logger.Printf("%s:%d: %v", file, line, err)
}
}
return res, err
})
}
}
もしくは GetReportableStackTrace()
を使うこともできます;
func ErrorLogger(logger *log.Logger) func(goa.Endpoint) goa.Endpoint {
return func(e goa.Endpoint) goa.Endpoint {
return goa.Endpoint(func(ctx context.Context, req any) (any, error) {
res, err := e(ctx, req)
if err != nil {
if st := withstack.GetReportableStackTrace(errors.Unwrap(err)); st != nil {
if len(st.Frames) >= 1 {
frame := st.Frames[len(st.Frames)-1]
logger.Printf("%s:%d: %v", frame.AbsPath, frame.Lineno, err)
}
}
}
return res, err
})
}
}
エラーの基になる具体的な値は ServiceError
です。型アサーションして条件を作ることもできます:
func ErrorLogger(logger *log.Logger) func(goa.Endpoint) goa.Endpoint {
return func(e goa.Endpoint) goa.Endpoint {
return goa.Endpoint(func(ctx context.Context, req any) (any, error) {
res, err := e(ctx, req)
if err != nil {
if serviceError, ok := err.(*goa.ServiceError); ok {
if serviceError.Fault {
file, line, _, ok := withstack.GetOneLineSource(err)
if ok {
logger.Printf("%s:%d: %v", file, line, err)
}
}
}
}
return res, err
})
}
}
また report.ReportError
を使って Sentry にエラーを送ることもできます:
func ErrorReporter() func(goa.Endpoint) goa.Endpoint {
return func(e goa.Endpoint) goa.Endpoint {
return goa.Endpoint(func(ctx context.Context, req any) (any, error) {
res, err := e(ctx, req)
if err != nil {
report.ReportError(err)
}
return res, err
})
}
}