自动获取文件名
func todayFilename() string {
currentPath, _ := filepath.Abs(filepath.Dir(os.Args[0]))
path := currentPath + "/logs/" + time.Now().Format("2006-01")
_ = os.MkdirAll(path, os.ModePerm)
day := time.Now().Format("02")
return path + "/" + day + ".log"
}
获取文件对象
func newLogFile() *os.File {
filename := todayFilename()
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("error")
}
return file
}
中间件
func newRequestLoggerForGin() (f gin.HandlerFunc, close func() error) {
logFile := newLogFile()
logFileLock := sync.RWMutex{}
info, _ := logFile.Stat()
logFileCreatTime := info.ModTime()
//实例化
logger := logrus.New()
//设置输出
logger.Out = logFile
//设置日志级别
logger.SetLevel(logrus.DebugLevel)
//设置日志格式
logger.SetFormatter(&logrus.TextFormatter{
TimestampFormat: "2006/01/02 - 15:04:05.000000",
})
close = func() error {
err := logger.Writer().Close()
return err
}
f = func(c *gin.Context) {
// 开始时间
startTime := time.Now()
// 处理请求
c.Next()
// 结束时间
endTime := time.Now()
// 执行时间
latencyTime := endTime.Sub(startTime)
// 请求方式
reqMethod := c.Request.Method
//不记录OPTION方法访问
switch reqMethod {
case "OPTIONS":
return
}
// 请求路由
reqUri := c.Request.RequestURI
// 状态码
statusCode := c.Writer.Status()
// 请求IP
// 若被nginx代理,则应在X-Real-IP中获取真实访问IP
clientIP := c.GetHeader("X-Real-IP")
if clientIP == "" {
clientIP = c.ClientIP()
}
// clientIP := c.ClientIP()
// realIP := c.GetHeader("X-Real-IP")
//更新日志文件
if logFileCreatTime.Day() != startTime.Day() ||
logFileCreatTime.Month() != startTime.Month() ||
logFileCreatTime.Year() != startTime.Year() {
logFileLock.Lock()
//关闭旧文件
_ = logFile.Close()
//创建新文件
logFile = newLogFile()
//更新时间戳
info, _ := logFile.Stat()
logFileCreatTime = info.ModTime()
logFileLock.Unlock()
}
//日志格式
//自带互斥锁不需要加锁
logger.Infof("| %3d | %13v | %15s | %s | %s |",
statusCode,
latencyTime,
clientIP,
reqMethod,
reqUri,
)
}
return
}
注意
logrus本身就带有互斥锁,写日志时是并发安全的,但是要在生成新日志时加锁。
gin本身自带有日志组件,修改设置可以直接输出到特定文件,但我个人还是喜欢用logrus来写日志文件