123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- package main
- import (
- "bytes"
- "fmt"
- "github.com/gin-gonic/gin"
- "io/ioutil"
- "net/http"
- "net/http/httputil"
- "runtime"
- "time"
- )
- var (
- crmchr_dunno = []byte("???")
- crmchr_centerDot = []byte("·")
- crmchr_dot = []byte(".")
- crmchr_slash = []byte("/")
- )
- func CustomAccessLogMidware(alogger *AccessLogger, elogger *CommonLogger) gin.HandlerFunc {
- return func(context *gin.Context) {
- start := time.Now()
- path := context.Request.URL.Path
- raw := context.Request.URL.RawQuery
- context.Next()
- end := time.Now()
- latency := end.Sub(start)
- clientIP := context.ClientIP()
- method := context.Request.Method
- statusCode := context.Writer.Status()
- comment := context.Errors.ByType(gin.ErrorTypePrivate).String()
- if raw != "" {
- path = path + "?" + raw
- }
- alogger.Emit(start, statusCode, latency, clientIP, method, path)
- if comment != "" {
- elogger.EmitF(LogLv_WARN, "WebContext", "Web Context Error Info: %s", comment)
- }
- }
- }
- func CustomRecoveryMidware(logger *CommonLogger, isDebug bool) gin.HandlerFunc {
- return func(context *gin.Context) {
- defer func() {
- if err := recover(); err != nil {
- if logger != nil {
- stack := stack(3)
- if isDebug {
- httprequest, _ := httputil.DumpRequest(context.Request, false)
- msg := fmt.Sprintf("Panic recovered from error '%s':\n%s\n%s", err, string(httprequest), stack)
- logger.Emit(LogLv_PANIC, "Recovery", msg)
- } else {
- msg := fmt.Sprintf("Panic recovered from error '%s':\n%s", err, stack)
- logger.Emit(LogLv_PANIC, "Recovery", msg)
- }
- }
- context.AbortWithStatus(http.StatusInternalServerError)
- }
- }()
- context.Next()
- }
- }
- // stack returns a nicely formatted stack frame, skipping skip frames.
- func stack(skip int) []byte {
- buf := new(bytes.Buffer) // the returned data
- // As we loop, we open files and read them. These variables record the currently
- // loaded file.
- var lines [][]byte
- var lastFile string
- for i := skip; ; i++ { // Skip the expected number of frames
- pc, file, line, ok := runtime.Caller(i)
- if !ok {
- break
- }
- // Print this much at least. If we can't find the source, it won't show.
- fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
- if file != lastFile {
- data, err := ioutil.ReadFile(file)
- if err != nil {
- continue
- }
- lines = bytes.Split(data, []byte{'\n'})
- lastFile = file
- }
- fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
- }
- return buf.Bytes()
- }
- // source returns a space-trimmed slice of the n'th line.
- func source(lines [][]byte, n int) []byte {
- n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
- if n < 0 || n >= len(lines) {
- return crmchr_dunno
- }
- return bytes.TrimSpace(lines[n])
- }
- // function returns, if possible, the name of the function containing the PC.
- func function(pc uintptr) []byte {
- fn := runtime.FuncForPC(pc)
- if fn == nil {
- return crmchr_dunno
- }
- name := []byte(fn.Name())
- // The name includes the path name to the package, which is unnecessary
- // since the file name is already included. Plus, it has center dots.
- // That is, we see
- // runtime/debug.*T·ptrmethod
- // and want
- // *T.ptrmethod
- // Also the package path might contains dot (e.g. code.google.com/...),
- // so first eliminate the path prefix
- if lastslash := bytes.LastIndex(name, crmchr_slash); lastslash >= 0 {
- name = name[lastslash+1:]
- }
- if period := bytes.Index(name, crmchr_dot); period >= 0 {
- name = name[period+1:]
- }
- name = bytes.Replace(name, crmchr_centerDot, crmchr_dot, -1)
- return name
- }
|