From a76b596a7dac18e06cbacb21d860f473ce351f35 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sat, 23 Nov 2024 15:17:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85Log=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/README.md | 41 ++++++++++- book/config/config.go | 137 +++++++++++++++++++++++++++++++++++ book/config/load.go | 20 +++++ book/controller/README.md | 2 +- book/controller/book.go | 13 +++- book/controller/logs/app.log | 3 + book/model/book.go | 5 ++ go.mod | 5 ++ go.sum | 18 ++++- 9 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 book/controller/logs/app.log diff --git a/book/README.md b/book/README.md index f3b3adb..0f5a0cd 100644 --- a/book/README.md +++ b/book/README.md @@ -147,4 +147,43 @@ func LoadConfigFromYaml(configFilePath string) error { + m: model + v: api -+ c: controller \ No newline at end of file ++ c: controller + +## 基本的工程化 + ++ 日志 + + fmt.Println() + + log: 标准库日志 + + 第三方的日志库: https://www.mcube.top/docs/framework/config/log/ + + https://github.com/rs/zerolog ++ 业务异常 + +```go +log := zerolog.New(os.Stdout) +l.log = &log +``` + +自己封装 +```go +func (l *Log) Logger() *zerolog.Logger { + l.lock.Lock() + defer l.lock.Unlock() + + if l.log == nil { + + // 文件: os.Stdout --> file + log := zerolog.New(os.Stdout) + l.log = &log + } + + return l.log +} +``` + +使用我自己封装好的Zerolog, 支持配置加载 + +```go +// log.Sub("") +// log.L() +logger: log.Sub("Book Controller"), +``` \ No newline at end of file diff --git a/book/config/config.go b/book/config/config.go index c98df80..5d8fbae 100644 --- a/book/config/config.go +++ b/book/config/config.go @@ -3,8 +3,15 @@ package config import ( "encoding/json" "fmt" + "io" + "strconv" + "strings" "sync" + "time" + "github.com/rs/zerolog" + "github.com/rs/zerolog/pkgerrors" + "gopkg.in/natefinch/lumberjack.v2" "gorm.io/driver/mysql" "gorm.io/gorm" ) @@ -17,6 +24,7 @@ import ( type Config struct { App *App `json:"app"` MySQL *MySQL `json:"mysql"` + Log *Log `json:"log"` } // &{0x102317ec0 0x10231c8a0} @@ -89,3 +97,132 @@ func (c *MySQL) DB() *gorm.DB { return c.db } + +// 怎么配置 +// 是否要打印到控制台 +// 是否要打印到文件里,是否要轮转 100M 5 +type Log struct { + // 0 为打印日志全路径, 默认打印2层路径 + CallerDeep int `toml:"caller_deep" json:"caller_deep" yaml:"caller_deep" env:"CALLER_DEEP"` + // 日志的级别, 默认Debug + Level zerolog.Level `toml:"level" json:"level" yaml:"level" env:"LEVEL"` + + // 控制台日志配置 + Console Console `toml:"console" json:"console" yaml:"console" envPrefix:"CONSOLE_"` + // 日志文件配置 + File File `toml:"file" json:"file" yaml:"file" envPrefix:"FILE_"` + + root *zerolog.Logger + lock sync.Mutex +} + +type Console struct { + Enable bool `toml:"enable" json:"enable" yaml:"enable" env:"ENABLE"` + NoColor bool `toml:"no_color" json:"no_color" yaml:"no_color" env:"NO_COLOR"` +} + +func (c *Console) ConsoleWriter() io.Writer { + output := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { + w.NoColor = c.NoColor + w.TimeFormat = time.RFC3339 + }) + + output.FormatLevel = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("%-6s", i)) + } + output.FormatMessage = func(i interface{}) string { + return fmt.Sprintf("%s", i) + } + output.FormatFieldName = func(i interface{}) string { + return fmt.Sprintf("%s:", i) + } + output.FormatFieldValue = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("%s", i)) + } + return output +} + +type File struct { + // 是否开启文件记录 + Enable bool `toml:"enable" json:"enable" yaml:"enable" env:"ENABLE"` + // 文件的路径 + FilePath string `toml:"file_path" json:"file_path" yaml:"file_path" env:"PATH"` + // 单位M, 默认100M + MaxSize int `toml:"max_size" json:"max_size" yaml:"max_size" env:"MAX_SIZE"` + // 默认保存 6个文件 + MaxBackups int `toml:"max_backups" json:"max_backups" yaml:"max_backups" env:"MAX_BACKUPS"` + // 保存多久 + MaxAge int `toml:"max_age" json:"max_age" yaml:"max_age" env:"MAX_AGE"` + // 是否压缩 + Compress bool `toml:"compress" json:"compress" yaml:"compress" env:"COMPRESS"` +} + +func (f *File) FileWriter() io.Writer { + return &lumberjack.Logger{ + Filename: f.FilePath, + MaxSize: f.MaxSize, + MaxAge: f.MaxAge, + MaxBackups: f.MaxBackups, + Compress: f.Compress, + } +} + +func (l *Log) Logger() *zerolog.Logger { + l.lock.Lock() + defer l.lock.Unlock() + + if l.root == nil { + var writers []io.Writer + if l.Console.Enable { + writers = append(writers, l.Console.ConsoleWriter()) + } + if l.File.Enable { + if l.File.FilePath == "" { + l.File.FilePath = "logs/app.log" + } + writers = append(writers, l.File.FileWriter()) + } + + zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack + + // logger对象配置一些上下文信息: 2024-11-23T15:11:42+08:00 + // 带上中间信息Any + // book/controller/book.go:60 + root := zerolog.New(io.MultiWriter(writers...)).With().Timestamp().Any("服务", "book-api") + + if l.CallerDeep > 0 { + zerolog.CallerMarshalFunc = l.CallerMarshalFunc + root = root.Caller() + } + + // 带有这些上下文信息的Logger对象 提前出来,作为全局Logger对象,供全局使用 + l.SetRoot(root.Logger().Level(l.Level)) + } + + return l.root +} + +func (m *Log) SetRoot(v zerolog.Logger) { + m.root = &v +} + +func (m *Log) CallerMarshalFunc(pc uintptr, file string, line int) string { + if m.CallerDeep == 0 { + return file + } + + short := file + count := 0 + for i := len(file) - 1; i > 0; i-- { + if file[i] == '/' { + short = file[i+1:] + count++ + } + + if count >= m.CallerDeep { + break + } + } + file = short + return file + ":" + strconv.Itoa(line) +} diff --git a/book/config/load.go b/book/config/load.go index 15391b4..f6fbf57 100644 --- a/book/config/load.go +++ b/book/config/load.go @@ -3,9 +3,16 @@ package config import ( "os" + "github.com/rs/zerolog" "gopkg.in/yaml.v3" + "gorm.io/gorm" ) +// 补充一些全局变量的快捷方式,方便全局使用 +func L() *gorm.DB { + return Get().MySQL.DB() +} + // 程序的配置需要有默认值, 给一个最小的可以运行的配置: 本地开发配置就是默认配置 // "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local" var conf = &Config{ @@ -21,6 +28,19 @@ var conf = &Config{ Password: "123456", Debug: true, }, + Log: &Log{ + CallerDeep: 3, + Level: zerolog.DebugLevel, + Console: Console{ + Enable: true, + NoColor: true, + }, + File: File{ + Enable: true, + MaxSize: 100, + MaxBackups: 6, + }, + }, } func Get() *Config { diff --git a/book/controller/README.md b/book/controller/README.md index 1071431..2d1cfb5 100644 --- a/book/controller/README.md +++ b/book/controller/README.md @@ -6,4 +6,4 @@ + 复杂的业务逻辑处理(controller之间相互调用) + 这个内部功能(公共功能) 不需要提供接口, 需要被其他多个业务模块引用 -+ 复杂的业务是需要有单元测试 \ No newline at end of file ++ 复杂的业务是需要有单元测试 diff --git a/book/controller/book.go b/book/controller/book.go index 4b224ab..0fe7014 100644 --- a/book/controller/book.go +++ b/book/controller/book.go @@ -3,6 +3,7 @@ package controller import ( "context" + "github.com/rs/zerolog" "gitlab.com/go-course-project/go17/book/config" "gitlab.com/go-course-project/go17/book/model" "gorm.io/gorm" @@ -11,12 +12,14 @@ import ( // 构造函数, 用户初始化这个结构体 func NewBookController() *BookController { return &BookController{ - db: config.Get().MySQL.DB(), + db: config.Get().MySQL.DB(), + logger: config.Get().Log.Logger(), } } type BookController struct { - db *gorm.DB + db *gorm.DB + logger *zerolog.Logger } type GetBookRequest struct { @@ -50,6 +53,12 @@ func (c *BookController) CreateBook(ctx context.Context, req *model.BookSpec) (* return nil, err } + // req 请求信息 + // 1. fmt.Println(req) + // log.Println() + // 2. zerolog + c.logger.Debug().Msgf("req 请求信息: %s", req) + // book, save ins := &model.Book{ BookSpec: *req, diff --git a/book/controller/logs/app.log b/book/controller/logs/app.log new file mode 100644 index 0000000..5428aba --- /dev/null +++ b/book/controller/logs/app.log @@ -0,0 +1,3 @@ +{"level":"debug","time":"2024-11-23T15:07:35+08:00","caller":"book/controller/book.go:60","message":"req 请求信息: {\n \"title\": \"Go语言\",\n \"author\": \"will\",\n \"price\": 10,\n \"is_sale\": null\n}"} +{"level":"debug","time":"2024-11-23T15:11:42+08:00","caller":"book/controller/book.go:60","message":"req 请求信息: {\n \"title\": \"Go语言\",\n \"author\": \"will\",\n \"price\": 10,\n \"is_sale\": null\n}"} +{"level":"debug","服务":"book-api","time":"2024-11-23T15:14:21+08:00","caller":"book/controller/book.go:60","message":"req 请求信息: {\n \"title\": \"Go语言\",\n \"author\": \"will\",\n \"price\": 10,\n \"is_sale\": null\n}"} diff --git a/book/model/book.go b/book/model/book.go index c41a5da..79bb532 100644 --- a/book/model/book.go +++ b/book/model/book.go @@ -39,6 +39,11 @@ type BookSpec struct { IsSale *bool `json:"is_sale" gorm:"column:is_sale"` } +// %s +func (s *BookSpec) String() string { + return pretty.ToJSON(s) +} + // 怎么校验 struct 参数 func (r *BookSpec) Validate() error { if r.Author == "" { diff --git a/go.mod b/go.mod index 98f9581..7df3352 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/go-playground/validator/v10 v10.20.0 github.com/infraboard/mcube/v2 v2.0.44 + github.com/rs/zerolog v1.32.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.7 gorm.io/gorm v1.25.12 @@ -22,15 +24,18 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect diff --git a/go.sum b/go.sum index 731dc47..27f6b3a 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,8 +28,10 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/infraboard/mcube/v2 v2.0.44 h1:dspnLDspWpz5r6YgFTOmIlWE4kjog0082luhvd8AUds= github.com/infraboard/mcube/v2 v2.0.44/go.mod h1:UkjuO7zbehNNvAsA1kZMB2ztaZlDY9XmTfBnNnilzB4= @@ -44,6 +47,10 @@ github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -53,8 +60,13 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -78,8 +90,10 @@ golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= @@ -88,6 +102,8 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=