Sfoglia il codice sorgente

初始化日志库

runningwater 1 anno fa
parent
commit
8abc6c974b
8 ha cambiato i file con 186 aggiunte e 14 eliminazioni
  1. 1 1
      bootstrap/database.go
  2. 22 0
      bootstrap/logger.go
  3. 23 0
      config/log.go
  4. 4 3
      go.mod
  5. 10 6
      go.sum
  6. 6 4
      main.go
  7. 16 0
      pkg/app/app.go
  8. 104 0
      pkg/logger/logger.go

+ 1 - 1
bootstrap/database.go

@@ -48,5 +48,5 @@ func SetupDB() {
 	// 设置连接的最大存活时间
 	database.SQLDB.SetConnMaxLifetime(time.Duration(config.GetInt("database.mysql.max_life_seconds"))* time.Second)
 
-	database.DB.AutoMigrate(&user.User{})
+	_ = database.DB.AutoMigrate(&user.User{})
 }

+ 22 - 0
bootstrap/logger.go

@@ -0,0 +1,22 @@
+package bootstrap
+
+import (
+	"github.com/runningwater/gohub/pkg/config"
+	"github.com/runningwater/gohub/pkg/logger"
+)
+
+func SetupLogger() {
+	// 这里可以添加日志的初始化代码
+	// 例如,设置日志级别、输出格式等
+	// 使用 logrus、zap 等第三方库进行日志处理
+
+	logger.Init(
+		config.GetString("log.filename"),
+		config.GetInt("log.max_size"),
+		config.GetInt("log.max_backup"),
+		config.GetInt("log.max_age"),
+		config.GetBool("log.compress"),
+		config.GetString("log.type"),
+		config.GetString("log.level"),
+	)
+}

+ 23 - 0
config/log.go

@@ -0,0 +1,23 @@
+package config
+
+import "github.com/runningwater/gohub/pkg/config"
+
+func init() {
+	config.Add("log", func() map[string]any {
+		return map[string]any{
+			
+			// 日志级别,必须是以下这些选项:debug, info, warn, error
+			"level": config.Env("LOG_LEVEL", "debug"),
+			// 日志类型,必须是以下这些选项:
+			// single 独立的文件
+			// daily 每天一个文件
+			"type": config.Env("LOG_TYPE", "single"),
+			// 滚动日志配置
+			"filename": config.Env("LOG_NAME", "storage/logs/logs.log"),
+			"max_size": config.Env("LOG_MAX_SIZE", 64), // MB
+			"max_backups": config.Env("LOG_MAX_BACKUPS", 5), // 保留的最大备份数
+			"max_age": config.Env("LOG_MAX_AGE", 7), // 保留的最大天数, 单位:天 0表示不限制
+			"compress": config.Env("LOG_COMPRESS", false), // 是否压缩
+		}
+	})
+}

+ 4 - 3
go.mod

@@ -7,6 +7,8 @@ require (
 	github.com/spf13/cast v1.7.1
 	github.com/spf13/viper v1.20.1
 	github.com/thedevsaddam/govalidator v1.9.10
+	go.uber.org/zap v1.27.0
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 	gorm.io/driver/mysql v1.5.7
 	gorm.io/driver/sqlite v1.5.7
 	gorm.io/gorm v1.25.12
@@ -43,11 +45,10 @@ require (
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.12 // indirect
-	go.uber.org/atomic v1.9.0 // indirect
-	go.uber.org/multierr v1.9.0 // indirect
+	go.uber.org/multierr v1.10.0 // indirect
 	golang.org/x/arch v0.8.0 // indirect
 	golang.org/x/crypto v0.37.0 // indirect
-	golang.org/x/net v0.33.0 // indirect
+	golang.org/x/net v0.39.0 // indirect
 	golang.org/x/sys v0.32.0 // indirect
 	golang.org/x/text v0.24.0 // indirect
 	google.golang.org/protobuf v1.36.1 // indirect

+ 10 - 6
go.sum

@@ -97,17 +97,19 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
 github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
 github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
-go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
-go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
+go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
 golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
 golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
 golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
-golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
+golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
@@ -119,6 +121,8 @@ google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 6 - 4
main.go

@@ -18,15 +18,17 @@ func init() {
 func main() {
 	//  配置初始化,依赖命令行 --env 参数
 	var env string
-	flag.StringVar(&env, "env", "", "加载.env 文件,如 --env=local加载的是 .env.local 文件")
+	flag.StringVar(&env, "env", "", "加载.env 文件,如 --env=local, 加载的是 .env.local 文件")
 	flag.Parse()
 	config.InitConfig(env)
 
-	// Gin 框架初始化
-	r:= gin.New()
-
 	// 初始化 DB
 	bootstrap.SetupDB()
+	// 初始化 Logger
+	bootstrap.SetupLogger()
+
+	// Gin 框架初始化
+	r:= gin.New()
 
 	// 初始化路由绑定
 	bootstrap.SetupRoute(r)

+ 16 - 0
pkg/app/app.go

@@ -0,0 +1,16 @@
+// package app 应用信息包
+package app
+
+import "github.com/runningwater/gohub/pkg/config"
+
+func IsLocal() bool {
+	// 判断是否是本地环境
+	return config.Get("app.env") == "local"
+}
+
+func IsProduction() bool {
+	return config.Get("app.env") == "production"
+}
+func IsTesting() bool {
+	return config.Get("app.env") == "testing"
+}

+ 104 - 0
pkg/logger/logger.go

@@ -0,0 +1,104 @@
+// Pacage logger provides a simple wrapper around zap logger
+package logger
+
+import (
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/runningwater/gohub/pkg/app"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+// Logger 全局日志对象
+var Logger *zap.Logger
+
+// Init 初始化日志
+func Init(filename string, maxSize, maxBackup, maxAge int, compress bool, logType, level string) {
+	// 获取日志写入介质
+	writeSyncer := getLogWriter(filename, maxSize, maxBackup, maxAge, compress, logType)
+	// 设置日志等级, 具体见 config/log.go
+	logLevel := new(zap.AtomicLevel)
+	if err := logLevel.UnmarshalText([]byte(level)); err != nil {
+		fmt.Println("日志初始化错误,日志级别设置有误。请修改 config/log.go 中的 log.level")
+	}
+
+	// 初始化 core
+	core := zapcore.NewCore(getEncoder(), writeSyncer, logLevel)
+	// 初始化 logger
+	Logger = zap.New(core,
+		 zap.AddCaller(), // 调用文件和行号,内部使用 runtime.Caller
+		 zap.AddCallerSkip(1), // 调用文件和行号,内部使用 runtime.Caller
+		 zap.Development(), // 开发模式,堆栈跟踪
+		 zap.AddStacktrace(zapcore.ErrorLevel), // 记录错误级别以上的堆栈信息
+	)
+
+	// 设置全局 logger
+	// 将自定义的 logger 替换 zap 的全局 logger
+	zap.ReplaceGlobals(Logger)
+}
+
+// getEncoder 获取编码器(如何写入日志), 输出: 日志格式
+func getEncoder() zapcore.Encoder {
+	// 日志模式规则
+	encoderConfig := zapcore.EncoderConfig{
+		TimeKey:        "time", // 时间字段名
+		LevelKey:       "level", // 日志级别字段名
+		NameKey:        "logger", // 日志名称字段名
+		CallerKey:      "caller", // 调用者字段名
+		FunctionKey:    zapcore.OmitKey, // 函数名字段名,不显示
+		MessageKey:     "msg", // 消息字段名
+		StacktraceKey:  "stacktrace", // 堆栈跟踪字段名
+		LineEnding:     zapcore.DefaultLineEnding, // 每行日志的结尾添加换行符
+		EncodeLevel:    zapcore.CapitalLevelEncoder, // 大写编码器
+		EncodeTime:     customTimeEncoder, // 自定义时间编码器
+		EncodeDuration: zapcore.SecondsDurationEncoder, // 以秒为单位显示持续时间
+		EncodeCaller:   zapcore.ShortCallerEncoder, // 短路径编码器
+	}
+
+	if app.IsLocal() {
+		encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 彩色编码器
+		// 如果是本地环境,使用 console 编码器
+		return zapcore.NewConsoleEncoder(encoderConfig)
+	}
+	// 如果不是本地环境,使用 JSON 编码器
+	return zapcore.NewJSONEncoder(encoderConfig)
+}
+
+func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+	// 自定义时间格式
+	enc.AppendString(t.Format("2006-01-02 15:04:05"))
+}
+
+// getLogWriter 获取日志写入介质
+// os.Stdout: 标准输出
+// 文件输出
+func getLogWriter(filename string, maxSize, maxBackup, maxAge int, compress bool, logType string) zapcore.WriteSyncer {
+	// 如果配置了按日期记录日志,则使用按日期记录日志
+	if logType == "daily" {
+		logname := time.Now().Format("2006-01-02") + ".log"
+		filename = strings.Replace(filename, "logs.log", logname, 1)
+	}
+
+	// 滚动日志,详见 config/log.go
+	lumberJackLogger := &lumberjack.Logger{
+		Filename:   filename, // 日志文件名
+		MaxSize:    maxSize, // 每个日志文件保存的最大尺寸 单位:MB
+		MaxBackups: maxBackup, // 日志文件最多保存多少个备份
+		MaxAge:     maxAge, // 文件最多保存多少天
+		Compress:   compress, // 是否压缩
+	}	
+	if app.IsLocal() {
+		// 如果是本地环境,使用标准输出和文件输出
+		return  zapcore.NewMultiWriteSyncer(
+			zapcore.AddSync(os.Stdout),
+			zapcore.AddSync(lumberJackLogger),
+		)
+	} else {
+		// 生产环境只记录文件
+		return zapcore.AddSync(lumberJackLogger)
+	}
+}