refactor: 重构日志模块并清理未使用的依赖

This commit is contained in:
kercylan98 2023-11-28 17:37:01 +08:00
parent 9f27da2dce
commit d3ad49d11e
16 changed files with 348 additions and 401 deletions

9
go.mod
View File

@ -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
)

16
go.sum
View File

@ -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=

View File

@ -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"

View File

@ -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))
}

View File

@ -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...))
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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)
})

View File

@ -1,5 +0,0 @@
package log
import "go.uber.org/zap/zapcore"
type Core = zapcore.Core

View File

@ -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(),
}
}

View File

@ -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
}
}

View File

@ -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 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段
// - 如果记录器处于开发模式,它就会出现 panicDPanic 的意思是“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...)
}

View File

@ -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
}

8
utils/log/minotaur.go Normal file
View File

@ -0,0 +1,8 @@
package log
import "go.uber.org/zap"
type Minotaur struct {
*zap.Logger
Sugared *zap.SugaredLogger
}

View File

@ -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
}
}

View File

@ -1,16 +0,0 @@
package log
const (
// RunModeDev 开发模式是默认的运行模式,同时也是最基础的运行模式
// - 开发模式下,将会输出所有级别的日志到控制台
// - 默认不再输出日志到文件
RunModeDev RunMode = iota
// RunModeTest 测试模式是一种特殊的运行模式,用于测试
// - 测试模式下,将会输出所有级别的日志到控制台和文件
RunModeTest
// RunModeProd 生产模式是一种特殊的运行模式,用于生产
// - 生产模式下,将会输出 InfoLevel 及以上级别的日志到控制台和文件
RunModeProd
)
type RunMode uint8