golang自带的log可以满足小部分场景,而如果我们需要有个比较完善的日志模块,推介用成熟的框架,这里推介组合:logrus+lfshook+file-rotatelogs。
logrus目前是github点赞最多的日志仓库,达18k,其有丰富的hook,且可以自定义,并且天然兼容log模块;但缺点是不支持日志按文件生成,于是引入file-rotatelogs来支持日志按文件生成,且按天分割;最后引入lfshook来组合二者。
这里不详细介绍logrus的formatter(json和text两种),也不说hook的使用,网上很多资源,这里直接给出最简单的代码。
先安装依赖:
go get github.com/lestrrat-go/file-rotatelogs go get github.com/rifflock/lfshook go get github.com/sirupsen/logrus
这里主要参照lfshook在github上的example,但很显然这个没有维护了,所以上面的example直接使用是会报错的,主要是rotatelogs.New()的时候需要返回writer, err,如下图:
修改后应该如下:
func NewLogger() *logrus.Logger {
filepaths := "./log/project.log"
writer, _ := rotatelogs.New(
filepaths+".%Y%m%d%H%M",
rotatelogs.WithLinkName(filepaths),
rotatelogs.WithMaxAge(time.Duration(604800)*time.Second),
rotatelogs.WithRotationTime(time.Duration(86400)*time.Second),
)
writeMap := lfshook.WriterMap{
logrus.InfoLevel: writer,
logrus.FatalLevel: writer,
logrus.DebugLevel: writer,
logrus.WarnLevel: writer,
logrus.ErrorLevel: writer,
logrus.PanicLevel: writer,
}
Log.SetReportCaller(true)
lfHook := lfshook.NewHook(writeMap, &MyFormatter{})
Log.AddHook(lfHook)
return Log
}
但是只有这些,是没有行号和文件名的;并且其他文件不能引用,行号和文件名的使用参考:https://cloud.tencent.com/developer/article/1830710。详细代码如下:
package logger
import (
"bytes"
"fmt"
"github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"path/filepath"
"time"
)
var Log = logrus.New()
func init() {
Log = NewLogger()
}
type MyFormatter struct {}
func (m *MyFormatter) Format(entry *logrus.Entry) ([]byte, error){
var b *bytes.Buffer
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
timestamp := entry.Time.Format("2006-01-02 15:04:05")
var newLog string
//HasCaller()为true才会有调用信息
if entry.HasCaller() {
fName := filepath.Base(entry.Caller.File)
newLog = fmt.Sprintf("[%s] [%s] [%s:%d %s] %sn",
timestamp, entry.Level, fName, entry.Caller.Line, entry.Caller.Function, entry.Message)
} else{
newLog = fmt.Sprintf("[%s] [%s] %sn", timestamp, entry.Level, entry.Message)
}
b.WriteString(newLog)
return b.Bytes(), nil
}
func NewLogger() *logrus.Logger {
filepaths := "./log/project.log"
writer, _ := rotatelogs.New(
filepaths+".%Y%m%d%H%M",
rotatelogs.WithLinkName(filepaths),
rotatelogs.WithMaxAge(time.Duration(604800)*time.Second),
rotatelogs.WithRotationTime(time.Duration(86400)*time.Second),
)
writeMap := lfshook.WriterMap{
logrus.InfoLevel: writer,
logrus.FatalLevel: writer,
logrus.DebugLevel: writer,
logrus.WarnLevel: writer,
logrus.ErrorLevel: writer,
logrus.PanicLevel: writer,
}
Log.SetReportCaller(true)
lfHook := lfshook.NewHook(writeMap, &MyFormatter{})
Log.AddHook(lfHook)
return Log
}
然后在main里可以这样引用:
package main
import (
"project/logger"
)
func main() {
logger.Log.Info("This is a logger")
logger.Log.Error("is logger")
}
执行go run main.go,会生成log目录和go.log以及go.log.xxx日期后缀。打开文件输出如下:
[2021-10-20 23:04:43] [info] [main.go:8 main.main] This is a logger [2021-10-20 23:04:43] [error] [main.go:9 main.main] is logger
至此则实现了日志记录和日志文件分割。



