midware.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/gin-gonic/gin"
  6. "io/ioutil"
  7. "net/http"
  8. "net/http/httputil"
  9. "runtime"
  10. "time"
  11. )
  12. var (
  13. crmchr_dunno = []byte("???")
  14. crmchr_centerDot = []byte("·")
  15. crmchr_dot = []byte(".")
  16. crmchr_slash = []byte("/")
  17. )
  18. func CustomAccessLogMidware(alogger *AccessLogger, elogger *CommonLogger) gin.HandlerFunc {
  19. return func(context *gin.Context) {
  20. start := time.Now()
  21. path := context.Request.URL.Path
  22. raw := context.Request.URL.RawQuery
  23. context.Next()
  24. end := time.Now()
  25. latency := end.Sub(start)
  26. clientIP := context.ClientIP()
  27. method := context.Request.Method
  28. statusCode := context.Writer.Status()
  29. comment := context.Errors.ByType(gin.ErrorTypePrivate).String()
  30. if raw != "" {
  31. path = path + "?" + raw
  32. }
  33. alogger.Emit(start, statusCode, latency, clientIP, method, path)
  34. if comment != "" {
  35. elogger.EmitF(LogLv_WARN, "WebContext", "Web Context Error Info: %s", comment)
  36. }
  37. }
  38. }
  39. func CustomRecoveryMidware(logger *CommonLogger, isDebug bool) gin.HandlerFunc {
  40. return func(context *gin.Context) {
  41. defer func() {
  42. if err := recover(); err != nil {
  43. if logger != nil {
  44. stack := stack(3)
  45. if isDebug {
  46. httprequest, _ := httputil.DumpRequest(context.Request, false)
  47. msg := fmt.Sprintf("Panic recovered from error '%s':\n%s\n%s", err, string(httprequest), stack)
  48. logger.Emit(LogLv_PANIC, "Recovery", msg)
  49. } else {
  50. msg := fmt.Sprintf("Panic recovered from error '%s':\n%s", err, stack)
  51. logger.Emit(LogLv_PANIC, "Recovery", msg)
  52. }
  53. }
  54. context.AbortWithStatus(http.StatusInternalServerError)
  55. }
  56. }()
  57. context.Next()
  58. }
  59. }
  60. // stack returns a nicely formatted stack frame, skipping skip frames.
  61. func stack(skip int) []byte {
  62. buf := new(bytes.Buffer) // the returned data
  63. // As we loop, we open files and read them. These variables record the currently
  64. // loaded file.
  65. var lines [][]byte
  66. var lastFile string
  67. for i := skip; ; i++ { // Skip the expected number of frames
  68. pc, file, line, ok := runtime.Caller(i)
  69. if !ok {
  70. break
  71. }
  72. // Print this much at least. If we can't find the source, it won't show.
  73. fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
  74. if file != lastFile {
  75. data, err := ioutil.ReadFile(file)
  76. if err != nil {
  77. continue
  78. }
  79. lines = bytes.Split(data, []byte{'\n'})
  80. lastFile = file
  81. }
  82. fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
  83. }
  84. return buf.Bytes()
  85. }
  86. // source returns a space-trimmed slice of the n'th line.
  87. func source(lines [][]byte, n int) []byte {
  88. n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
  89. if n < 0 || n >= len(lines) {
  90. return crmchr_dunno
  91. }
  92. return bytes.TrimSpace(lines[n])
  93. }
  94. // function returns, if possible, the name of the function containing the PC.
  95. func function(pc uintptr) []byte {
  96. fn := runtime.FuncForPC(pc)
  97. if fn == nil {
  98. return crmchr_dunno
  99. }
  100. name := []byte(fn.Name())
  101. // The name includes the path name to the package, which is unnecessary
  102. // since the file name is already included. Plus, it has center dots.
  103. // That is, we see
  104. // runtime/debug.*T·ptrmethod
  105. // and want
  106. // *T.ptrmethod
  107. // Also the package path might contains dot (e.g. code.google.com/...),
  108. // so first eliminate the path prefix
  109. if lastslash := bytes.LastIndex(name, crmchr_slash); lastslash >= 0 {
  110. name = name[lastslash+1:]
  111. }
  112. if period := bytes.Index(name, crmchr_dot); period >= 0 {
  113. name = name[period+1:]
  114. }
  115. name = bytes.Replace(name, crmchr_centerDot, crmchr_dot, -1)
  116. return name
  117. }