package config
import (
"bufio"
"fmt"
"os"
"path"
"strings"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
)
const FileDir = "./runtime/logs/"
const FileSuffix = ".log"
const TimeFormat = "2006-01-02 15:04:05"
const LogFileNums = 5
type MineFormatter struct{}
func (s *MineFormatter) Format(entry *logrus.Entry) ([]byte, error) {
msg := fmt.Sprintf("[%s] [%s] %s\n", time.Now().Local().Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message)
return []byte(msg), nil
}
var Log = logrus.New()
// 初始化日志
func InitLog() {
/* 禁止日志打印到标准输出stdout */
devnull, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
Log.Panicf("LoggerToFile open os.DevNull failed: %v", err)
}
writernull := bufio.NewWriter(devnull)
Log.SetOutput(writernull)
//设置日志级别
var loglevel logrus.Level
err = loglevel.UnmarshalText([]byte("info"))
if err != nil {
Log.Panicf("设置log级别失败:%v", err)
}
Log.SetLevel(loglevel)
NewSimpleLogger(Log, FileDir, LogFileNums)
}
// 文件日志
func NewSimpleLogger(log *logrus.Logger, logPath string, save uint) {
lfHook := lfshook.NewHook(lfshook.WriterMap{
logrus.DebugLevel: writer(logPath, "debug", save), // 为不同级别设置不同的输出目的
logrus.InfoLevel: writer(logPath, "info", save),
logrus.WarnLevel: writer(logPath, "warn", save),
logrus.ErrorLevel: writer(logPath, "error", save),
logrus.FatalLevel: writer(logPath, "fatal", save),
logrus.PanicLevel: writer(logPath, "panic", save),
}, &MineFormatter{})
log.AddHook(lfHook)
}
/*
*
文件设置
*/
func writer(logPath string, level string, save uint) *rotatelogs.RotateLogs {
logFullPath := path.Join(logPath, level)
var cstSh, _ = time.LoadLocation("Asia/Shanghai") //上海
fileSuffix := time.Now().In(cstSh).Format("2006-01-02") + FileSuffix
loggerWriter, err := rotatelogs.New(
logFullPath+"-"+fileSuffix,
rotatelogs.WithLinkName(logFullPath), // 生成软链,指向最新日志文件
rotatelogs.WithRotationCount(save), // 文件最大保存份数
rotatelogs.WithRotationTime(time.Hour*24), // 日志切割时间间隔
rotatelogs.WithRotationSize(1024*1024), //1M回滚
)
if err != nil {
panic(err)
}
return loggerWriter
}
package main
import (
"gin/config"
)
func main() {
config.InitLog()
for {
config.Log.Info("提示信息")
config.Log.Debug("测试信息")
}
}
/**************************************************************/
failed to rotate: failed to create new symlink: symlink server.log.20210608 server.log.20210608_symlink: A required privilege is not held by the client.
用管理员 CMD 去跑下代码就知道了,此时不会出现报错
日志切割组件将文件新建软链接时候在 Windows 默认环境中会没有这个权限:
https://github.com/golang/go/issues/22874
windows10 在设置中将开发者模式打开即可,因为开发出来的代码一般跑在 Linux, 因此不用担心:
打开后
此时测试,就不见了:
xxx/xxx/log.go
package log
import (
"fmt"
"io"
"os"
"path"
"time"
"github.com/gin-gonic/gin"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
)
var (
logFilePath = "./"
logFileName = "server.log"
rotateLogsFile = "server." + "%Y%m%d" + ".log"
Log *logrus.Logger
)
func LoggerMiddleware() gin.HandlerFunc {
// 日志文件
logFile := path.Join(logFilePath, logFileName)
// 写入文件
src, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("Open log file err:", err)
}
// 实例化
Log = logrus.New()
//设置日志级别
Log.SetLevel(logrus.DebugLevel)
logrus.SetReportCaller(true)
//设置输出
writers := []io.Writer{
src,
os.Stdout}
// Log.Out = src
fileAndStdoutWriter := io.MultiWriter(writers...)
if err == nil {
Log.SetOutput(fileAndStdoutWriter)
} else {
Log.Info("failed to log to file.")
}
// 设置 rotatelogs
logWriter, err := rotatelogs.New(
rotateLogsFile, // 分割后的文件名称
rotatelogs.WithMaxAge(7*24*time.Hour), // 设置最大保存时间(7天)
rotatelogs.WithRotationTime(24*time.Hour), // 设置日志切割时间间隔(1天)
)
if err != nil {
fmt.Printf("Failed to create rotatelogs: %s", err)
}
writeMap := lfshook.WriterMap{
logrus.InfoLevel: logWriter,
logrus.FatalLevel: logWriter,
logrus.DebugLevel: logWriter,
logrus.WarnLevel: logWriter,
logrus.ErrorLevel: logWriter,
logrus.PanicLevel: logWriter,
}
Log.AddHook(lfshook.NewHook(writeMap, &logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05.000",
}))
return func(c *gin.Context) {
startTime := time.Now() //开始时间
c.Next() //处理请求
endTime := time.Now() //结束时间
latencyTime := endTime.Sub(startTime) // 执行时间
reqMethod := c.Request.Method //请求方式
reqUrl := c.Request.RequestURI //请求路由
statusCode := c.Writer.Status() //状态码
remoteIP, _ := c.RemoteIP() //请求IP
// 日志格式
Log.WithFields(logrus.Fields{
"status_code": statusCode,
"latency_time": latencyTime,
"remote_ip": remoteIP,
"req_method": reqMethod,
"req_uri": reqUrl,
}).Info()
}
}
package main
import (
"github.com/gin-gonic/gin"
"xxx/xxx/log"
"net/http"
)
func main() {
router := gin.Default()
router.Use(log.LoggerMiddleware()) // 日志中间件
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello World")
log.Log.Info("测试")
})
router.Run(":8080")
}
很多情况下,自己需要写一些小工具,也需要去记录日志,为此,准备了如下模块:
package main
import (
"fmt"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
log "github.com/sirupsen/logrus"
)
func init() {
serverName := "server"
path := serverName+".log"
rotateLogsFile := serverName+".%Y%m%d%H%M.log" //【测试时】
// 下面配置日志每隔 7 天轮转一个新文件,保留最近 30 天的日志文件,多余的自动清理掉
writer, _ := rotatelogs.New(
rotateLogsFile,
rotatelogs.WithLinkName(path), // 为最新的日志建立软连接
rotatelogs.WithMaxAge(time.Duration(30)*time.Second), // 【测试时】 设置文件清理前的最长保存时间
rotatelogs.WithRotationTime(time.Duration(10)*time.Second), // 【测试时】 设置日志分割的时间,隔多久分割一次
)
log.SetOutput(writer)
//log.SetFormatter(&log.JSONFormatter{})
}
func main() {
for {
log.Info("hello, world!")
time.Sleep(time.Duration(2) * time.Second)
fmt.Println(time.Now().Round(time.Second), "写入日志")
}
}
package main
import (
"fmt"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
log "github.com/sirupsen/logrus"
)
func init() {
serverName := "server"
path := serverName + ".log"
rotateLogsFile := serverName+".%Y%m%d.log"
// 下面配置日志每隔 7 天轮转一个新文件,保留最近 30 天的日志文件,多余的自动清理掉
writer, _ := rotatelogs.New(
rotateLogsFile,
rotatelogs.WithLinkName(path), // 为最新的日志建立软连接
rotatelogs.WithMaxAge(time.Hour*24*30), // 设置文件清理前的最长保存时间
rotatelogs.WithRotationTime(time.Hour*24*7), // 设置日志分割的时间,隔多久分割一次
)
log.SetOutput(writer)
//log.SetFormatter(&log.JSONFormatter{})
}
func main() {
for {
log.Info("hello, world!")
time.Sleep(time.Duration(2) * time.Second)
fmt.Println(time.Now().Round(time.Second), "写入日志")
}
}
/*************************************************************/
需要引入的软件包如下:
"github.com/sirupsen/logrus"
"github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
其中 logrus 是基本的软件包,可以提供多种级别日志接口,可以通过其hook机制,将日志进行分发,定制文件输出格式等。
通过file-rotatelogs包,可以对本地日志文件进行分割,可以按照时间,也可以按照文件大小来实现。lfshook是专门为logrus
定制的本地文件系统钩子,帮助开发者直接把日志写入到本地文件系统的文件中。
另外,还需要几个辅助包,否则编译不通过。
"github.com/lestrrat-go/strftime"
"github.com/pkg/errors"
"golang.org/x/sys"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
|
实际日志输出到/home/goproject/log目录下,日志文件名为helloworld.YYYYMMDD.log形式;helloworld.log为当前日志文件的链接