recovery.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. package recovery
  2. import (
  3. "context"
  4. "encoding/json"
  5. "runtime"
  6. "time"
  7. "github.com/go-kratos/kratos/v2/errors"
  8. "github.com/go-kratos/kratos/v2/log"
  9. "github.com/go-kratos/kratos/v2/middleware"
  10. )
  11. // Latency is recovery latency context key
  12. type Latency struct{}
  13. // ErrUnknownRequest is unknown request error.
  14. var ErrUnknownRequest = errors.InternalServer("UNKNOWN", "unknown request error")
  15. // HandlerFunc is recovery handler func.
  16. type HandlerFunc func(ctx context.Context, req, err any) error
  17. // Option is recovery option.
  18. type Option func(*options)
  19. type options struct {
  20. handler HandlerFunc
  21. }
  22. // WithHandler with recovery handler.
  23. func WithHandler(h HandlerFunc) Option {
  24. return func(o *options) {
  25. o.handler = h
  26. }
  27. }
  28. // Recovery is a server middleware that recovers from any panics.
  29. func Recovery(opts ...Option) middleware.Middleware {
  30. op := options{
  31. handler: func(context.Context, any, any) error {
  32. return ErrUnknownRequest
  33. },
  34. }
  35. for _, o := range opts {
  36. o(&op)
  37. }
  38. return func(handler middleware.Handler) middleware.Handler {
  39. return func(ctx context.Context, req any) (reply any, err error) {
  40. startTime := time.Now()
  41. defer func() {
  42. if rerr := recover(); rerr != nil {
  43. buf := make([]byte, 64<<10) //nolint:mnd
  44. n := runtime.Stack(buf, false)
  45. buf = buf[:n]
  46. reqJson, _ := json.Marshal(req)
  47. log.Context(ctx).Errorf("%v: %+v\n%s\n", rerr, string(reqJson), buf)
  48. ctx = context.WithValue(ctx, Latency{}, time.Since(startTime).Seconds())
  49. err = op.handler(ctx, req, rerr)
  50. }
  51. }()
  52. return handler(ctx, req)
  53. }
  54. }
  55. }