package logger_util import ( "bytes" "fmt" "log" "os" "path" "runtime" formatter "github.com/antonfisher/nested-logrus-formatter" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) var(//const ( maxLogFileSize int64= 1024*1024*128 maxLogFile = 8 ) func SetLoggerSizeAndNum(size, num int) { maxLogFileSize = 1024*1024*int64(size) maxLogFile = num } type FileHook struct { file *os.File flag int chmod os.FileMode formatter *formatter.Formatter } func renameFiles(file string) { var oldFileName, newFileName string var i int for i = maxLogFile-1; i > 0; i-- { if i > 1 { oldFileName = fmt.Sprintf("%s.%d",file, i-1) } else { oldFileName = file } if _, err := os.Stat(oldFileName); err == nil { newFileName = fmt.Sprintf("%s.%d", file, i) os.Rename(oldFileName, newFileName) } } } func NewFileHook(file string, flag int, chmod os.FileMode) (*FileHook, error) { plainFormatter := &formatter.Formatter{ TimestampFormat: "2006-01-02 15:04:05.000", TrimMessages: true, NoColors: true, NoFieldsColors: true, NoFieldsSpace: true, HideKeys: true, FieldsOrder: []string{"component", "category", "remote_addr"}, CallerFirst: true, CustomCallerFormatter: func(f *runtime.Frame) string { file := path.Base(f.File) if len(file) > 25 { file = file[len(file)-25:] } return fmt.Sprintf(" %15s:%04d", file, f.Line) }, } logFile, err := os.OpenFile(file, flag, chmod) if err != nil { fmt.Fprintf(os.Stderr, "unable to write file on filehook %v", err) return nil, err } return &FileHook{logFile, flag, chmod, plainFormatter}, err } // Fire event func (hook *FileHook) Fire(entry *logrus.Entry) error { var line string info, err := hook.file.Stat() if err != nil { return err } if info.Size() > maxLogFileSize { fileInfo, err := os.Stat(hook.file.Name()) if err != nil { return err } if fileInfo.Size() > maxLogFileSize { renameFiles(hook.file.Name()) } hook.file.Close() if hook.file, err = os.OpenFile(hook.file.Name(), hook.flag, hook.chmod); err != nil { fmt.Fprintf(os.Stderr, "unable to write file on filehook %v", err) return err } } if plainformat, err := hook.formatter.Format(entry); err != nil { log.Printf("Formatter error: %+v", err) return err } else { line = string(plainformat) } if _, err := hook.file.WriteString(line); err != nil { fmt.Fprintf(os.Stderr, "unable to write file on filehook(entry.String)%v", err) return err } return nil } func (hook *FileHook) Levels() []logrus.Level { return []logrus.Level{ logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, logrus.WarnLevel, logrus.InfoLevel, logrus.DebugLevel, } } // CustomResponseWriter type CustomResponseWriter struct { gin.ResponseWriter body *bytes.Buffer } func (w CustomResponseWriter) Write(b []byte) (int, error) { w.body.Write(b) return w.ResponseWriter.Write(b) } func (w CustomResponseWriter) WriteString(s string) (int, error) { w.body.WriteString(s) return w.ResponseWriter.WriteString(s) } // //The Middleware will write the Gin logs to logrus. func ginToLogrus(log *logrus.Entry) gin.HandlerFunc { return func(c *gin.Context) { path := c.Request.URL.Path raw := c.Request.URL.RawQuery blw := &CustomResponseWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer} c.Writer = blw // // Process request c.Next() clientIP := c.ClientIP() method := c.Request.Method statusCode := c.Writer.Status() errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String() if raw != "" { path = path + "?" + raw } log.Infof("| %3d | %15s | %-7s | %s | %s", statusCode, clientIP, method, path, errorMessage) } } //NewGinWithLogrus - returns an Engine instance with the ginToLogrus and Recovery middleware already attached. func NewGinWithLogrus(log *logrus.Entry) *gin.Engine { engine := gin.New() engine.Use(ginToLogrus(log), gin.Recovery()) return engine }