diff --git a/go.mod b/go.mod index 36c7a6d..42bcad4 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,9 @@ require ( github.com/gin-contrib/pprof v1.4.0 github.com/gin-gonic/gin v1.9.1 github.com/go-resty/resty/v2 v2.7.0 + github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 github.com/gorilla/websocket v1.5.0 github.com/json-iterator/go v1.1.12 - github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/panjf2000/ants/v2 v2.8.1 github.com/panjf2000/gnet v1.6.7 github.com/smartystreets/goconvey v1.8.1 @@ -23,6 +23,7 @@ require ( go.uber.org/zap v1.25.0 golang.org/x/crypto v0.14.0 google.golang.org/grpc v1.59.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( @@ -36,16 +37,13 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect - github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jonboulle/clockwork v0.3.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/reedsolomon v1.11.8 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect - github.com/lestrrat-go/strftime v1.0.6 // indirect - github.com/mattn/go-isatty v0.0.19 // 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.0.8 // indirect @@ -69,6 +67,5 @@ require ( golang.org/x/text v0.13.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index db67b8c..7841553 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108 h1:iPugyBI7oFtbDZXC4dnY093M1kZx6k/95sen92gafbY= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108/go.mod h1:WAMLHwunr1hi3u7OjGV6/VWG9QbdMhGpEKjROiSFd10= -github.com/alphadose/haxmap v1.2.0 h1:noGrAmCE+gNheZ4KpW+sYj9W5uMcO1UAjbAq9XBOAfM= -github.com/alphadose/haxmap v1.2.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM= github.com/alphadose/haxmap v1.3.0 h1:C/2LboOnPCZP27GmmSXOcwx360st0P8N0fTJ3voefKc= github.com/alphadose/haxmap v1.3.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -82,8 +80,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= -github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -105,15 +101,9 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= -github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= -github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= -github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= -github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= -github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -134,8 +124,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= -github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= diff --git a/server/constants.go b/server/constants.go index 888dc8b..014eb51 100644 --- a/server/constants.go +++ b/server/constants.go @@ -1,20 +1,9 @@ package server import ( - "github.com/kercylan98/minotaur/utils/log" "time" ) -type ( - RunMode = log.RunMode -) - -const ( - RunModeDev RunMode = log.RunModeDev - RunModeProd RunMode = log.RunModeProd - RunModeTest RunMode = log.RunModeTest -) - const ( serverMultipleMark = "Minotaur Multiple Server" serverMark = "Minotaur Server" diff --git a/server/internal/logger/ants.go b/server/internal/logger/ants.go new file mode 100644 index 0000000..72d6875 --- /dev/null +++ b/server/internal/logger/ants.go @@ -0,0 +1,10 @@ +package logger + +import "github.com/kercylan98/minotaur/utils/log" + +type Ants struct { +} + +func (slf *Ants) Printf(format string, args ...interface{}) { + log.Warn(format, log.Any("args", args)) +} diff --git a/server/internal/logger/gnet.go b/server/internal/logger/gnet.go new file mode 100644 index 0000000..c169e49 --- /dev/null +++ b/server/internal/logger/gnet.go @@ -0,0 +1,29 @@ +package logger + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/log" +) + +type GNet struct { +} + +func (slf *GNet) Debugf(format string, args ...interface{}) { + log.Debug(fmt.Sprintf(format, args...)) +} + +func (slf *GNet) Infof(format string, args ...interface{}) { + log.Info(fmt.Sprintf(format, args...)) +} + +func (slf *GNet) Warnf(format string, args ...interface{}) { + log.Warn(fmt.Sprintf(format, args...)) +} + +func (slf *GNet) Errorf(format string, args ...interface{}) { + log.Error(fmt.Sprintf(format, args...)) +} + +func (slf *GNet) Fatalf(format string, args ...interface{}) { + log.Fatal(fmt.Sprintf(format, args...)) +} diff --git a/server/options.go b/server/options.go index 53a1def..c843b29 100644 --- a/server/options.go +++ b/server/options.go @@ -195,14 +195,6 @@ func WithGRPCServerOptions(options ...grpc.ServerOption) Option { } } -// WithRunMode 通过特定模式运行服务器 -// - 默认为 RunModeDev -func WithRunMode(mode RunMode) Option { - return func(srv *Server) { - srv.runMode = mode - } -} - // WithWebsocketMessageType 设置仅支持特定类型的Websocket消息 func WithWebsocketMessageType(messageTypes ...int) Option { return func(srv *Server) { diff --git a/server/server.go b/server/server.go index 9626d6d..7492347 100644 --- a/server/server.go +++ b/server/server.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "github.com/kercylan98/minotaur/server/internal/logger" "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/network" @@ -14,7 +15,6 @@ import ( "github.com/kercylan98/minotaur/utils/timer" "github.com/panjf2000/ants/v2" "github.com/panjf2000/gnet" - "github.com/panjf2000/gnet/pkg/logging" "github.com/xtaci/kcp-go/v5" "google.golang.org/grpc" "net" @@ -67,7 +67,7 @@ func New(network Network, options ...Option) *Server { server.antsPoolSize = DefaultAsyncPoolSize } var err error - server.ants, err = ants.NewPool(server.antsPoolSize, ants.WithLogger(log.GetLogger())) + server.ants, err = ants.NewPool(server.antsPoolSize, ants.WithLogger(new(logger.Ants))) if err != nil { panic(err) } @@ -93,7 +93,6 @@ type Server struct { online *concurrent.BalanceMap[string, *Conn] // 在线连接 network Network // 网络类型 addr string // 侦听地址 - runMode RunMode // 运行模式 systemSignal chan os.Signal // 系统信号 closeChannel chan struct{} // 关闭信号 multipleRuntimeErrorChan chan error // 多服务器模式下的运行时错误 @@ -177,8 +176,7 @@ func (slf *Server) Run(addr string) error { slf.isRunning = true slf.OnStartBeforeEvent() if err := gnet.Serve(slf.gServer, protoAddr, - gnet.WithLogger(log.GetLogger()), - gnet.WithLogLevel(super.If(slf.runMode == RunModeProd, logging.ErrorLevel, logging.DebugLevel)), + gnet.WithLogger(new(logger.GNet)), gnet.WithTicker(true), gnet.WithMulticore(true), ); err != nil { @@ -230,18 +228,19 @@ func (slf *Server) Run(addr string) error { } }) case NetworkHttp: - switch slf.runMode { - case RunModeDev: - gin.SetMode(gin.DebugMode) - case RunModeTest: - gin.SetMode(gin.TestMode) - case RunModeProd: - gin.SetMode(gin.ReleaseMode) - } go func() { slf.isRunning = true slf.OnStartBeforeEvent() slf.httpServer.Addr = slf.addr + gin.SetMode(gin.ReleaseMode) + slf.ginServer.Use(func(c *gin.Context) { + t := time.Now() + c.Next() + log.Info("Server", log.String("type", "http"), + log.String("method", c.Request.Method), log.Int("status", c.Writer.Status()), + log.String("ip", c.ClientIP()), log.String("path", c.Request.URL.Path), + log.Duration("cost", time.Since(t))) + }) go connectionInitHandle(nil) if len(slf.certFile)+len(slf.keyFile) > 0 { if err := slf.httpServer.ListenAndServeTLS(slf.certFile, slf.keyFile); err != nil { diff --git a/server/server_test.go b/server/server_test.go index 855280e..d6dca80 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/kercylan98/minotaur/server" "github.com/kercylan98/minotaur/server/client" + "github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/times" "testing" "time" @@ -29,6 +30,13 @@ func TestNew(t *testing.T) { //} }) + srv.RegStartFinishEvent(func(srv *server.Server) { + log.Warn("启动完成") + log.Error("启动完成") + log.Info("启动完成") + log.Debug("启动完成") + }) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { conn.Write(packet) }) diff --git a/utils/log/core.go b/utils/log/core.go deleted file mode 100644 index 5d8965e..0000000 --- a/utils/log/core.go +++ /dev/null @@ -1,5 +0,0 @@ -package log - -import "go.uber.org/zap/zapcore" - -type Core = zapcore.Core diff --git a/utils/log/encoder.go b/utils/log/encoder.go index c7d5570..442debf 100644 --- a/utils/log/encoder.go +++ b/utils/log/encoder.go @@ -1,26 +1,53 @@ package log import ( + "go.uber.org/zap" "go.uber.org/zap/zapcore" - "time" + "gopkg.in/natefinch/lumberjack.v2" + "os" ) -type Encoder = zapcore.Encoder - -// NewEncoder 创建一个 Minotaur 默认使用的编码器 -func NewEncoder() Encoder { - return zapcore.NewConsoleEncoder(zapcore.EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - EncodeLevel: zapcore.CapitalLevelEncoder, - TimeKey: "ts", - EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendString(t.Format(time.DateTime)) - }, - CallerKey: "file", - EncodeCaller: zapcore.ShortCallerEncoder, - EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendInt64(int64(d) / 1000000) - }, - }) +type Encoder struct { + e zapcore.Encoder + cores []Core + conf *Config +} + +func (slf *Encoder) Split(config *lumberjack.Logger) *Encoder { + slf.cores = append(slf.cores, zapcore.NewCore(slf.e, zapcore.AddSync(config), zapcore.DebugLevel)) + return slf +} + +func (slf *Encoder) AddCore(ws WriteSyncer, enab LevelEnabler) *Encoder { + slf.cores = append(slf.cores, zapcore.NewCore(slf.e, ws, enab)) + return slf +} + +func (slf *Encoder) Build(options ...LoggerOption) *Minotaur { + l, err := slf.conf.Build() + if err != nil { + panic(err) + } + options = append([]LoggerOption{zap.AddCaller(), zap.AddCallerSkip(1)}, options...) + l = l.WithOptions(options...) + if len(slf.cores) == 0 { + // stdout、stderr,不使用 lumberjack.Logger + slf.cores = append(slf.cores, zapcore.NewCore( + slf.e, + zapcore.Lock(os.Stdout), + zapcore.InfoLevel, + )) + slf.cores = append(slf.cores, zapcore.NewCore( + slf.e, + zapcore.Lock(os.Stderr), + zapcore.ErrorLevel, + )) + } + l = l.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { + return zapcore.NewTee(slf.cores...) + })) + return &Minotaur{ + Logger: l, + Sugared: l.Sugar(), + } } diff --git a/utils/log/level.go b/utils/log/level.go index b1dac5b..fd7a0ae 100644 --- a/utils/log/level.go +++ b/utils/log/level.go @@ -1,13 +1,6 @@ package log -import ( - "github.com/kercylan98/minotaur/utils/hash" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -type Level = zapcore.Level -type LevelEnablerFunc = zap.LevelEnablerFunc +import "go.uber.org/zap/zapcore" const ( // DebugLevel 调试级别日志通常非常庞大,并且通常在生产中被禁用 @@ -25,86 +18,3 @@ const ( // FatalLevel 记录一条消息,然后调用 os.Exit(1) FatalLevel Level = zapcore.FatalLevel ) - -var ( - levels = []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} - defaultLevelPartition = map[Level]func() LevelEnablerFunc{ - DebugLevel: DebugLevelPartition, - InfoLevel: InfoLevelPartition, - WarnLevel: WarnLevelPartition, - ErrorLevel: ErrorLevelPartition, - DPanicLevel: DPanicLevelPartition, - PanicLevel: PanicLevelPartition, - FatalLevel: FatalLevelPartition, - } -) - -// Levels 返回所有日志级别 -func Levels() []Level { - return levels -} - -// MultiLevelPartition 返回一个 LevelEnablerFunc,该函数在指定的多个级别时返回 true -// - 该函数被用于划分不同级别的日志输出 -func MultiLevelPartition(levels ...Level) LevelEnablerFunc { - var levelMap = hash.ToIterator(levels) - return func(level zapcore.Level) bool { - return hash.Exist(levelMap, level) - } -} - -// DebugLevelPartition 返回一个 LevelEnablerFunc,该函数在 DebugLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func DebugLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == DebugLevel - } -} - -// InfoLevelPartition 返回一个 LevelEnablerFunc,该函数在 InfoLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func InfoLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == InfoLevel - } -} - -// WarnLevelPartition 返回一个 LevelEnablerFunc,该函数在 WarnLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func WarnLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == WarnLevel - } -} - -// ErrorLevelPartition 返回一个 LevelEnablerFunc,该函数在 ErrorLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func ErrorLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == ErrorLevel - } -} - -// DPanicLevelPartition 返回一个 LevelEnablerFunc,该函数在 DPanicLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func DPanicLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == DPanicLevel - } -} - -// PanicLevelPartition 返回一个 LevelEnablerFunc,该函数在 PanicLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func PanicLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == PanicLevel - } -} - -// FatalLevelPartition 返回一个 LevelEnablerFunc,该函数在 FatalLevel 时返回 true -// - 该函数被用于划分不同级别的日志输出 -func FatalLevelPartition() LevelEnablerFunc { - return func(level zapcore.Level) bool { - return level == FatalLevel - } -} diff --git a/utils/log/log.go b/utils/log/log.go deleted file mode 100644 index 2226cdc..0000000 --- a/utils/log/log.go +++ /dev/null @@ -1,170 +0,0 @@ -package log - -import ( - "fmt" - "github.com/kercylan98/minotaur/utils/str" - "github.com/kercylan98/minotaur/utils/times" - rotateLogs "github.com/lestrrat-go/file-rotatelogs" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "os" - "path/filepath" - "strings" -) - -// NewLog 创建一个日志记录器 -func NewLog(options ...Option) *Log { - log := &Log{ - filename: func(level Level) string { - return fmt.Sprintf("%s.log", level.String()) - }, - rotateFilename: func(level Level) string { - return strings.Join([]string{level.String(), "%Y%m%d.log"}, ".") - }, - levelPartition: defaultLevelPartition, - } - - for _, option := range options { - option(log) - } - - if len(log.rotateOptions) == 0 { - log.rotateOptions = []rotateLogs.Option{ - rotateLogs.WithMaxAge(times.Week), - rotateLogs.WithRotationTime(times.Day), - } - } - - if len(log.cores) == 0 { - var encoder = NewEncoder() - - switch log.mode { - case RunModeDev: - var partition LevelEnablerFunc = func(lvl Level) bool { - return true - } - log.cores = append(log.cores, zapcore.NewCore(encoder, os.Stdout, partition)) - case RunModeTest, RunModeProd: - if log.mode == RunModeTest { - infoRotate, err := rotateLogs.New( - filepath.Join(log.rotateLogDir, log.rotateFilename(InfoLevel)), - append([]rotateLogs.Option{rotateLogs.WithLinkName(filepath.Join(log.logDir, log.filename(InfoLevel)))}, log.rotateOptions...)..., - ) - if err != nil { - panic(err) - } - errRotate, err := rotateLogs.New( - filepath.Join(log.rotateLogDir, log.rotateFilename(ErrorLevel)), - append([]rotateLogs.Option{rotateLogs.WithLinkName(filepath.Join(log.logDir, log.filename(ErrorLevel)))}, log.rotateOptions...)..., - ) - if err != nil { - panic(err) - } - if log.logDir != str.None { - log.cores = append(log.cores, zapcore.NewCore(encoder, zapcore.AddSync(infoRotate), LevelEnablerFunc(func(lvl Level) bool { return lvl < ErrorLevel }))) - log.cores = append(log.cores, zapcore.NewCore(encoder, zapcore.AddSync(errRotate), LevelEnablerFunc(func(lvl Level) bool { return lvl >= ErrorLevel }))) - log.cores = append(log.cores, zapcore.NewCore(encoder, os.Stdout, LevelEnablerFunc(func(lvl Level) bool { return lvl < ErrorLevel }))) - log.cores = append(log.cores, zapcore.NewCore(encoder, os.Stdout, LevelEnablerFunc(func(lvl Level) bool { return lvl >= ErrorLevel }))) - } - } else { - infoRotate, err := rotateLogs.New( - filepath.Join(log.rotateLogDir, log.rotateFilename(InfoLevel)), - append([]rotateLogs.Option{rotateLogs.WithLinkName(filepath.Join(log.logDir, log.filename(InfoLevel)))}, log.rotateOptions...)..., - ) - if err != nil { - panic(err) - } - errRotate, err := rotateLogs.New( - filepath.Join(log.rotateLogDir, log.rotateFilename(ErrorLevel)), - append([]rotateLogs.Option{rotateLogs.WithLinkName(filepath.Join(log.logDir, log.filename(ErrorLevel)))}, log.rotateOptions...)..., - ) - if err != nil { - panic(err) - } - if log.logDir != str.None { - log.cores = append(log.cores, zapcore.NewCore(encoder, zapcore.AddSync(infoRotate), LevelEnablerFunc(func(lvl Level) bool { return lvl == InfoLevel }))) - log.cores = append(log.cores, zapcore.NewCore(encoder, zapcore.AddSync(errRotate), LevelEnablerFunc(func(lvl Level) bool { return lvl >= ErrorLevel }))) - } - } - } - } - - log.zap = zap.New(zapcore.NewTee(log.cores...), zap.AddCaller(), zap.AddCallerSkip(2)) - log.sugar = log.zap.Sugar() - return log -} - -type Log struct { - zap *zap.Logger - sugar *zap.SugaredLogger - filename func(level Level) string - rotateFilename func(level Level) string - rotateOptions []rotateLogs.Option - levelPartition map[Level]func() LevelEnablerFunc - cores []Core - mode RunMode - logDir string - rotateLogDir string -} - -func (slf *Log) Debugf(format string, args ...interface{}) { - slf.sugar.Debugf(format, args...) -} - -func (slf *Log) Infof(format string, args ...interface{}) { - slf.sugar.Infof(format, args...) -} - -func (slf *Log) Warnf(format string, args ...interface{}) { - slf.sugar.Warnf(format, args...) -} - -func (slf *Log) Errorf(format string, args ...interface{}) { - slf.sugar.Errorf(format, args...) -} - -func (slf *Log) Fatalf(format string, args ...interface{}) { - slf.sugar.Fatalf(format, args...) -} - -func (slf *Log) Printf(format string, args ...interface{}) { - slf.sugar.Infof(format, args...) -} - -// Debug 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func (slf *Log) Debug(msg string, fields ...Field) { - slf.zap.Debug(msg, fields...) -} - -// Info 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func (slf *Log) Info(msg string, fields ...Field) { - slf.zap.Info(msg, fields...) -} - -// Warn 在 WarnLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func (slf *Log) Warn(msg string, fields ...Field) { - slf.zap.Warn(msg, fields...) -} - -// Error 在 ErrorLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func (slf *Log) Error(msg string, fields ...Field) { - slf.zap.Error(msg, fields...) -} - -// DPanic 在 DPanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -// - 如果记录器处于开发模式,它就会出现 panic(DPanic 的意思是“development panic”)。这对于捕获可恢复但不应该发生的错误很有用 -func (slf *Log) DPanic(msg string, fields ...Field) { - slf.zap.DPanic(msg, fields...) -} - -// Panic 在 PanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -// - 即使禁用了 PanicLevel 的日志记录,记录器也会出现 panic -func (slf *Log) Panic(msg string, fields ...Field) { - slf.zap.Panic(msg, fields...) -} - -// Fatal 在 FatalLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -// - 然后记录器调用 os.Exit(1),即使 FatalLevel 的日志记录被禁用 -func (slf *Log) Fatal(msg string, fields ...Field) { - slf.zap.Fatal(msg, fields...) -} diff --git a/utils/log/logger.go b/utils/log/logger.go index a0a1c96..2b35dfe 100644 --- a/utils/log/logger.go +++ b/utils/log/logger.go @@ -1,16 +1,22 @@ package log -import ( - "github.com/panjf2000/ants/v2" - "github.com/panjf2000/gnet/pkg/logging" -) +var logger Logger -var logger Logger = NewLog() +func init() { + logger = Default().Build() +} + +// SetLogger 设置日志记录器 +func SetLogger(l Logger) { + if m, ok := l.(*Minotaur); ok && m != nil { + _ = m.Sync() + _ = m.Sugared.Sync() + } + logger = l +} // Logger 适用于 Minotaur 的日志接口 type Logger interface { - ants.Logger - logging.Logger // Debug 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 Debug(msg string, fields ...Field) // Info 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 @@ -67,13 +73,3 @@ func Panic(msg string, fields ...Field) { func Fatal(msg string, fields ...Field) { logger.Fatal(msg, fields...) } - -// SetLogger 设置日志记录器 -func SetLogger(log Logger) { - logger = log -} - -// GetLogger 获取日志记录器 -func GetLogger() Logger { - return logger -} diff --git a/utils/log/minotaur.go b/utils/log/minotaur.go new file mode 100644 index 0000000..73efa10 --- /dev/null +++ b/utils/log/minotaur.go @@ -0,0 +1,8 @@ +package log + +import "go.uber.org/zap" + +type Minotaur struct { + *zap.Logger + Sugared *zap.SugaredLogger +} diff --git a/utils/log/options.go b/utils/log/options.go index c00297c..2938b28 100644 --- a/utils/log/options.go +++ b/utils/log/options.go @@ -1,52 +1,237 @@ package log import ( - rotateLogs "github.com/lestrrat-go/file-rotatelogs" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "io" + "time" ) -type Option func(log *Log) +type ( + Config = zap.Config + Level = zapcore.Level + LevelEncoder = zapcore.LevelEncoder + TimeEncoder = zapcore.TimeEncoder + DurationEncoder = zapcore.DurationEncoder + CallerEncoder = zapcore.CallerEncoder + NameEncoder = zapcore.NameEncoder + ReflectedEncoder = zapcore.ReflectedEncoder + WriteSyncer = zapcore.WriteSyncer + LoggerOption = zap.Option + Core = zapcore.Core + LevelEnabler = zapcore.LevelEnabler + Option func(config *Config) +) -// WithRunMode 设置运行模式 -// - 默认的运行模式为: RunModeDev -// - 当 handle 不为空时,将会调用 handle(),并将返回值添加到日志记录器中,同时将会抑制默认的日志记录器 -func WithRunMode(mode RunMode, handle func() Core) Option { - return func(log *Log) { - log.mode = mode - if handle != nil { - log.cores = append(log.cores, handle()) +func Default(opts ...Option) *Encoder { + config := &zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + Development: true, + DisableStacktrace: true, + Sampling: &zap.SamplingConfig{ + Initial: 100, + Thereafter: 100, + }, + EncoderConfig: zapcore.EncoderConfig{ + TimeKey: "Time", + LevelKey: "Level", + NameKey: "Name", + CallerKey: "Caller", + MessageKey: "Msg", + StacktraceKey: "Stack", + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) { + encoder.AppendString(t.Format(time.DateTime)) + }, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + }, + } + + // 应用选项 + for _, opt := range opts { + opt(config) + } + + if len(config.Encoding) == 0 { + if config.Development { + config.Encoding = "console" + } else { + config.Encoding = "json" } } + + var encoder = new(Encoder) + encoder.conf = config + switch config.Encoding { + case "console": + encoder.e = zapcore.NewConsoleEncoder(config.EncoderConfig) + case "json": + encoder.e = zapcore.NewJSONEncoder(config.EncoderConfig) + default: + panic("unknown encoding") + } + + return encoder } -// WithFilename 设置日志文件名 -// - 默认的日志文件名为: {level}.log -func WithFilename(filename func(level Level) string) Option { - return func(log *Log) { - log.filename = filename +// WithLevel 设置日志级别 +func WithLevel(level Level) Option { + return func(config *Config) { + config.Level.SetLevel(level) } } -// WithRotateFilename 设置日志分割文件名 -// - 默认的日志分割文件名为: {level}.%Y%m%d.log -func WithRotateFilename(filename func(level Level) string) Option { - return func(log *Log) { - log.rotateFilename = filename +// WithDevelopment 设置是否为开发模式 +func WithDevelopment(development bool) Option { + return func(config *Config) { + config.Development = development } } -// WithRotateOption 设置日志分割选项 -// - 默认的日志分割选项为: WithMaxAge(7天), WithRotationTime(1天) -func WithRotateOption(options ...rotateLogs.Option) Option { - return func(log *Log) { - log.rotateOptions = options +// WithDisableCaller 设置是否禁用调用者 +func WithDisableCaller(disableCaller bool) Option { + return func(config *Config) { + config.DisableCaller = disableCaller } } -// WithLogDir 设置日志文件夹 -// - 默认情况下不会设置日志文件夹,日志将不会被文件存储 -func WithLogDir(logDir, rotateLogDir string) Option { - return func(log *Log) { - log.logDir = logDir - log.rotateLogDir = rotateLogDir +// WithDisableStacktrace 设置是否禁用堆栈跟踪 +func WithDisableStacktrace(disableStacktrace bool) Option { + return func(config *Config) { + config.DisableStacktrace = disableStacktrace + } +} + +// WithSampling 设置采样策略 +func WithSampling(sampling *zap.SamplingConfig) Option { + return func(config *Config) { + config.Sampling = sampling + } +} + +// WithEncoding 设置编码器 +func WithEncoding(encoding string) Option { + return func(config *Config) { + config.Encoding = encoding + } +} + +// WithEncoderMessageKey 设置消息键 +func WithEncoderMessageKey(encoderMessageKey string) Option { + return func(config *Config) { + config.EncoderConfig.MessageKey = encoderMessageKey + } +} + +// WithEncoderLevelKey 设置级别键 +func WithEncoderLevelKey(encoderLevelKey string) Option { + return func(config *Config) { + config.EncoderConfig.LevelKey = encoderLevelKey + } +} + +// WithEncoderTimeKey 设置时间键 +func WithEncoderTimeKey(encoderTimeKey string) Option { + return func(config *Config) { + config.EncoderConfig.TimeKey = encoderTimeKey + } +} + +// WithEncoderNameKey 设置名称键 +func WithEncoderNameKey(encoderNameKey string) Option { + return func(config *Config) { + config.EncoderConfig.NameKey = encoderNameKey + } +} + +// WithEncoderCallerKey 设置调用者键 +func WithEncoderCallerKey(encoderCallerKey string) Option { + return func(config *Config) { + config.EncoderConfig.CallerKey = encoderCallerKey + } +} + +// WithEncoderFunctionKey 设置函数键 +func WithEncoderFunctionKey(encoderFunctionKey string) Option { + return func(config *Config) { + config.EncoderConfig.FunctionKey = encoderFunctionKey + } +} + +// WithEncoderStacktraceKey 设置堆栈跟踪键 +func WithEncoderStacktraceKey(encoderStacktraceKey string) Option { + return func(config *Config) { + config.EncoderConfig.StacktraceKey = encoderStacktraceKey + } +} + +// WithEncoderLineEnding 设置行尾 +func WithEncoderLineEnding(encoderLineEnding string) Option { + return func(config *Config) { + config.EncoderConfig.LineEnding = encoderLineEnding + } +} + +// WithEncoderLevel 设置级别编码器 +func WithEncoderLevel(encoderLevel LevelEncoder) Option { + return func(config *Config) { + config.EncoderConfig.EncodeLevel = encoderLevel + } +} + +// WithEncoderTime 设置时间编码器 +func WithEncoderTime(encoderTime TimeEncoder) Option { + return func(config *Config) { + config.EncoderConfig.EncodeTime = encoderTime + } +} + +// WithEncoderDuration 设置持续时间编码器 +func WithEncoderDuration(encoderDuration DurationEncoder) Option { + return func(config *Config) { + config.EncoderConfig.EncodeDuration = encoderDuration + } +} + +// WithEncoderCaller 设置调用者编码器 +func WithEncoderCaller(encoderCaller CallerEncoder) Option { + return func(config *Config) { + config.EncoderConfig.EncodeCaller = encoderCaller + } +} + +// WithEncoderName 设置名称编码器 +func WithEncoderName(encoderName NameEncoder) Option { + return func(config *Config) { + config.EncoderConfig.EncodeName = encoderName + } +} + +// WithEncoderNewReflectedEncoder 设置反射编码器 +func WithEncoderNewReflectedEncoder(encoderNewReflectedEncoder func(io.Writer) ReflectedEncoder) Option { + return func(config *Config) { + config.EncoderConfig.NewReflectedEncoder = encoderNewReflectedEncoder + } +} + +// WithOutputPaths 设置输出路径 +func WithOutputPaths(outputPaths ...string) Option { + return func(config *Config) { + config.OutputPaths = outputPaths + } +} + +// WithErrorOutputPaths 设置错误输出路径 +func WithErrorOutputPaths(errorOutputPaths ...string) Option { + return func(config *Config) { + config.ErrorOutputPaths = errorOutputPaths + } +} + +// WithInitialFields 设置初始字段 +func WithInitialFields(initialFields map[string]interface{}) Option { + return func(config *Config) { + config.InitialFields = initialFields } } diff --git a/utils/log/run_mode.go b/utils/log/run_mode.go deleted file mode 100644 index b49a7d4..0000000 --- a/utils/log/run_mode.go +++ /dev/null @@ -1,16 +0,0 @@ -package log - -const ( - // RunModeDev 开发模式是默认的运行模式,同时也是最基础的运行模式 - // - 开发模式下,将会输出所有级别的日志到控制台 - // - 默认不再输出日志到文件 - RunModeDev RunMode = iota - // RunModeTest 测试模式是一种特殊的运行模式,用于测试 - // - 测试模式下,将会输出所有级别的日志到控制台和文件 - RunModeTest - // RunModeProd 生产模式是一种特殊的运行模式,用于生产 - // - 生产模式下,将会输出 InfoLevel 及以上级别的日志到控制台和文件 - RunModeProd -) - -type RunMode uint8