这里实现了控制台和文件日志
/utils/zlog/zlog.go
package zlog
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)
type LogType int
const (
Console LogType = iota
File
ConsoleFile
)
var logType = Console
var projectPath = ""
// Init 在main.go中调用
func Init() {
_, file, _, _ := runtime.Caller(1)
dirPath := filepath.Dir(file)
dirPath = strings.ReplaceAll(dirPath, "\\", "/")
projectPath = dirPath + "/"
}
func SetLogType(mType LogType) {
logType = mType
}
func LogDebug(msg string, a ...any) {
logRecord(fmt.Sprintf(msg, a...), "DEBUG")
}
func LogError(msg string, a ...any) {
logRecord(fmt.Sprintf(msg, a...), "ERROR")
}
func logRecord(msg string, level string) {
now := time.Now().Format("2006-01-02 15:04:05")
_, mFilepath, line, _ := runtime.Caller(2)
if projectPath != "" {
mFilepath = strings.Replace(mFilepath, projectPath, "", 1)
}
logStr := "[" + now + "]\t" + level + "\t" + msg + "\t" + mFilepath + ":" + strconv.Itoa(line)
switch logType {
case Console:
writeConsole(logStr)
case File:
writeFile(logStr, level)
case ConsoleFile:
writeConsole(logStr)
writeFile(logStr, level)
}
}
func writeConsole(logStr string) {
fmt.Println(logStr)
}
func writeFile(logStr string, level string) {
ymd := time.Now().Format("2006-01-02")
err := os.MkdirAll("logs", os.ModePerm)
if err != nil {
return
}
fileName := "logs/" + level + "-" + ymd + ".log"
logFile, logFileErr := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if logFileErr != nil {
return
}
defer logFile.Close()
_, err = logFile.WriteString(logStr)
if err != nil {
return
}
return
}
调用
//先调用这个方法 可以不调用
zlog.Init()
//设置日志的输出类型 可以不调用
zlog.SetLogType(zlog.Console)
//打印日志的地方这样调用
zlog.LogDebug("测试日志")
zlog.LogDebug("insert success, last id:%d\n", 123)
当我们使用Gin框架的时候,我们可以简单的封装一下
package zlog
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
func Debug(format string, values ...any) {
now := time.Now().Format("2006/01/02 15:04:05")
f := fmt.Sprintf("[Debug] %s %s\n", now, format)
_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}
func Error(format string, values ...any) {
now := time.Now().Format("2006/01/02 15:04:05")
f := fmt.Sprintf("[Error] %s %s\n", now, format)
_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}
调用
zlog.Debug(utils.ObjToJson(list))
package zlog
import (
"fmt"
"github.com/gin-gonic/gin"
"runtime"
"time"
)
func mCaller(skip int) string {
pc, file, line, ok := runtime.Caller(skip)
if !ok {
return ""
}
// 获取方法名
funcName := runtime.FuncForPC(pc).Name()
return fmt.Sprintf("%s:%d(%s)", file, line, funcName)
}
func Info(format string, values ...any) {
fileStrAll := mCaller(2)
now := time.Now().Format("2006-01-02 15:04:05")
f := fmt.Sprintf("%s INFO %s ↓\n%s\n", now, fileStrAll, format)
_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}
func Error(format string, values ...any) {
fileStrAll := mCaller(2)
now := time.Now().Format("2006-01-02 15:04:05")
f := fmt.Sprintf("%s ERROR %s ↓\n%s\n", now, fileStrAll, format)
_, _ = fmt.Fprintf(gin.DefaultWriter, f, values...)
}
调用
zlog.Info("Hello World")
打印日志
2024-02-26 21:53:15 INFO D:/Project/go/z-wiki/main.go:33(main.main) ↓ Hello World
这样我们点击日志就直接跳转到对应的代码位置了。
Gin框架的请求日志默认在控制台输出,但更多的时候,尤其上线运行时,我们希望将用户的请求日志保存到日志文件中,以便更好的分析与备份。
在Gin框架中,通过gin.DefaultWriter
变量可能控制日志的保存方式,gin.DefaultWriter
在Gin框架中的定义如下:
var DefaultWriter io.Writer = os.Stdout
从上面的定义我们可以看出,gin.DefaultWriter
的类型为io.Writer
,默认值为os.Stdout
,即控制台输出,
因此我们可以通过修改gin.DefaultWriter
值来将请求日志保存到日志文件或其他地方(比如数据库)。
package main
import (
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
gin.DisableConsoleColor()//保存到文件不需要颜色
file, _ := os.Create("access.log")
gin.DefaultWriter = file
//gin.DefaultWriter = io.MultiWriter(file) 效果是一样的
router := gin.Default()
router.GET("/test", func(c *gin.Context) {
c.String(200, "test")
})
_ = router.Run(":8080")
}
运行后上面的程序,会在程序所在目录创建access.log
文件,当我们发起Web请求后,请求的日志会保存到access.log
文件,而不会在控制台输出。
也可以让请求日志同行保存到文件和在控制台输出:
file, _ := os.Create("access.log")
gin.DefaultWriter = io.MultiWriter(file,os.Stdout) //同时保存到文件和在控制台中输出
需要go1.19版本及以上
安装
go get go.uber.org/zap
如果您想在不先创建 Logger 实例的情况下写入日志,您可以在 init() 函数中使用 ReplaceGlobals() 方法:
package main
import (
"go.uber.org/zap"
)
func init() {
zap.ReplaceGlobals(zap.Must(zap.NewDevelopment()))
}
func main() {
zap.L().Info("Hello from Zap!")
}
此方法将可通过 zap.L() 访问的全局记录器替换为功能性 Logger 实例,以便您只需将 zap 包导入文件即可直接使用它。
您可以使用环境变量轻松地在开发和生产 Logger 之间切换:
logger := zap.Must(zap.NewProduction())
if os.Getenv("APP_ENV") == "development" {
logger = zap.Must(zap.NewDevelopment())
}
在Gin中
func init() {
if gin.Mode() == gin.DebugMode {
zap.ReplaceGlobals(zap.Must(zap.NewDevelopment()))
} else {
zap.ReplaceGlobals(zap.Must(zap.NewProduction()))
}
}
示例
logger := zap.NewExample(zap.AddCaller())
defer logger.Sync()
logger.Info("Hello World", zap.String("name", "psvmc"))
logger2, _ := zap.NewProduction(zap.AddCaller())
defer logger2.Sync()
logger2.Info("Hello World", zap.String("name", "psvmc"))
logger3, _ := zap.NewDevelopment(zap.AddCaller())
defer logger3.Sync()
logger3.Info("Hello World", zap.String("name", "psvmc"))
打印输出结果:
{“level”:”info”,”msg”:”Hello World”,”name”:”psvmc”} {“level”:”info”,”ts”:1708956240.9281151,”caller”:”z-wiki/main.go:38”,”msg”:”Hello World”,”name”:”psvmc”} 2024-02-26T22:04:00.928+0800 INFO z-wiki/main.go:42 Hello World {“name”: “psvmc”}
需要go1.21版本及以上
log/slog是Go 1.21中引入的一个新的结构化日志库,它与标准库的log包兼容,但提供了更多的功能和灵活性。
log/slog定义了一个类型,Logger,用于记录不同级别和格式的日志信息。每个Logger都关联一个Handler,用于处理日志记录。
log/slog还提供了一个默认的Logger,可以通过顶级函数(如Info和Error)来使用,它们会调用相应的Logger方法。
该默认Logger将日志信息写入标准错误,并在每条日志信息前添加日期和时间。
log/slog的日志记录由以下几个部分组成:
例如,以下代码:
package main
import (
"log/slog"
"os"
)
func main() {
slog.Info("helloworld", "user", os.Getenv("USER"))
}
会产生以下输出:
2024/02/26 18:58:17 INFO hello-world user=""
其中,2024/02/26 18:58:17
是时间,INFO
是级别,hello-world
是消息,user=""
是属性。
log/slog还提供了一些有用的特性,如:
以下是一个使用log/slog的示例代码:
package main
import (
"log/slog"
"os"
)
func main() {
// 创建一个文本处理器
textHandler := slog.NewTextHandler(os.Stderr, nil)
// 创建一个文本 Logger
textLogger := slog.New(textHandler)
// 使用Logger记录结构化的日志信息
textLogger.Info("hello-world", "user", os.Getenv("USER"))
// 创建一个JSON处理器
jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
// 创建一个 JSON Logger
jsonLogger := slog.New(jsonHandler)
// 使用Logger记录结构化的日志信息
jsonLogger.Info("hello-world", "user", os.Getenv("USER"))
}
该程序会在标准错误上输出文本格式的日志信息:
time=2024-02-26T22:35:53.533+08:00 level=INFO msg=hello-world user=""
然后在标准输出上输出JSON格式的日志信息:
{"time":"2024-02-26T22:35:53.5512541+08:00","level":"INFO","msg":"hello-world","user":""}
go get github.com/sirupsen/logrus
引用
import "github.com/sirupsen/logrus"
示例
package main
import (
"os"
"github.com/sirupsen/logrus"
)
func main() {
// 创建一个Logrus实例
log := logrus.New()
// 设置日志级别为Debug
log.SetLevel(logrus.DebugLevel)
// 设置输出目标为标准输出
log.SetOutput(os.Stdout)
// 设置格式器为JSON
log.SetFormatter(&logrus.JSONFormatter{})
// 使用Fields添加结构化的上下文信息
log.WithFields(logrus.Fields{
"key1": "value1",
"key2": "value2",
}).Info("This is a structured log")
// 使用Entry记录带有字段的日志信息
entry := log.WithFields(logrus.Fields{
"key3": "value3",
"key4": "value4",
})
entry.Warn("This is another structured log")
// 使用日志级别函数记录不同级别的日志信息
log.Debug("This is a debug log")
log.Info("This is an info log")
log.Warn("This is a warn log")
log.Error("This is an error log")
log.Fatal("This is a fatal log")
log.Panic("This is a panic log")
// 使用Log或Print等方法记录默认级别的日志信息
log.Log(logrus.InfoLevel, "This is a log with level")
log.Print("This is a print log")
}
File
=> Settings
=> Plugins
搜索 Grep Console
安装即可,安装后会根据日志中日志级别显示不同颜色。