Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

99 строки
2.4 KiB

  1. package handlers
  2. import (
  3. "log"
  4. "net/http"
  5. "runtime/debug"
  6. )
  7. // RecoveryHandlerLogger is an interface used by the recovering handler to print logs.
  8. type RecoveryHandlerLogger interface {
  9. Println(...interface{})
  10. }
  11. type recoveryHandler struct {
  12. handler http.Handler
  13. logger RecoveryHandlerLogger
  14. printStack bool
  15. }
  16. // RecoveryOption provides a functional approach to define
  17. // configuration for a handler; such as setting the logging
  18. // whether or not to print stack traces on panic.
  19. type RecoveryOption func(http.Handler)
  20. func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler {
  21. for _, option := range opts {
  22. option(h)
  23. }
  24. return h
  25. }
  26. // RecoveryHandler is HTTP middleware that recovers from a panic,
  27. // logs the panic, writes http.StatusInternalServerError, and
  28. // continues to the next handler.
  29. //
  30. // Example:
  31. //
  32. // r := mux.NewRouter()
  33. // r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  34. // panic("Unexpected error!")
  35. // })
  36. //
  37. // http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
  38. func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
  39. return func(h http.Handler) http.Handler {
  40. r := &recoveryHandler{handler: h}
  41. return parseRecoveryOptions(r, opts...)
  42. }
  43. }
  44. // RecoveryLogger is a functional option to override
  45. // the default logger.
  46. func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption {
  47. return func(h http.Handler) {
  48. r := h.(*recoveryHandler) //nolint:errcheck //TODO:
  49. // @bharat-rajani should return type-assertion error but would break the API?
  50. r.logger = logger
  51. }
  52. }
  53. // PrintRecoveryStack is a functional option to enable
  54. // or disable printing stack traces on panic.
  55. func PrintRecoveryStack(shouldPrint bool) RecoveryOption {
  56. return func(h http.Handler) {
  57. r := h.(*recoveryHandler) //nolint:errcheck //TODO:
  58. // @bharat-rajani should return type-assertion error but would break the API?
  59. r.printStack = shouldPrint
  60. }
  61. }
  62. func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  63. defer func() {
  64. if err := recover(); err != nil {
  65. w.WriteHeader(http.StatusInternalServerError)
  66. h.log(err)
  67. }
  68. }()
  69. h.handler.ServeHTTP(w, req)
  70. }
  71. func (h recoveryHandler) log(v ...interface{}) {
  72. if h.logger != nil {
  73. h.logger.Println(v...)
  74. } else {
  75. log.Println(v...)
  76. }
  77. if h.printStack {
  78. stack := string(debug.Stack())
  79. if h.logger != nil {
  80. h.logger.Println(stack)
  81. } else {
  82. log.Println(stack)
  83. }
  84. }
  85. }