middleware.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Package mhayaGin from https://github.com/gin-contrib/zap/
  2. package mhayaGin
  3. import (
  4. csync "github.com/mhaya/extend/sync"
  5. clog "github.com/mhaya/logger"
  6. "net"
  7. "net/http"
  8. "net/http/httputil"
  9. "os"
  10. "runtime/debug"
  11. "strings"
  12. "time"
  13. )
  14. func GinDefaultZap() GinHandlerFunc {
  15. return GinZap(time.RFC3339, true)
  16. }
  17. // GinZap returns a gin.HandlerFunc (middleware) that logs requests using uber-go/zap.
  18. //
  19. // Requests with errors are logged using zap.Error().
  20. // Requests without errors are logged using zap.Info().
  21. //
  22. // It receives:
  23. // 1. A time package format string (e.g. time.RFC3339).
  24. // 2. A boolean stating whether to use UTC time zone or local.
  25. func GinZap(timeFormat string, utc bool) GinHandlerFunc {
  26. return func(c *Context) {
  27. start := time.Now()
  28. // some evil middlewares modify this values
  29. path := c.Request.URL.Path
  30. query := c.Request.URL.RawQuery
  31. c.Next()
  32. end := time.Now()
  33. latency := end.Sub(start)
  34. if utc {
  35. end = end.UTC()
  36. }
  37. if len(c.Errors) > 0 {
  38. // Append error field if this is an erroneous request.
  39. for _, e := range c.Errors.Errors() {
  40. clog.Error(e)
  41. }
  42. } else {
  43. clog.Debugw(c.FullPath(),
  44. "status", c.Writer.Status(),
  45. "method", c.Request.Method,
  46. "path", path,
  47. "query", query,
  48. "ip", c.ClientIP(),
  49. "user-agent", c.Request.UserAgent(),
  50. "time", end.Format(timeFormat),
  51. "latency", latency,
  52. )
  53. }
  54. }
  55. }
  56. // RecoveryWithZap returns a gin.HandlerFunc (middleware)
  57. // that recovers from any panics and logs requests using uber-go/zap.
  58. // All errors are logged using zap.Error().
  59. // stack means whether output the stack info.
  60. // The stack info is easy to find where the error occurs but the stack info is too large.
  61. func RecoveryWithZap(stack bool) GinHandlerFunc {
  62. return func(c *Context) {
  63. defer func() {
  64. if err := recover(); err != nil {
  65. // Check for a broken connection, as it is not really a
  66. // condition that warrants a panic stack trace.
  67. var brokenPipe bool
  68. if ne, ok := err.(*net.OpError); ok {
  69. if se, ok := ne.Err.(*os.SyscallError); ok {
  70. if strings.Contains(strings.ToLower(se.Error()), "broken pipe") ||
  71. strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
  72. brokenPipe = true
  73. }
  74. }
  75. }
  76. httpRequest, _ := httputil.DumpRequest(c.Request, false)
  77. if brokenPipe {
  78. clog.Warnw(c.Request.URL.Path,
  79. "error", err,
  80. "request", string(httpRequest),
  81. )
  82. // If the connection is dead, we can't write a status to it.
  83. c.Error(err.(error)) // nolint: errcheck
  84. c.Abort()
  85. return
  86. }
  87. if stack {
  88. clog.Warnw("[Recovery from panic]",
  89. "time", time.Now(),
  90. "error", err,
  91. "request", string(httpRequest),
  92. "stack", string(debug.Stack()),
  93. )
  94. } else {
  95. clog.Warnw("[Recovery from panic]",
  96. "time", time.Now(),
  97. "error", err,
  98. "request", string(httpRequest),
  99. )
  100. }
  101. c.AbortWithStatus(http.StatusInternalServerError)
  102. }
  103. }()
  104. c.Next()
  105. }
  106. }
  107. func Cors(domain ...string) GinHandlerFunc {
  108. return func(c *Context) {
  109. method := c.Request.Method
  110. if len(domain) > 0 {
  111. c.Header("Access-Control-Allow-Origin", domain[0])
  112. } else {
  113. c.Header("Access-Control-Allow-Origin", "*")
  114. }
  115. c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
  116. c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
  117. c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
  118. c.Header("Access-Control-Allow-Credentials", "true")
  119. if method == "OPTIONS" {
  120. c.AbortWithStatus(http.StatusNoContent)
  121. }
  122. c.Next()
  123. }
  124. }
  125. // MaxConnect limit max connect
  126. func MaxConnect(n int) GinHandlerFunc {
  127. latch := csync.NewLimit(n)
  128. return func(c *Context) {
  129. if latch.TryBorrow() {
  130. defer func() {
  131. if err := latch.Return(); err != nil {
  132. clog.Warn(err)
  133. }
  134. }()
  135. c.Next()
  136. } else {
  137. clog.Warnf("limit = %d, service unavailable. url = %s", n, c.Request.RequestURI)
  138. c.AbortWithStatus(http.StatusServiceUnavailable)
  139. }
  140. }
  141. }