diff --git a/go.mod b/go.mod index 5be7bc6..c7855df 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/panjf2000/ants/v2 v2.8.1 github.com/panjf2000/gnet v1.6.7 + github.com/pkg/errors v0.9.1 github.com/smartystreets/goconvey v1.8.1 github.com/sony/sonyflake v1.2.0 github.com/spf13/cobra v1.7.0 @@ -21,10 +22,8 @@ require ( github.com/tidwall/gjson v1.16.0 github.com/xtaci/kcp-go/v5 v5.6.3 go.uber.org/atomic v1.10.0 - go.uber.org/zap v1.25.0 golang.org/x/crypto v0.17.0 google.golang.org/grpc v1.59.0 - gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( @@ -49,7 +48,6 @@ require ( 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 - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/smarty/assertions v1.15.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -62,6 +60,7 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.25.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.17.0 // indirect @@ -70,5 +69,6 @@ require ( golang.org/x/text v0.14.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/server/server.go b/server/server.go index 86c2840..fed11f3 100644 --- a/server/server.go +++ b/server/server.go @@ -9,6 +9,7 @@ import ( "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/network" + "github.com/kercylan98/minotaur/utils/sher" "github.com/kercylan98/minotaur/utils/str" "github.com/kercylan98/minotaur/utils/super" "github.com/kercylan98/minotaur/utils/timer" @@ -451,7 +452,7 @@ func (srv *Server) low(message *Message, present time.Time, expect time.Duration fields = append(fields, log.String("type", messageNames[message.t]), log.String("cost", cost.String()), log.String("message", message.String())) fields = append(fields, message.marks...) //fields = append(fields, log.Stack("stack")) - log.Warn("ServerLowMessage", fields...) + log.Warn("ServerLowMessage", sher.SliceCastToAny(fields)...) srv.OnMessageLowExecEvent(message, cost) } } @@ -742,14 +743,14 @@ func showServersInfo(mark string, servers ...*Server) { for _, srv := range servers { srv := srv serverInfos = append(serverInfos, func() { - log.Info("Server", log.String(mark, "RunningInfo"), log.Any("network", srv.network), log.String("ip", ip.String()), log.String("listen", srv.addr)) + log.Info(mark, log.String("", "RunningInfo"), log.Any("network", srv.network), log.String("ip", ip.String()), log.String("listen", srv.addr)) }) } - log.Info("Server", log.String(mark, "====================================================================")) + log.Info(mark, log.String("", "====================================================================")) for _, info := range serverInfos { info() } - log.Info("Server", log.String(mark, "====================================================================")) + log.Info(mark, log.String("", "====================================================================")) } // onServicesInit 服务初始化 diff --git a/utils/log/buffer.go b/utils/log/buffer.go new file mode 100644 index 0000000..4cb2c9e --- /dev/null +++ b/utils/log/buffer.go @@ -0,0 +1,56 @@ +package log + +import "sync" + +type buffer struct { + bytes *[]byte + disableColor bool +} + +var bufPool = sync.Pool{ + New: func() any { + buf := make([]byte, 0, 1024) + return &buffer{ + bytes: &buf, + } + }, +} + +func newBuffer(handler *handler) *buffer { + buf := bufPool.Get().(*buffer) + buf.disableColor = handler.opts.DisableColor + return buf +} + +func (b *buffer) Free() { + const maxBufferSize = 16 << 10 + if cap(*b.bytes) <= maxBufferSize { + *b.bytes = (*b.bytes)[:0] + b.disableColor = false + bufPool.Put(b) + } +} +func (b *buffer) Write(bytes []byte) *buffer { + *b.bytes = append(*b.bytes, bytes...) + return b +} + +func (b *buffer) WriteBytes(char ...byte) *buffer { + *b.bytes = append(*b.bytes, char...) + return b +} + +func (b *buffer) WriteString(str string) *buffer { + *b.bytes = append(*b.bytes, str...) + return b +} + +func (b *buffer) WriteColorString(str ...string) *buffer { + for _, s := range str { + _, isColor := colors[s] + if !isColor || !b.disableColor { + b.WriteString(s) + } + } + return b +} diff --git a/utils/log/caller.go b/utils/log/caller.go new file mode 100644 index 0000000..6ae415a --- /dev/null +++ b/utils/log/caller.go @@ -0,0 +1,11 @@ +package log + +import ( + "path/filepath" + "strconv" +) + +// CallerBasicFormat 返回调用者的基本格式 +func CallerBasicFormat(file string, line int) (repFile, refLine string) { + return filepath.Base(file), strconv.Itoa(line) +} diff --git a/utils/log/color.go b/utils/log/color.go new file mode 100644 index 0000000..dd0626c --- /dev/null +++ b/utils/log/color.go @@ -0,0 +1,338 @@ +package log + +const ( + ColorDefault = "\033[0m" // 默认 + ColorDefaultBold = "\033[1m" // 默认加粗 + ColorDefaultUnderline = "\033[4m" // 默认下划线 + ColorDefaultReverse = "\033[7m" // 默认反显 + ColorDefaultDelete = "\033[9m" // 默认删除线 + ColorBlack = "\033[30m" // 黑色 + ColorRed = "\033[31m" // 红色 + ColorGreen = "\033[32m" // 绿色 + ColorYellow = "\033[33m" // 黄色 + ColorBlue = "\033[34m" // 蓝色 + ColorPurple = "\033[35m" // 紫色 + ColorCyan = "\033[36m" // 青色 + ColorWhite = "\033[37m" // 白色 + ColorBgBlack = "\033[40m" // 背景黑色 + ColorBgRed = "\033[41m" // 背景红色 + ColorBgGreen = "\033[42m" // 背景绿色 + ColorBgYellow = "\033[43m" // 背景黄色 + ColorBgBlue = "\033[44m" // 背景蓝色 + ColorBgPurple = "\033[45m" // 背景紫色 + ColorBgCyan = "\033[46m" // 背景青色 + ColorBgWhite = "\033[47m" // 背景白色 + ColorBrightBlack = "\033[90m" // 亮黑色 + ColorBrightRed = "\033[91m" // 亮红色 + ColorBrightGreen = "\033[92m" // 亮绿色 + ColorBrightYellow = "\033[93m" // 亮黄色 + ColorBrightBlue = "\033[94m" // 亮蓝色 + ColorBrightPurple = "\033[95m" // 亮紫色 + ColorBrightCyan = "\033[96m" // 亮青色 + ColorBrightWhite = "\033[97m" // 亮白色 + ColorBgBrightBlack = "\033[100m" // 背景亮黑色 + ColorBgBrightRed = "\033[101m" // 背景亮红色 + ColorBgBrightGreen = "\033[102m" // 背景亮绿色 + ColorBgBrightYellow = "\033[103m" // 背景亮黄色 + ColorBgBrightBlue = "\033[104m" // 背景亮蓝色 + ColorBgBrightPurple = "\033[105m" // 背景亮紫色 + ColorBgBrightCyan = "\033[106m" // 背景亮青色 + ColorBgBrightWhite = "\033[107m" // 背景亮白色 + + ColorBlackBold = "\033[1;30m" // 黑色加粗 + ColorRedBold = "\033[1;31m" // 红色加粗 + ColorGreenBold = "\033[1;32m" // 绿色加粗 + ColorYellowBold = "\033[1;33m" // 黄色加粗 + ColorBlueBold = "\033[1;34m" // 蓝色加粗 + ColorPurpleBold = "\033[1;35m" // 紫色加粗 + ColorCyanBold = "\033[1;36m" // 青色加粗 + ColorWhiteBold = "\033[1;37m" // 白色加粗 + ColorBgBlackBold = "\033[1;40m" // 背景黑色加粗 + ColorBgRedBold = "\033[1;41m" // 背景红色加粗 + ColorBgGreenBold = "\033[1;42m" // 背景绿色加粗 + ColorBgYellowBold = "\033[1;43m" // 背景黄色加粗 + ColorBgBlueBold = "\033[1;44m" // 背景蓝色加粗 + ColorBgPurpleBold = "\033[1;45m" // 背景紫色加粗 + ColorBgCyanBold = "\033[1;46m" // 背景青色加粗 + ColorBgWhiteBold = "\033[1;47m" // 背景白色加粗 + ColorBrightBlackBold = "\033[1;90m" // 亮黑色加粗 + ColorBrightRedBold = "\033[1;91m" // 亮红色加粗 + ColorBrightGreenBold = "\033[1;92m" // 亮绿色加粗 + ColorBrightYellowBold = "\033[1;93m" // 亮黄色加粗 + ColorBrightBlueBold = "\033[1;94m" // 亮蓝色加粗 + ColorBrightPurpleBold = "\033[1;95m" // 亮紫色加粗 + ColorBrightCyanBold = "\033[1;96m" // 亮青色加粗 + ColorBrightWhiteBold = "\033[1;97m" // 亮白色加粗 + ColorBgBrightBlackBold = "\033[1;100m" // 背景亮黑色加粗 + ColorBgBrightRedBold = "\033[1;101m" // 背景亮红色加粗 + ColorBgBrightGreenBold = "\033[1;102m" // 背景亮绿色加粗 + ColorBgBrightYellowBold = "\033[1;103m" // 背景亮黄色加粗 + ColorBgBrightBlueBold = "\033[1;104m" // 背景亮蓝色加粗 + ColorBgBrightPurpleBold = "\033[1;105m" // 背景亮紫色加粗 + ColorBgBrightCyanBold = "\033[1;106m" // 背景亮青色加粗 + ColorBgBrightWhiteBold = "\033[1;107m" // 背景亮白色加粗 + ColorBlackUnderline = "\033[4;30m" // 黑色下划线 + ColorRedUnderline = "\033[4;31m" // 红色下划线 + ColorGreenUnderline = "\033[4;32m" // 绿色下划线 + ColorYellowUnderline = "\033[4;33m" // 黄色下划线 + ColorBlueUnderline = "\033[4;34m" // 蓝色下划线 + ColorPurpleUnderline = "\033[4;35m" // 紫色下划线 + ColorCyanUnderline = "\033[4;36m" // 青色下划线 + ColorWhiteUnderline = "\033[4;37m" // 白色下划线 + ColorBgBlackUnderline = "\033[4;40m" // 背景黑色下划线 + ColorBgRedUnderline = "\033[4;41m" // 背景红色下划线 + ColorBgGreenUnderline = "\033[4;42m" // 背景绿色下划线 + ColorBgYellowUnderline = "\033[4;43m" // 背景黄色下划线 + ColorBgBlueUnderline = "\033[4;44m" // 背景蓝色下划线 + ColorBgPurpleUnderline = "\033[4;45m" // 背景紫色下划线 + ColorBgCyanUnderline = "\033[4;46m" // 背景青色下划线 + ColorBgWhiteUnderline = "\033[4;47m" // 背景白色下划线 + ColorBrightBlackUnderline = "\033[4;90m" // 亮黑色下划线 + ColorBrightRedUnderline = "\033[4;91m" // 亮红色下划线 + ColorBrightGreenUnderline = "\033[4;92m" // 亮绿色下划线 + ColorBrightYellowUnderline = "\033[4;93m" // 亮黄色下划线 + ColorBrightBlueUnderline = "\033[4;94m" // 亮蓝色下划线 + ColorBrightPurpleUnderline = "\033[4;95m" // 亮紫色下划线 + ColorBrightCyanUnderline = "\033[4;96m" // 亮青色下划线 + ColorBrightWhiteUnderline = "\033[4;97m" // 亮白色下划线 + ColorBgBrightBlackUnderline = "\033[4;100m" // 背景亮黑色下划线 + ColorBgBrightRedUnderline = "\033[4;101m" // 背景亮红色下划线 + ColorBgBrightGreenUnderline = "\033[4;102m" // 背景亮绿色下划线 + ColorBgBrightYellowUnderline = "\033[4;103m" // 背景亮黄色下划线 + ColorBgBrightBlueUnderline = "\033[4;104m" // 背景亮蓝色下划线 + ColorBgBrightPurpleUnderline = "\033[4;105m" // 背景亮紫色下划线 + ColorBgBrightCyanUnderline = "\033[4;106m" // 背景亮青色下划线 + ColorBgBrightWhiteUnderline = "\033[4;107m" // 背景亮白色下划线 + ColorBlackReverse = "\033[7;30m" // 黑色反显 + ColorRedReverse = "\033[7;31m" // 红色反显 + ColorGreenReverse = "\033[7;32m" // 绿色反显 + ColorYellowReverse = "\033[7;33m" // 黄色反显 + ColorBlueReverse = "\033[7;34m" // 蓝色反显 + ColorPurpleReverse = "\033[7;35m" // 紫色反显 + ColorCyanReverse = "\033[7;36m" // 青色反显 + ColorWhiteReverse = "\033[7;37m" // 白色反显 + ColorBgBlackReverse = "\033[7;40m" // 背景黑色反显 + ColorBgRedReverse = "\033[7;41m" // 背景红色反显 + ColorBgGreenReverse = "\033[7;42m" // 背景绿色反显 + ColorBgYellowReverse = "\033[7;43m" // 背景黄色反显 + ColorBgBlueReverse = "\033[7;44m" // 背景蓝色反显 + ColorBgPurpleReverse = "\033[7;45m" // 背景紫色反显 + ColorBgCyanReverse = "\033[7;46m" // 背景青色反显 + ColorBgWhiteReverse = "\033[7;47m" // 背景白色反显 + ColorBrightBlackReverse = "\033[7;90m" // 亮黑色反显 + ColorBrightRedReverse = "\033[7;91m" // 亮红色反显 + ColorBrightGreenReverse = "\033[7;92m" // 亮绿色反显 + ColorBrightYellowReverse = "\033[7;93m" // 亮黄色反显 + ColorBrightBlueReverse = "\033[7;94m" // 亮蓝色反显 + ColorBrightPurpleReverse = "\033[7;95m" // 亮紫色反显 + ColorBrightCyanReverse = "\033[7;96m" // 亮青色反显 + ColorBrightWhiteReverse = "\033[7;97m" // 亮白色反显 + ColorBgBrightBlackReverse = "\033[7;100m" // 背景亮黑色反显 + ColorBgBrightRedReverse = "\033[7;101m" // 背景亮红色反显 + ColorBgBrightGreenReverse = "\033[7;102m" // 背景亮绿色反显 + ColorBgBrightYellowReverse = "\033[7;103m" // 背景亮黄色反显 + ColorBgBrightBlueReverse = "\033[7;104m" // 背景亮蓝色反显 + ColorBgBrightPurpleReverse = "\033[7;105m" // 背景亮紫色反显 + ColorBgBrightCyanReverse = "\033[7;106m" // 背景亮青色反显 + ColorBgBrightWhiteReverse = "\033[7;107m" // 背景亮白色反显 + StrikeThroughBlack = "\033[9;30m" // 黑色删除线 + StrikeThroughRed = "\033[9;31m" // 红色删除线 + StrikeThroughGreen = "\033[9;32m" // 绿色删除线 + StrikeThroughYellow = "\033[9;33m" // 黄色删除线 + StrikeThroughBlue = "\033[9;34m" // 蓝色删除线 + StrikeThroughPurple = "\033[9;35m" // 紫色删除线 + StrikeThroughCyan = "\033[9;36m" // 青色删除线 + StrikeThroughWhite = "\033[9;37m" // 白色删除线 + BgStrikeThroughBlack = "\033[9;40m" // 背景黑色删除线 + BgStrikeThroughRed = "\033[9;41m" // 背景红色删除线 + BgStrikeThroughGreen = "\033[9;42m" // 背景绿色删除线 + BgStrikeThroughYellow = "\033[9;43m" // 背景黄色删除线 + BgStrikeThroughBlue = "\033[9;44m" // 背景蓝色删除线 + BgStrikeThroughPurple = "\033[9;45m" // 背景紫色删除线 + BgStrikeThroughCyan = "\033[9;46m" // 背景青色删除线 + BgStrikeThroughWhite = "\033[9;47m" // 背景白色删除线 + BrightStrikeThroughBlack = "\033[9;90m" // 亮黑色删除线 + BrightStrikeThroughRed = "\033[9;91m" // 亮红色删除线 + BrightStrikeThroughGreen = "\033[9;92m" // 亮绿色删除线 + BrightStrikeThroughYellow = "\033[9;93m" // 亮黄色删除线 + BrightStrikeThroughBlue = "\033[9;94m" // 亮蓝色删除线 + BrightStrikeThroughPurple = "\033[9;95m" // 亮紫色删除线 + BrightStrikeThroughCyan = "\033[9;96m" // 亮青色删除线 + BrightStrikeThroughWhite = "\033[9;97m" // 亮白色删除线 + BgBrightStrikeThroughBlack = "\033[9;100m" // 背景亮黑色删除线 + BgBrightStrikeThroughRed = "\033[9;101m" // 背景亮红色删除线 + BgBrightStrikeThroughGreen = "\033[9;102m" // 背景亮绿色删除线 + BgBrightStrikeThroughYellow = "\033[9;103m" // 背景亮黄色删除线 + BgBrightStrikeThroughBlue = "\033[9;104m" // 背景亮蓝色删除线 + BgBrightStrikeThroughPurple = "\033[9;105m" // 背景亮紫色删除线 + BgBrightStrikeThroughCyan = "\033[9;106m" // 背景亮青色删除线 + BgBrightStrikeThroughWhite = "\033[9;107m" // 背景亮白色删除线 +) + +var colors = map[string]struct{}{ + ColorDefault: {}, + ColorDefaultBold: {}, + ColorDefaultUnderline: {}, + ColorDefaultReverse: {}, + ColorDefaultDelete: {}, + ColorBlack: {}, + ColorRed: {}, + ColorGreen: {}, + ColorYellow: {}, + ColorBlue: {}, + ColorPurple: {}, + ColorCyan: {}, + ColorWhite: {}, + ColorBgBlack: {}, + ColorBgRed: {}, + ColorBgGreen: {}, + ColorBgYellow: {}, + ColorBgBlue: {}, + ColorBgPurple: {}, + ColorBgCyan: {}, + ColorBgWhite: {}, + ColorBrightBlack: {}, + ColorBrightRed: {}, + ColorBrightGreen: {}, + ColorBrightYellow: {}, + ColorBrightBlue: {}, + ColorBrightPurple: {}, + ColorBrightCyan: {}, + ColorBrightWhite: {}, + ColorBgBrightBlack: {}, + ColorBgBrightRed: {}, + ColorBgBrightGreen: {}, + ColorBgBrightYellow: {}, + ColorBgBrightBlue: {}, + ColorBgBrightPurple: {}, + ColorBgBrightCyan: {}, + ColorBgBrightWhite: {}, + ColorBlackBold: {}, + ColorRedBold: {}, + ColorGreenBold: {}, + ColorYellowBold: {}, + ColorBlueBold: {}, + ColorPurpleBold: {}, + ColorCyanBold: {}, + ColorWhiteBold: {}, + ColorBgBlackBold: {}, + ColorBgRedBold: {}, + ColorBgGreenBold: {}, + ColorBgYellowBold: {}, + ColorBgBlueBold: {}, + ColorBgPurpleBold: {}, + ColorBgCyanBold: {}, + ColorBgWhiteBold: {}, + ColorBrightBlackBold: {}, + ColorBrightRedBold: {}, + ColorBrightGreenBold: {}, + ColorBrightYellowBold: {}, + ColorBrightBlueBold: {}, + ColorBrightPurpleBold: {}, + ColorBrightCyanBold: {}, + ColorBrightWhiteBold: {}, + ColorBgBrightBlackBold: {}, + ColorBgBrightRedBold: {}, + ColorBgBrightGreenBold: {}, + ColorBgBrightYellowBold: {}, + ColorBgBrightBlueBold: {}, + ColorBgBrightPurpleBold: {}, + ColorBgBrightCyanBold: {}, + ColorBgBrightWhiteBold: {}, + ColorBlackUnderline: {}, + ColorRedUnderline: {}, + ColorGreenUnderline: {}, + ColorYellowUnderline: {}, + ColorBlueUnderline: {}, + ColorPurpleUnderline: {}, + ColorCyanUnderline: {}, + ColorWhiteUnderline: {}, + ColorBgBlackUnderline: {}, + ColorBgRedUnderline: {}, + ColorBgGreenUnderline: {}, + ColorBgYellowUnderline: {}, + ColorBgBlueUnderline: {}, + ColorBgPurpleUnderline: {}, + ColorBgCyanUnderline: {}, + ColorBgWhiteUnderline: {}, + ColorBrightBlackUnderline: {}, + ColorBrightRedUnderline: {}, + ColorBrightGreenUnderline: {}, + ColorBrightYellowUnderline: {}, + ColorBrightBlueUnderline: {}, + ColorBrightPurpleUnderline: {}, + ColorBrightCyanUnderline: {}, + ColorBrightWhiteUnderline: {}, + ColorBgBrightBlackUnderline: {}, + ColorBgBrightRedUnderline: {}, + ColorBgBrightGreenUnderline: {}, + ColorBgBrightYellowUnderline: {}, + ColorBgBrightBlueUnderline: {}, + ColorBgBrightPurpleUnderline: {}, + ColorBgBrightCyanUnderline: {}, + ColorBgBrightWhiteUnderline: {}, + ColorBlackReverse: {}, + ColorRedReverse: {}, + ColorGreenReverse: {}, + ColorYellowReverse: {}, + ColorBlueReverse: {}, + ColorPurpleReverse: {}, + ColorCyanReverse: {}, + ColorWhiteReverse: {}, + ColorBgBlackReverse: {}, + ColorBgRedReverse: {}, + ColorBgGreenReverse: {}, + ColorBgYellowReverse: {}, + ColorBgBlueReverse: {}, + ColorBgPurpleReverse: {}, + ColorBgCyanReverse: {}, + ColorBgWhiteReverse: {}, + ColorBrightBlackReverse: {}, + ColorBrightRedReverse: {}, + ColorBrightGreenReverse: {}, + ColorBrightYellowReverse: {}, + ColorBrightBlueReverse: {}, + ColorBrightPurpleReverse: {}, + ColorBrightCyanReverse: {}, + ColorBrightWhiteReverse: {}, + ColorBgBrightBlackReverse: {}, + ColorBgBrightRedReverse: {}, + ColorBgBrightGreenReverse: {}, + ColorBgBrightYellowReverse: {}, + ColorBgBrightBlueReverse: {}, + ColorBgBrightPurpleReverse: {}, + ColorBgBrightCyanReverse: {}, + ColorBgBrightWhiteReverse: {}, + StrikeThroughBlack: {}, + StrikeThroughRed: {}, + StrikeThroughGreen: {}, + StrikeThroughYellow: {}, + StrikeThroughBlue: {}, + StrikeThroughPurple: {}, + StrikeThroughCyan: {}, + StrikeThroughWhite: {}, + BgStrikeThroughBlack: {}, + BgStrikeThroughRed: {}, + BgStrikeThroughGreen: {}, + BgStrikeThroughYellow: {}, + BgStrikeThroughBlue: {}, + BgStrikeThroughPurple: {}, + BgStrikeThroughCyan: {}, + BgStrikeThroughWhite: {}, + BrightStrikeThroughBlack: {}, + BrightStrikeThroughRed: {}, + BrightStrikeThroughGreen: {}, + BrightStrikeThroughYellow: {}, + BrightStrikeThroughBlue: {}, + BrightStrikeThroughPurple: {}, + BrightStrikeThroughCyan: {}, + BrightStrikeThroughWhite: {}, + BgBrightStrikeThroughBlack: {}, + BgBrightStrikeThroughRed: {}, + BgBrightStrikeThroughGreen: {}, + BgBrightStrikeThroughYellow: {}, + BgBrightStrikeThroughBlue: {}, + BgBrightStrikeThroughPurple: {}, + BgBrightStrikeThroughCyan: {}, + BgBrightStrikeThroughWhite: {}, +} diff --git a/utils/log/color_test.go b/utils/log/color_test.go new file mode 100644 index 0000000..0b83d60 --- /dev/null +++ b/utils/log/color_test.go @@ -0,0 +1,230 @@ +package log + +import ( + "errors" + "fmt" + "testing" +) + +func TestColor(t *testing.T) { + var colors []string + var desc []string + var Define = func(str, d string) string { + colors = append(colors, str) + desc = append(desc, d) + return str + } + + Define("\033[0m", "ColorDefault 默认") + Define("\033[1m", "ColorDefaultBold 默认加粗") + Define("\033[4m", "ColorDefaultUnderline 默认下划线") + Define("\033[7m", "ColorDefaultReverse 默认反显") + Define("\033[9m", "ColorDefaultDelete 默认删除线") + Define("\033[30m", "ColorBlack 黑色") + Define("\033[31m", "ColorRed 红色") + Define("\033[32m", "ColorGreen 绿色") + Define("\033[33m", "ColorYellow 黄色") + Define("\033[34m", "ColorBlue 蓝色") + Define("\033[35m", "ColorPurple 紫色") + Define("\033[36m", "ColorCyan 青色") + Define("\033[37m", "ColorWhite 白色") + Define("\033[40m", "ColorBgBlack 背景黑色") + Define("\033[41m", "ColorBgRed 背景红色") + Define("\033[42m", "ColorBgGreen 背景绿色") + Define("\033[43m", "ColorBgYellow 背景黄色") + Define("\033[44m", "ColorBgBlue 背景蓝色") + Define("\033[45m", "ColorBgPurple 背景紫色") + Define("\033[46m", "ColorBgCyan 背景青色") + Define("\033[47m", "ColorBgWhite 背景白色") + Define("\033[90m", "ColorBrightBlack 亮黑色") + Define("\033[91m", "ColorBrightRed 亮红色") + Define("\033[92m", "ColorBrightGreen 亮绿色") + Define("\033[93m", "ColorBrightYellow 亮黄色") + Define("\033[94m", "ColorBrightBlue 亮蓝色") + Define("\033[95m", "ColorBrightPurple 亮紫色") + Define("\033[96m", "ColorBrightCyan 亮青色") + Define("\033[97m", "ColorBrightWhite 亮白色") + Define("\033[100m", "ColorBgBrightBlack 背景亮黑色") + Define("\033[101m", "ColorBgBrightRed 背景亮红色") + Define("\033[102m", "ColorBgBrightGreen 背景亮绿色") + Define("\033[103m", "ColorBgBrightYellow 背景亮黄色") + Define("\033[104m", "ColorBgBrightBlue 背景亮蓝色") + Define("\033[105m", "ColorBgBrightPurple 背景亮紫色") + Define("\033[106m", "ColorBgBrightCyan 背景亮青色") + Define("\033[107m", "ColorBgBrightWhite 背景亮白色") + Define("\033[1;30m", "ColorBlackBold 黑色加粗") + Define("\033[1;31m", "ColorRedBold 红色加粗") + Define("\033[1;32m", "ColorGreenBold 绿色加粗") + Define("\033[1;33m", "ColorYellowBold 黄色加粗") + Define("\033[1;34m", "ColorBlueBold 蓝色加粗") + Define("\033[1;35m", "ColorPurpleBold 紫色加粗") + Define("\033[1;36m", "ColorCyanBold 青色加粗") + Define("\033[1;37m", "ColorWhiteBold 白色加粗") + Define("\033[1;40m", "ColorBgBlackBold 背景黑色加粗") + Define("\033[1;41m", "ColorBgRedBold 背景红色加粗") + Define("\033[1;42m", "ColorBgGreenBold 背景绿色加粗") + Define("\033[1;43m", "ColorBgYellowBold 背景黄色加粗") + Define("\033[1;44m", "ColorBgBlueBold 背景蓝色加粗") + Define("\033[1;45m", "ColorBgPurpleBold 背景紫色加粗") + Define("\033[1;46m", "ColorBgCyanBold 背景青色加粗") + Define("\033[1;47m", "ColorBgWhiteBold 背景白色加粗") + Define("\033[1;90m", "ColorBrightBlackBold 亮黑色加粗") + Define("\033[1;91m", "ColorBrightRedBold 亮红色加粗") + Define("\033[1;92m", "ColorBrightGreenBold 亮绿色加粗") + Define("\033[1;93m", "ColorBrightYellowBold 亮黄色加粗") + Define("\033[1;94m", "ColorBrightBlueBold 亮蓝色加粗") + Define("\033[1;95m", "ColorBrightPurpleBold 亮紫色加粗") + Define("\033[1;96m", "ColorBrightCyanBold 亮青色加粗") + Define("\033[1;97m", "ColorBrightWhiteBold 亮白色加粗") + Define("\033[1;100m", "ColorBgBrightBlackBold 背景亮黑色加粗") + Define("\033[1;101m", "ColorBgBrightRedBold 背景亮红色加粗") + Define("\033[1;102m", "ColorBgBrightGreenBold 背景亮绿色加粗") + Define("\033[1;103m", "ColorBgBrightYellowBold 背景亮黄色加粗") + Define("\033[1;104m", "ColorBgBrightBlueBold 背景亮蓝色加粗") + Define("\033[1;105m", "ColorBgBrightPurpleBold 背景亮紫色加粗") + Define("\033[1;106m", "ColorBgBrightCyanBold 背景亮青色加粗") + Define("\033[1;107m", "ColorBgBrightWhiteBold 背景亮白色加粗") + Define("\033[4;30m", "ColorBlackUnderline 黑色下划线") + Define("\033[4;31m", "ColorRedUnderline 红色下划线") + Define("\033[4;32m", "ColorGreenUnderline 绿色下划线") + Define("\033[4;33m", "ColorYellowUnderline 黄色下划线") + Define("\033[4;34m", "ColorBlueUnderline 蓝色下划线") + Define("\033[4;35m", "ColorPurpleUnderline 紫色下划线") + Define("\033[4;36m", "ColorCyanUnderline 青色下划线") + Define("\033[4;37m", "ColorWhiteUnderline 白色下划线") + Define("\033[4;40m", "ColorBgBlackUnderline 背景黑色下划线") + Define("\033[4;41m", "ColorBgRedUnderline 背景红色下划线") + Define("\033[4;42m", "ColorBgGreenUnderline 背景绿色下划线") + Define("\033[4;43m", "ColorBgYellowUnderline 背景黄色下划线") + Define("\033[4;44m", "ColorBgBlueUnderline 背景蓝色下划线") + Define("\033[4;45m", "ColorBgPurpleUnderline 背景紫色下划线") + Define("\033[4;46m", "ColorBgCyanUnderline 背景青色下划线") + Define("\033[4;47m", "ColorBgWhiteUnderline 背景白色下划线") + Define("\033[4;90m", "ColorBrightBlackUnderline 亮黑色下划线") + Define("\033[4;91m", "ColorBrightRedUnderline 亮红色下划线") + Define("\033[4;92m", "ColorBrightGreenUnderline 亮绿色下划线") + Define("\033[4;93m", "ColorBrightYellowUnderline 亮黄色下划线") + Define("\033[4;94m", "ColorBrightBlueUnderline 亮蓝色下划线") + Define("\033[4;95m", "ColorBrightPurpleUnderline 亮紫色下划线") + Define("\033[4;96m", "ColorBrightCyanUnderline 亮青色下划线") + Define("\033[4;97m", "ColorBrightWhiteUnderline 亮白色下划线") + Define("\033[4;100m", "ColorBgBrightBlackUnderline 背景亮黑色下划线") + Define("\033[4;101m", "ColorBgBrightRedUnderline 背景亮红色下划线") + Define("\033[4;102m", "ColorBgBrightGreenUnderline 背景亮绿色下划线") + Define("\033[4;103m", "ColorBgBrightYellowUnderline 背景亮黄色下划线") + Define("\033[4;104m", "ColorBgBrightBlueUnderline 背景亮蓝色下划线") + Define("\033[4;105m", "ColorBgBrightPurpleUnderline 背景亮紫色下划线") + Define("\033[4;106m", "ColorBgBrightCyanUnderline 背景亮青色下划线") + Define("\033[4;107m", "ColorBgBrightWhiteUnderline 背景亮白色下划线") + Define("\033[7;30m", "ColorBlackReverse 黑色反显") + Define("\033[7;31m", "ColorRedReverse 红色反显") + Define("\033[7;32m", "ColorGreenReverse 绿色反显") + Define("\033[7;33m", "ColorYellowReverse 黄色反显") + Define("\033[7;34m", "ColorBlueReverse 蓝色反显") + Define("\033[7;35m", "ColorPurpleReverse 紫色反显") + Define("\033[7;36m", "ColorCyanReverse 青色反显") + Define("\033[7;37m", "ColorWhiteReverse 白色反显") + Define("\033[7;40m", "ColorBgBlackReverse 背景黑色反显") + Define("\033[7;41m", "ColorBgRedReverse 背景红色反显") + Define("\033[7;42m", "ColorBgGreenReverse 背景绿色反显") + Define("\033[7;43m", "ColorBgYellowReverse 背景黄色反显") + Define("\033[7;44m", "ColorBgBlueReverse 背景蓝色反显") + Define("\033[7;45m", "ColorBgPurpleReverse 背景紫色反显") + Define("\033[7;46m", "ColorBgCyanReverse 背景青色反显") + Define("\033[7;47m", "ColorBgWhiteReverse 背景白色反显") + Define("\033[7;90m", "ColorBrightBlackReverse 亮黑色反显") + Define("\033[7;91m", "ColorBrightRedReverse 亮红色反显") + Define("\033[7;92m", "ColorBrightGreenReverse 亮绿色反显") + Define("\033[7;93m", "ColorBrightYellowReverse 亮黄色反显") + Define("\033[7;94m", "ColorBrightBlueReverse 亮蓝色反显") + Define("\033[7;95m", "ColorBrightPurpleReverse 亮紫色反显") + Define("\033[7;96m", "ColorBrightCyanReverse 亮青色反显") + Define("\033[7;97m", "ColorBrightWhiteReverse 亮白色反显") + Define("\033[7;100m", "ColorBgBrightBlackReverse 背景亮黑色反显") + Define("\033[7;101m", "ColorBgBrightRedReverse 背景亮红色反显") + Define("\033[7;102m", "ColorBgBrightGreenReverse 背景亮绿色反显") + Define("\033[7;103m", "ColorBgBrightYellowReverse 背景亮黄色反显") + Define("\033[7;104m", "ColorBgBrightBlueReverse 背景亮蓝色反显") + Define("\033[7;105m", "ColorBgBrightPurpleReverse 背景亮紫色反显") + Define("\033[7;106m", "ColorBgBrightCyanReverse 背景亮青色反显") + Define("\033[7;107m", "ColorBgBrightWhiteReverse 背景亮白色反显") + Define("\033[9;30m", "StrikeThroughBlack 黑色删除线") + Define("\033[9;31m", "StrikeThroughRed 红色删除线") + Define("\033[9;32m", "StrikeThroughGreen 绿色删除线") + Define("\033[9;33m", "StrikeThroughYellow 黄色删除线") + Define("\033[9;34m", "StrikeThroughBlue 蓝色删除线") + Define("\033[9;35m", "StrikeThroughPurple 紫色删除线") + Define("\033[9;36m", "StrikeThroughCyan 青色删除线") + Define("\033[9;37m", "StrikeThroughWhite 白色删除线") + Define("\033[9;40m", "BgStrikeThroughBlack 背景黑色删除线") + Define("\033[9;41m", "BgStrikeThroughRed 背景红色删除线") + Define("\033[9;42m", "BgStrikeThroughGreen 背景绿色删除线") + Define("\033[9;43m", "BgStrikeThroughYellow 背景黄色删除线") + Define("\033[9;44m", "BgStrikeThroughBlue 背景蓝色删除线") + Define("\033[9;45m", "BgStrikeThroughPurple 背景紫色删除线") + Define("\033[9;46m", "BgStrikeThroughCyan 背景青色删除线") + Define("\033[9;47m", "BgStrikeThroughWhite 背景白色删除线") + Define("\033[9;90m", "BrightStrikeThroughBlack 亮黑色删除线") + Define("\033[9;91m", "BrightStrikeThroughRed 亮红色删除线") + Define("\033[9;92m", "BrightStrikeThroughGreen 亮绿色删除线") + Define("\033[9;93m", "BrightStrikeThroughYellow 亮黄色删除线") + Define("\033[9;94m", "BrightStrikeThroughBlue 亮蓝色删除线") + Define("\033[9;95m", "BrightStrikeThroughPurple 亮紫色删除线") + Define("\033[9;96m", "BrightStrikeThroughCyan 亮青色删除线") + Define("\033[9;97m", "BrightStrikeThroughWhite 亮白色删除线") + Define("\033[9;100m", "BgBrightStrikeThroughBlack 背景亮黑色删除线") + Define("\033[9;101m", "BgBrightStrikeThroughRed 背景亮红色删除线") + Define("\033[9;102m", "BgBrightStrikeThroughGreen 背景亮绿色删除线") + Define("\033[9;103m", "BgBrightStrikeThroughYellow 背景亮黄色删除线") + Define("\033[9;104m", "BgBrightStrikeThroughBlue 背景亮蓝色删除线") + Define("\033[9;105m", "BgBrightStrikeThroughPurple 背景亮紫色删除线") + Define("\033[9;106m", "BgBrightStrikeThroughCyan 背景亮青色删除线") + Define("\033[9;107m", "BgBrightStrikeThroughWhite 背景亮白色删除线") + Define("\033[1;4;30m", "BoldUnderlineBlack 黑色加粗下划线") + Define("\033[1;4;31m", "BoldUnderlineRed 红色加粗下划线") + Define("\033[1;4;32m", "BoldUnderlineGreen 绿色加粗下划线") + Define("\033[1;4;33m", "BoldUnderlineYellow 黄色加粗下划线") + Define("\033[1;4;34m", "BoldUnderlineBlue 蓝色加粗下划线") + Define("\033[1;4;35m", "BoldUnderlinePurple 紫色加粗下划线") + Define("\033[1;4;36m", "BoldUnderlineCyan 青色加粗下划线") + Define("\033[1;4;37m", "BoldUnderlineWhite 白色加粗下划线") + Define("\033[1;4;40m", "BgBoldUnderlineBlack 背景黑色加粗下划线") + Define("\033[1;4;41m", "BgBoldUnderlineRed 背景红色加粗下划线") + Define("\033[1;4;42m", "BgBoldUnderlineGreen 背景绿色加粗下划线") + Define("\033[1;4;43m", "BgBoldUnderlineYellow 背景黄色加粗下划线") + Define("\033[1;4;44m", "BgBoldUnderlineBlue 背景蓝色加粗下划线") + Define("\033[1;4;45m", "BgBoldUnderlinePurple 背景紫色加粗下划线") + Define("\033[1;4;46m", "BgBoldUnderlineCyan 背景青色加粗下划线") + Define("\033[1;4;47m", "BgBoldUnderlineWhite 背景白色加粗下划线") + Define("\033[1;4;90m", "BoldBrightUnderlineBlack 亮黑色加粗下划线") + Define("\033[1;4;91m", "BoldBrightUnderlineRed 亮红色加粗下划线") + Define("\033[1;4;92m", "BoldBrightUnderlineGreen 亮绿色加粗下划线") + Define("\033[1;4;93m", "BoldBrightUnderlineYellow 亮黄色加粗下划线") + Define("\033[1;4;94m", "BoldBrightUnderlineBlue 亮蓝色加粗下划线") + Define("\033[1;4;95m", "BoldBrightUnderlinePurple 亮紫色加粗下划线") + Define("\033[1;4;96m", "BoldBrightUnderlineCyan 亮青色加粗下划线") + Define("\033[1;4;97m", "BoldBrightUnderlineWhite 亮白色加粗下划线") + Define("\033[1;4;100m", "BgBoldBrightUnderlineBlack 背景亮黑色加粗下划线") + Define("\033[1;4;101m", "BgBoldBrightUnderlineRed 背景亮红色加粗下划线") + Define("\033[1;4;102m", "BgBoldBrightUnderlineGreen 背景亮绿色加粗下划线") + Define("\033[1;4;103m", "BgBoldBrightUnderlineYellow 背景亮黄色加粗下划线") + Define("\033[1;4;104m", "BgBoldBrightUnderlineBlue 背景亮蓝色加粗下划线") + Define("\033[1;4;105m", "BgBoldBrightUnderlinePurple 背景亮紫色加粗下划线") + Define("\033[1;4;106m", "BgBoldBrightUnderlineCyan 背景亮青色加粗下划线") + Define("\033[1;4;107m", "BgBoldBrightUnderlineWhite 背景亮白色加粗下划线") + + Debug("TestStack", String("UserName", "Kara"), Err(errors.New("record not found"))) + Info("TestStack") + Warn("TestStack") + Error("TestStack") + + fmt.Println() + + for i, colorString := range colors { + Println("Hello World!", colorString, desc[i]) + } +} + +func Println(str string, color string, desc string) { + fmt.Println(fmt.Sprintf("%s%s\033[0m %s", color, str, desc)) +} diff --git a/utils/log/default.go b/utils/log/default.go new file mode 100644 index 0000000..e06b5d6 --- /dev/null +++ b/utils/log/default.go @@ -0,0 +1,80 @@ +package log + +import ( + "context" + "errors" + "log/slog" + "os" + "runtime" + "sync/atomic" + "time" +) + +var logger atomic.Pointer[Logger] + +func init() { + logger.Store(NewLogger()) +} + +// Default 获取默认的日志记录器 +func Default() *Logger { + return logger.Load() +} + +// SetDefault 设置默认的日志记录器 +func SetDefault(l *Logger) { + logger.Store(l) +} + +// Debug 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +func Debug(msg string, args ...any) { + handle(DebugLevel, msg, args...) +} + +// Info 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +func Info(msg string, args ...any) { + handle(InfoLevel, msg, args...) +} + +// Warn 在 WarnLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +func Warn(msg string, args ...any) { + handle(WarnLevel, msg, args...) +} + +// Error 在 ErrorLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +func Error(msg string, args ...any) { + handle(ErrorLevel, msg, args...) +} + +// DPanic 在 DPanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +// - 如果记录器处于开发模式,它就会出现 panic(DPanic 的意思是“development panic”)。这对于捕获可恢复但不应该发生的错误很有用 +func DPanic(msg string, args ...any) { + handle(DPanicLevel, msg, args...) +} + +// Panic 在 PanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +// - 即使禁用了 PanicLevel 的日志记录,记录器也会出现 panic +func Panic(msg string, args ...any) { + handle(PanicLevel, msg, args...) + panic(errors.New(msg)) +} + +// Fatal 在 FatalLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +// - 然后记录器调用 os.Exit(1),即使 FatalLevel 的日志记录被禁用 +func Fatal(msg string, args ...any) { + handle(FatalLevel, msg, args...) + os.Exit(1) +} + +// handle 在指定的级别记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 +func handle(level slog.Level, msg string, args ...any) { + d := Default() + pcs := make([]uintptr, 1) + runtime.CallersFrames(pcs[:runtime.Callers(d.opts.CallerSkip, pcs)]) + r := slog.NewRecord(time.Now(), level, msg, pcs[0]) + r.Add(args...) + _ = d.Handler().Handle(context.Background(), r) + if level == DPanicLevel && d.opts.DevMode { + panic(errors.New(msg)) + } +} diff --git a/utils/log/encoder.go b/utils/log/encoder.go deleted file mode 100644 index 0ebcca5..0000000 --- a/utils/log/encoder.go +++ /dev/null @@ -1,41 +0,0 @@ -package log - -import ( - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/natefinch/lumberjack.v2" -) - -type Encoder struct { - e zapcore.Encoder - cores []Core - conf *Config -} - -func (slf *Encoder) Split(config *lumberjack.Logger, level LevelEnabler) *Encoder { - slf.cores = append(slf.cores, zapcore.NewCore(slf.e, - zapcore.NewMultiWriteSyncer(zapcore.AddSync(config)), - level)) - 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...) - options = append(options, zap.WrapCore(func(core zapcore.Core) zapcore.Core { - return zapcore.NewTee(append(slf.cores, core)...) - })) - l = l.WithOptions(options...) - return &Minotaur{ - Logger: l, - Sugared: l.Sugar(), - } -} diff --git a/utils/log/field.go b/utils/log/field.go index 5228882..e8c925e 100644 --- a/utils/log/field.go +++ b/utils/log/field.go @@ -1,168 +1,257 @@ package log import ( - "go.uber.org/zap" + "github.com/kercylan98/minotaur/utils/generic" + "github.com/pkg/errors" + "log/slog" "time" ) -type Field = zap.Field +type Field = slog.Attr -var ( - // Skip 构造一个无操作字段,这在处理其他 Field 构造函数中的无效输入时通常很有用 - Skip = zap.Skip +// Skip 构造一个无操作字段,这在处理其他 Field 构造函数中的无效输入时通常很有用 +// - 该函数还支持将其他字段快捷的转换为 Skip 字段 +func Skip(vs ...any) slog.Attr { + return slog.Attr{Key: ""} +} - // Binary 构造一个携带不透明二进制 blob 的字段。二进制数据以适合编码的格式进行序列化。例如,JSON 编码器对二进制 blob 进行 base64 编码。要记录 UTF-8 编码文本,请使用 ByteString - Binary = zap.Binary +// Duration 使用给定的键和值构造一个字段。编码器控制持续时间的序列化方式 +func Duration(key string, val time.Duration) slog.Attr { + return slog.String(key, val.String()) +} - // Bool 构造一个带有布尔值的字段 - Bool = zap.Bool - - // BoolP 构造一个带有布尔值的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - BoolP = zap.Boolp - - // ByteString 构造一个将 UTF-8 编码文本作为 [] 字节传送的字段。要记录不透明的二进制 blob(不一定是有效的 UTF-8),请使用 Binary - ByteString = zap.ByteString - - // Complex128 构造一个带有复数的字段。与大多数数字字段不同,这需要分配(将complex128转换为interface{}) - Complex128 = zap.Complex128 - - // Complex128P 构造一个带有complex128 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Complex128P = zap.Complex128p - - // Complex64 构造一个带有复数的字段。与大多数数字字段不同,这需要分配(将complex64转换为interface{}) - Complex64 = zap.Complex64 - - // Complex64P 构造一个带有complex64 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Complex64P = zap.Complex64p - - // Float64 构造一个带有 float64 的字段。浮点值的表示方式取决于编码器,因此封送处理必然是惰性的 - Float64 = zap.Float64 - - // Float64P 构造一个带有 float64 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Float64P = zap.Float64p - - // Float32 构造一个带有 float32 的字段。浮点值的表示方式取决于编码器,因此封送处理必然是惰性的 - Float32 = zap.Float32 - - // Float32P 构造一个带有 float32 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Float32P = zap.Float32p - - // Int constructs a field with the given key and value. - Int = zap.Int - - // IntP 构造一个带有 int 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - IntP = zap.Intp - - // Int64 使用给定的键和值构造一个字段. - Int64 = zap.Int64 - - // Int64P 构造一个带有 int64 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Int64P = zap.Int64p - - // Int32 使用给定的键和值构造一个字段 - Int32 = zap.Int32 - - // Int32P 构造一个带有 int32 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil”. - Int32P = zap.Int32p - - // Int16 使用给定的键和值构造一个字段 - Int16 = zap.Int16 - - // Int16P 构造一个带有 int16 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Int16P = zap.Int16p - - // Int8 使用给定的键和值构造一个字段 - Int8 = zap.Int8 - - // Int8P 构造一个带有 int8 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Int8P = zap.Int8p - - // String 使用给定的键和值构造一个字段 - String = zap.String - - // StringP 构造一个带有字符串的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - StringP = zap.Stringp - - // Uint 使用给定的键和值构造一个字段 - Uint = zap.Uint - - // UintP 构造一个带有 uint 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - UintP = zap.Uintp - - // Uint64 使用给定的键和值构造一个字段 - Uint64 = zap.Uint64 - - // Uint64P 构造一个带有 uint64 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Uint64P = zap.Uint64p - - // Uint32 使用给定的键和值构造一个字段 - Uint32 = zap.Uint32 - - // Uint32P 构造一个带有 uint32 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Uint32P = zap.Uint32p - - // Uint16 使用给定的键和值构造一个字段 - Uint16 = zap.Uint16 - - // Uint16P 构造一个带有 uint16 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Uint16P = zap.Uint16p - - // Uint8 使用给定的键和值构造一个字段 - Uint8 = zap.Uint8 - - // Uint8P 构造一个带有 uint8 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - Uint8P = zap.Uint8p - - // Uintptr 使用给定的键和值构造一个字段 - Uintptr = zap.Uintptr - - // UintptrP 构造一个带有 uintptr 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - UintptrP = zap.Uintptrp - - // Reflect 使用给定的键和任意对象构造一个字段。它使用适当的编码、基于反射的方式将几乎任何对象延迟序列化到日志记录上下文中,但它相对较慢且分配繁重。在测试之外,Any 始终是更好的选择 - // - 如果编码失败(例如,尝试将 map[int]string 序列化为 JSON),Reflect 将在最终日志输出中包含错误消息 - Reflect = zap.Reflect - - // Namespace 命名空间在记录器的上下文中创建一个命名的、隔离的范围。所有后续字段都将添加到新的命名空间中 - // - 这有助于防止将记录器注入子组件或第三方库时发生按键冲突 - Namespace = zap.Namespace - - // Stringer 使用给定的键和值的 String 方法的输出构造一个字段。 Stringer 的 String 方法被延迟调用 - Stringer = zap.Stringer - - // Time 使用给定的键和值构造一个 Field。编码器控制时间的序列化方式 - Time = zap.Time - - // TimeP 构造一个带有 time.Time 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - TimeP = zap.Timep - - // Stack 构造一个字段,在提供的键下存储当前 goroutine 的堆栈跟踪。请记住,进行堆栈跟踪是急切且昂贵的(相对而言);此操作会进行分配并花费大约两微秒的时间 - Stack = zap.Stack - - // StackSkip 构造一个与 Stack 类似的字段,但也会从堆栈跟踪顶部跳过给定数量的帧 - StackSkip = zap.StackSkip - - // Duration 使用给定的键和值构造一个字段。编码器控制持续时间的序列化方式 - Duration = func(key string, val time.Duration) Field { - return String(key, val.String()) +// DurationP 构造一个带有 time.Duration 的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func DurationP(key string, val *time.Duration) slog.Attr { + if val == nil { + return slog.Any(key, nil) } + return Duration(key, *val) +} - // DurationP 构造一个带有 time.Duration 的字段。返回的 Field 将在适当的时候安全且显式地表示“nil” - DurationP = func(key string, val *time.Duration) Field { - var s = (*val).String() - return StringP(key, &s) +// Bool 构造一个带有布尔值的字段 +func Bool(key string, val bool) slog.Attr { + return slog.Bool(key, val) +} + +// BoolP 构造一个带有布尔值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func BoolP(key string, val *bool) slog.Attr { + if val == nil { + return slog.Any(key, nil) } + return Bool(key, *val) +} - // Object 使用给定的键和 ObjectMarshaler 构造一个字段。它提供了一种灵活但仍然类型安全且高效的方法来将类似映射或结构的用户定义类型添加到日志记录上下文。该结构的 MarshalLogObject 方法被延迟调用 - Object = zap.Object +// String 构造一个带有字符串值的字段 +func String(key, val string) slog.Attr { + return slog.String(key, val) +} - // Inline 构造一个与 Object 类似的 Field,但它会将提供的 ObjectMarshaler 的元素添加到当前命名空间 - Inline = zap.Inline +// StringP 构造一个带有字符串值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func StringP(key string, val *string) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return String(key, *val) +} - // Any 接受一个键和一个任意值,并选择将它们表示为字段的最佳方式,仅在必要时才回退到基于反射的方法。 - // 由于 byteuint8 和 runeint32 是别名,Any 无法区分它们。为了尽量减少意外情况,[]byte 值被视为二进制 blob,字节值被视为 uint8,而 runes 始终被视为整数 - Any = zap.Any +// Int 构造一个带有整数值的字段 +func Int[I generic.Integer](key string, val I) slog.Attr { + return slog.Int(key, int(val)) +} - // Err 是常见习语 NamedError("error", err) 的简写 - Err = zap.Error -) +// IntP 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func IntP[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Int(key, *val) +} + +// Int8 构造一个带有整数值的字段 +func Int8[I generic.Integer](key string, val I) slog.Attr { + return slog.Int(key, int(val)) +} + +// Int8P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Int8P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Int8(key, *val) +} + +// Int16 构造一个带有整数值的字段 +func Int16[I generic.Integer](key string, val I) slog.Attr { + return slog.Int(key, int(val)) +} + +// Int16P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Int16P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Int16(key, *val) +} + +// Int32 构造一个带有整数值的字段 +func Int32[I generic.Integer](key string, val I) slog.Attr { + return slog.Int(key, int(val)) +} + +// Int32P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Int32P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Int32(key, *val) +} + +// Int64 构造一个带有整数值的字段 +func Int64[I generic.Integer](key string, val I) slog.Attr { + return slog.Int64(key, int64(val)) +} + +// Int64P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Int64P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Int64(key, *val) +} + +// Uint 构造一个带有整数值的字段 +func Uint[I generic.Integer](key string, val I) slog.Attr { + return slog.Uint64(key, uint64(val)) +} + +// UintP 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func UintP[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Uint(key, *val) +} + +// Uint8 构造一个带有整数值的字段 +func Uint8[I generic.Integer](key string, val I) slog.Attr { + return slog.Uint64(key, uint64(val)) +} + +// Uint8P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Uint8P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Uint8(key, *val) +} + +// Uint16 构造一个带有整数值的字段 +func Uint16[I generic.Integer](key string, val I) slog.Attr { + return slog.Uint64(key, uint64(val)) +} + +// Uint16P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Uint16P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Uint16(key, *val) +} + +// Uint32 构造一个带有整数值的字段 +func Uint32[I generic.Integer](key string, val I) slog.Attr { + return slog.Uint64(key, uint64(val)) +} + +// Uint32P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Uint32P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Uint32(key, *val) +} + +// Uint64 构造一个带有整数值的字段 +func Uint64[I generic.Integer](key string, val I) slog.Attr { + return slog.Uint64(key, uint64(val)) +} + +// Uint64P 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Uint64P[I generic.Integer](key string, val *I) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Uint64(key, *val) +} + +// Float 构造一个带有浮点值的字段 +func Float[F generic.Float](key string, val F) slog.Attr { + return slog.Float64(key, float64(val)) +} + +// FloatP 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func FloatP[F generic.Float](key string, val *F) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Float(key, *val) +} + +// Float32 构造一个带有浮点值的字段 +func Float32[F generic.Float](key string, val F) slog.Attr { + return slog.Float64(key, float64(val)) +} + +// Float32P 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Float32P[F generic.Float](key string, val *F) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Float32(key, *val) +} + +// Float64 构造一个带有浮点值的字段 +func Float64[F generic.Float](key string, val F) slog.Attr { + return slog.Float64(key, float64(val)) +} + +// Float64P 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func Float64P[F generic.Float](key string, val *F) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Float64(key, *val) +} + +// Time 构造一个带有时间值的字段 +func Time(key string, val time.Time) slog.Attr { + return slog.Time(key, val) +} + +// TimeP 构造一个带有时间值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" +func TimeP(key string, val *time.Time) slog.Attr { + if val == nil { + return slog.Any(key, nil) + } + return Time(key, *val) +} + +// Any 构造一个带有任意值的字段 +func Any(key string, val any) slog.Attr { + return slog.Any(key, val) +} + +// Group 返回分组字段 +func Group(key string, args ...any) slog.Attr { + return slog.Group(key, args...) +} + +// Stack 返回堆栈字段 +func Stack(key string) slog.Attr { + return slog.Any(key, errors.New("")) +} + +// Err 构造一个带有错误值的字段 +func Err(err error) slog.Attr { + return slog.Any("error", err) +} diff --git a/utils/log/field_test.go b/utils/log/field_test.go new file mode 100644 index 0000000..bab95ae --- /dev/null +++ b/utils/log/field_test.go @@ -0,0 +1,17 @@ +package log_test + +import ( + "github.com/kercylan98/minotaur/utils/log" + "testing" +) + +func TestStack(t *testing.T) { + + log.Debug("TestStack") + log.Info("TestStack") + log.Warn("TestStack") + log.Error("TestStack") + //log.Panic("TestStack") + //log.DPanic("TestStack") + //log.Fatal("TestStack") +} diff --git a/utils/log/handler.go b/utils/log/handler.go new file mode 100644 index 0000000..dbbb0cb --- /dev/null +++ b/utils/log/handler.go @@ -0,0 +1,304 @@ +package log + +import ( + "context" + "encoding" + "fmt" + "io" + "log/slog" + "runtime" + "strconv" + "sync" + "time" + "unicode" +) + +const ( + ErrKey = "err" +) + +// NewHandler 创建一个更偏向于人类可读的处理程序,该处理程序也是默认的处理程序 +func NewHandler(w io.Writer, opts *Options) slog.Handler { + if opts == nil { + opts = NewOptions().apply() + } else { + opts = opts.apply() + } + if len(opts.Handlers) > 0 { + var handlers = make([]slog.Handler, len(opts.Handlers)) + for i, s := range opts.Handlers { + handlers[i] = s + } + mh := NewMultiHandler(handlers...) + return mh + } + + return &handler{ + opts: opts, + w: w, + } +} + +type handler struct { + opts *Options + groupPrefix string + groups []string + + mu sync.Mutex + w io.Writer +} + +func (h *handler) clone() *handler { + return &handler{ + groupPrefix: h.groupPrefix, + groups: h.groups, + w: h.w, + } +} + +func (h *handler) Enabled(_ context.Context, level slog.Level) bool { + return level >= h.opts.Level.Level() +} + +func (h *handler) Handle(_ context.Context, r slog.Record) error { + buf := newBuffer(h) + defer buf.Free() + + if !r.Time.IsZero() { + h.appendTime(buf, r.Time) + buf.WriteBytes(' ') + } + + h.appendLevel(buf, r.Level) + buf.WriteBytes(' ') + + if h.opts.Caller { + fs := runtime.CallersFrames([]uintptr{r.PC}) + f, _ := fs.Next() + if f.File != "" { + src := &slog.Source{ + Function: f.Function, + File: f.File, + Line: f.Line, + } + + h.appendSource(buf, src) + buf.WriteBytes(' ') + } + } + + if r.Message != "" { + buf.WriteColorString(h.opts.MessageColor, r.Message, ColorDefault).WriteBytes(' ') + } + + if len(h.opts.FieldPrefix) > 0 { + buf.WriteString(h.opts.FieldPrefix) + } + + r.Attrs(func(attr slog.Attr) bool { + h.appendAttr(buf, attr, h.groupPrefix, h.groups, h.opts.ErrTrace) + return true + }) + + if len(*buf.bytes) == 0 { + return nil + } + buf.WriteBytes(' ', '\n') + + h.mu.Lock() + defer h.mu.Unlock() + + _, err := h.w.Write(*buf.bytes) + return err +} + +func (h *handler) WithAttrs(attrs []slog.Attr) slog.Handler { + if len(attrs) == 0 { + return h + } + h2 := h.clone() + + buf := newBuffer(h) + defer buf.Free() + + for _, attr := range attrs { + h.appendAttr(buf, attr, h.groupPrefix, h.groups) + } + h2.opts.FieldPrefix = h.opts.FieldPrefix + string(*buf.bytes) + return h2 +} + +func (h *handler) WithGroup(name string) slog.Handler { + if name == "" { + return h + } + h2 := h.clone() + h2.groupPrefix += name + "." + h2.groups = append(h2.groups, name) + return h2 +} + +func (h *handler) appendTime(buf *buffer, t time.Time) { + if h.opts.TimePrefix != "" { + buf.WriteColorString( + h.opts.TimePrefixColor, h.opts.TimePrefix, ColorDefault, // 时间前缀 + h.opts.TimePrefixDelimiterColor, h.opts.TimePrefixDelimiter, ColorDefault, // 时间前缀分隔符 + ) + } + buf.WriteColorString(h.opts.TimeColor) + *buf.bytes = t.AppendFormat(*buf.bytes, h.opts.TimeLayout) + buf.WriteColorString(ColorDefault) +} + +func (h *handler) appendLevel(buf *buffer, level slog.Level) { + switch { + case level < InfoLevel: + buf.WriteColorString(h.opts.LevelColor[DebugLevel], h.opts.LevelText[DebugLevel]) + appendLevelDelta(buf, level-DebugLevel) + buf.WriteColorString(ColorDefault) + case level < WarnLevel: + buf.WriteColorString(h.opts.LevelColor[InfoLevel], h.opts.LevelText[InfoLevel]) + appendLevelDelta(buf, level-InfoLevel) + buf.WriteColorString(ColorDefault) + case level < ErrorLevel: + buf.WriteColorString(h.opts.LevelColor[WarnLevel], h.opts.LevelText[WarnLevel]) + appendLevelDelta(buf, level-WarnLevel) + buf.WriteColorString(ColorDefault) + case level < PanicLevel: + buf.WriteColorString(h.opts.LevelColor[ErrorLevel], h.opts.LevelText[ErrorLevel]) + appendLevelDelta(buf, level-ErrorLevel) + buf.WriteColorString(ColorDefault) + case level < FatalLevel: + var tag = h.opts.LevelText[PanicLevel] + if level == DPanicLevel { + tag = h.opts.LevelText[DPanicLevel] + } + buf.WriteColorString(h.opts.LevelColor[PanicLevel], tag) + appendLevelDelta(buf, level-PanicLevel) + buf.WriteColorString(ColorDefault) + default: + buf.WriteColorString(h.opts.LevelColor[FatalLevel], h.opts.LevelText[FatalLevel]) + appendLevelDelta(buf, level-FatalLevel) + buf.WriteColorString(ColorDefault) + } +} + +func appendLevelDelta(buf *buffer, delta slog.Level) { + if delta == 0 { + return + } else if delta > 0 { + buf.WriteBytes('+') + } + *buf.bytes = strconv.AppendInt(*buf.bytes, int64(delta), 10) +} + +func (h *handler) appendSource(buf *buffer, src *slog.Source) { + repFile, repLine := h.opts.CallerFormat(src.File, src.Line) + buf.WriteColorString(h.opts.CallerColor, repFile). + WriteBytes(':'). + WriteString(repLine). + WriteColorString(ColorDefault) +} + +func (h *handler) appendAttr(buf *buffer, attr slog.Attr, groupsPrefix string, groups []string, errTrace ...bool) { + attr.Value = attr.Value.Resolve() + + if attr.Equal(slog.Attr{}) { + return + } + + switch attr.Value.Kind() { + case slog.KindGroup: + if attr.Key != "" { + groupsPrefix += attr.Key + "." + groups = append(groups, attr.Key) + } + for _, groupAttr := range attr.Value.Group() { + h.appendAttr(buf, groupAttr, groupsPrefix, groups) + } + default: + switch v := attr.Value.Any().(type) { + case error: + if len(errTrace) > 0 && errTrace[0] { + h.appendAttr(buf, slog.Attr{Key: attr.Key, Value: formatTraceError(v, h.opts.ErrTraceBeauty)}, h.groupPrefix, h.groups) + return + } + h.appendError(buf, v, groupsPrefix) + buf.WriteBytes(' ') + case *beautyTrace: + for _, s := range v.trace { + buf.WriteBytes('\n', '\t').WriteColorString(h.opts.ErrTraceColor, s, ColorDefault) + } + default: + h.appendKey(buf, attr.Key, groupsPrefix) + h.appendValue(buf, attr.Value, true) + buf.WriteBytes(' ') + } + } +} + +func (h *handler) appendKey(buf *buffer, key, groups string) { + if key == "" { + return + } + buf.WriteColorString(h.opts.KeyColor) + appendString(buf, groups+key, true) + buf.WriteColorString(ColorDefault, h.opts.KVDelimiter) +} + +func (h *handler) appendValue(buf *buffer, v slog.Value, quote bool) { + switch v.Kind() { + case slog.KindString: + appendString(buf, v.String(), quote) + case slog.KindInt64: + *buf.bytes = strconv.AppendInt(*buf.bytes, v.Int64(), 10) + case slog.KindUint64: + *buf.bytes = strconv.AppendUint(*buf.bytes, v.Uint64(), 10) + case slog.KindFloat64: + *buf.bytes = strconv.AppendFloat(*buf.bytes, v.Float64(), 'g', -1, 64) + case slog.KindBool: + *buf.bytes = strconv.AppendBool(*buf.bytes, v.Bool()) + case slog.KindDuration: + appendString(buf, v.Duration().String(), quote) + case slog.KindTime: + appendString(buf, v.Time().String(), quote) + case slog.KindAny: + switch cv := v.Any().(type) { + case slog.Level: + h.appendLevel(buf, cv) + case encoding.TextMarshaler: + data, err := cv.MarshalText() + if err != nil { + break + } + appendString(buf, string(data), quote) + case *slog.Source: + h.appendSource(buf, cv) + default: + appendString(buf, fmt.Sprint(v.Any()), quote) + } + } +} + +func (h *handler) appendError(buf *buffer, err error, groupsPrefix string) { + buf.WriteColorString(h.opts.KeyColor) + appendString(buf, groupsPrefix+ErrKey, true) + buf.WriteColorString(ColorDefault, h.opts.KVDelimiter, h.opts.ErrorColor) + appendString(buf, err.Error(), true) + buf.WriteColorString(ColorDefault) +} + +func appendString(buf *buffer, s string, quote bool) { + quoting := len(s) == 0 + for _, r := range s { + if unicode.IsSpace(r) || r == '"' || r == '=' || !unicode.IsPrint(r) { + quoting = true + break + } + } + if quote && quoting { + *buf.bytes = strconv.AppendQuote(*buf.bytes, s) + } else { + buf.WriteString(s) + } +} diff --git a/utils/log/level.go b/utils/log/level.go index fd7a0ae..cb83cd4 100644 --- a/utils/log/level.go +++ b/utils/log/level.go @@ -1,20 +1,22 @@ package log -import "go.uber.org/zap/zapcore" +import ( + "log/slog" +) const ( // DebugLevel 调试级别日志通常非常庞大,并且通常在生产中被禁用 - DebugLevel Level = zapcore.DebugLevel + DebugLevel = slog.LevelDebug // InfoLevel 是默认的日志记录优先级 - InfoLevel Level = zapcore.InfoLevel + InfoLevel = slog.LevelInfo // WarnLevel 日志比信息更重要,但不需要单独的人工审核 - WarnLevel Level = zapcore.WarnLevel + WarnLevel = slog.LevelWarn // ErrorLevel 日志具有高优先级。如果应用程序运行顺利,它不应该生成任何错误级别的日志 - ErrorLevel Level = zapcore.ErrorLevel - // DPanicLevel 日志是特别重要的错误。在开发中,记录器在写入消息后会出现恐慌 - DPanicLevel Level = zapcore.DPanicLevel + ErrorLevel = slog.LevelError // PanicLevel 记录一条消息,然后出现恐慌 - PanicLevel Level = zapcore.PanicLevel + PanicLevel slog.Level = 16 + // DPanicLevel 日志是特别重要的错误。在开发中,记录器在写入消息后会出现恐慌 + DPanicLevel slog.Level = 17 // FatalLevel 记录一条消息,然后调用 os.Exit(1) - FatalLevel Level = zapcore.FatalLevel + FatalLevel slog.Level = 20 ) diff --git a/utils/log/logger.go b/utils/log/logger.go index 2b35dfe..554df3a 100644 --- a/utils/log/logger.go +++ b/utils/log/logger.go @@ -1,75 +1,20 @@ package log -var logger Logger +import ( + "log/slog" + "os" +) -func init() { - logger = Default().Build() -} - -// SetLogger 设置日志记录器 -func SetLogger(l Logger) { - if m, ok := l.(*Minotaur); ok && m != nil { - _ = m.Sync() - _ = m.Sugared.Sync() +// NewLogger 创建一个新的日志记录器 +func NewLogger(options ...*Options) *Logger { + opts := NewOptions().With(options...) + return &Logger{ + Logger: slog.New(NewHandler(os.Stdout, opts)), + opts: opts, } - logger = l } -// Logger 适用于 Minotaur 的日志接口 -type Logger interface { - // Debug 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - Debug(msg string, fields ...Field) - // Info 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - Info(msg string, fields ...Field) - // Warn 在 WarnLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - Warn(msg string, fields ...Field) - // Error 在 ErrorLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - Error(msg string, fields ...Field) - // DPanic 在 DPanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - // - 如果记录器处于开发模式,它就会出现 panic(DPanic 的意思是“development panic”)。这对于捕获可恢复但不应该发生的错误很有用 - DPanic(msg string, fields ...Field) - // Panic 在 PanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - // - 即使禁用了 PanicLevel 的日志记录,记录器也会出现 panic - Panic(msg string, fields ...Field) - // Fatal 在 FatalLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 - // - 然后记录器调用 os.Exit(1),即使 FatalLevel 的日志记录被禁用 - Fatal(msg string, fields ...Field) -} - -// Debug 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func Debug(msg string, fields ...Field) { - logger.Debug(msg, fields...) -} - -// Info 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func Info(msg string, fields ...Field) { - logger.Info(msg, fields...) -} - -// Warn 在 WarnLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func Warn(msg string, fields ...Field) { - logger.Warn(msg, fields...) -} - -// Error 在 ErrorLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -func Error(msg string, fields ...Field) { - logger.Error(msg, fields...) -} - -// DPanic 在 DPanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -// - 如果记录器处于开发模式,它就会出现 panic(DPanic 的意思是“development panic”)。这对于捕获可恢复但不应该发生的错误很有用 -func DPanic(msg string, fields ...Field) { - logger.DPanic(msg, fields...) -} - -// Panic 在 PanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -// - 即使禁用了 PanicLevel 的日志记录,记录器也会出现 panic -func Panic(msg string, fields ...Field) { - logger.Panic(msg, fields...) -} - -// Fatal 在 FatalLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 -// - 然后记录器调用 os.Exit(1),即使 FatalLevel 的日志记录被禁用 -func Fatal(msg string, fields ...Field) { - logger.Fatal(msg, fields...) +type Logger struct { + *slog.Logger + opts *Options } diff --git a/utils/log/minotaur.go b/utils/log/minotaur.go deleted file mode 100644 index 73efa10..0000000 --- a/utils/log/minotaur.go +++ /dev/null @@ -1,8 +0,0 @@ -package log - -import "go.uber.org/zap" - -type Minotaur struct { - *zap.Logger - Sugared *zap.SugaredLogger -} diff --git a/utils/log/multi_handler.go b/utils/log/multi_handler.go new file mode 100644 index 0000000..6533cec --- /dev/null +++ b/utils/log/multi_handler.go @@ -0,0 +1,62 @@ +package log + +import ( + "context" + "github.com/kercylan98/minotaur/utils/super" + "log/slog" +) + +// NewMultiHandler 创建一个新的多处理程序 +func NewMultiHandler(handlers ...slog.Handler) slog.Handler { + return &MultiHandler{ + handlers: handlers, + } +} + +type MultiHandler struct { + handlers []slog.Handler +} + +func (h MultiHandler) Enabled(ctx context.Context, level slog.Level) bool { + for i := range h.handlers { + if h.handlers[i].Enabled(ctx, level) { + return true + } + } + + return false +} + +func (h MultiHandler) Handle(ctx context.Context, record slog.Record) (err error) { + for i := range h.handlers { + if h.handlers[i].Enabled(ctx, record.Level) { + err = func() error { + defer func() { + err = super.RecoverTransform(recover()) + }() + return h.handlers[i].Handle(ctx, record.Clone()) + }() + if err != nil { + return err + } + } + } + + return nil +} + +func (h MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + var handlers = make([]slog.Handler, len(h.handlers)) + for i, s := range h.handlers { + handlers[i] = s.WithAttrs(attrs) + } + return NewMultiHandler(handlers...) +} + +func (h MultiHandler) WithGroup(name string) slog.Handler { + var handlers = make([]slog.Handler, len(h.handlers)) + for i, s := range h.handlers { + handlers[i] = s.WithGroup(name) + } + return NewMultiHandler(handlers...) +} diff --git a/utils/log/options.go b/utils/log/options.go index 37dd5a6..112ec85 100644 --- a/utils/log/options.go +++ b/utils/log/options.go @@ -1,239 +1,400 @@ package log import ( - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "io" + "log/slog" "time" ) -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) +const ( + DefaultTimeLayout = time.DateTime + DefaultTimePrefix = "" + DefaultTimePrefixDelimiter = "=" + DefaultCaller = true + DefaultCallerSkip = 3 + DefaultFieldPrefix = "" + DefaultLevel = DebugLevel + DefaultErrTrace = true + DefaultErrTraceBeauty = true + DefaultKVDelimiter = "=" + DefaultDisableColor = false + DefaultLevelInfoText = "INF" + DefaultLevelWarnText = "WRN" + DefaultLevelErrorText = "ERR" + DefaultLevelDebugText = "DBG" + DefaultLevelPanicText = "PNC" + DefaultLevelDPanicText = "DPC" + DefaultLevelFatalText = "FTL" + DefaultDevMode = true + + DefaultTimePrefixColor = ColorDefault + DefaultTimePrefixDelimiterColor = ColorDefault + DefaultTimeColor = ColorDefaultBold + DefaultLevelDebugColor = ColorBlue + DefaultLevelInfoColor = ColorGreen + DefaultLevelWarnColor = ColorBrightYellow + DefaultLevelErrorColor = ColorRed + DefaultLevelPanicColor = ColorBrightRed + DefaultLevelDPanicColor = ColorBrightRed + DefaultLevelFatalColor = ColorBrightRed + DefaultCallerColor = ColorBrightBlueUnderline + DefaultErrTraceColor = ColorBrightBlack + DefaultKeyColor = ColorWhite + DefaultValueColor = ColorDefault + DefaultErrorColor = ColorRedBold + DefaultMessageColor = ColorWhiteBold ) -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, - }, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } +// NewOptions 创建一个新的日志选项 +func NewOptions() *Options { + return (&Options{}).WithDev() +} - // 应用选项 - for _, opt := range opts { - opt(config) +type ( + // Option 日志选项 + Option func(opts *Options) + // Options 日志选项 + Options struct { + opts []Option + Handlers []slog.Handler // 处理程序 + TimeLayout string // 时间格式化字符串 + TimePrefix string // 时间前缀 + TimePrefixDelimiter string // 时间前缀分隔符 + Caller bool // 是否显示调用者信息 + CallerSkip int // 跳过的调用层数 + CallerFormat func(file string, line int) (repFile, refLine string) // 调用者信息格式化函数 + FieldPrefix string // 字段前缀 + Level slog.Leveler // 日志级别 + ErrTrace bool // 是否显示错误堆栈 + ErrTraceBeauty bool // 是否美化错误堆栈 + KVDelimiter string // 键值对分隔符 + DisableColor bool // 是否禁用颜色 + TimePrefixColor string // 时间前缀颜色 + TimePrefixDelimiterColor string // 时间前缀分隔符颜色 + TimeColor string // 时间颜色 + LevelText map[slog.Level]string // 日志级别文本 + LevelColor map[slog.Level]string // 日志级别颜色 + CallerColor string // 调用者信息颜色 + ErrTraceColor string // 错误堆栈颜色 + KeyColor string // 键颜色 + ValueColor string // 值颜色 + ErrorColor string // 错误颜色 + MessageColor string // 消息颜色 + DevMode bool // 是否为开发模式 } +) - if len(config.Encoding) == 0 { - if config.Development { - config.Encoding = "console" - } else { - config.Encoding = "json" +// WithDev 设置可选项为开发模式 +// - 开发模式适用于本地开发环境,会以更友好的方式输出日志 +func (o *Options) WithDev() *Options { + o.opts = append(o.opts, func(opts *Options) { + opts.DevMode = DefaultDevMode + opts.Handlers = nil + opts.TimeLayout = DefaultTimeLayout + opts.TimePrefix = DefaultTimePrefix + opts.TimePrefixDelimiter = DefaultTimePrefixDelimiter + opts.Caller = DefaultCaller + opts.CallerSkip = DefaultCallerSkip + opts.CallerFormat = CallerBasicFormat + opts.FieldPrefix = DefaultFieldPrefix + opts.Level = DefaultLevel + opts.ErrTrace = DefaultErrTrace + opts.ErrTraceBeauty = DefaultErrTraceBeauty + opts.KVDelimiter = DefaultKVDelimiter + opts.DisableColor = DefaultDisableColor + opts.TimePrefixColor = DefaultTimePrefixColor + opts.TimePrefixDelimiterColor = DefaultTimePrefixDelimiterColor + opts.TimeColor = DefaultTimeColor + opts.LevelText = map[slog.Level]string{ + DebugLevel: DefaultLevelDebugText, + InfoLevel: DefaultLevelInfoText, + WarnLevel: DefaultLevelWarnText, + ErrorLevel: DefaultLevelErrorText, + PanicLevel: DefaultLevelPanicText, + DPanicLevel: DefaultLevelDPanicText, + FatalLevel: DefaultLevelFatalText, } - } + opts.LevelColor = map[slog.Level]string{ + DebugLevel: DefaultLevelDebugColor, + InfoLevel: DefaultLevelInfoColor, + WarnLevel: DefaultLevelWarnColor, + ErrorLevel: DefaultLevelErrorColor, + PanicLevel: DefaultLevelPanicColor, + DPanicLevel: DefaultLevelDPanicColor, + FatalLevel: DefaultLevelFatalColor, + } + opts.CallerColor = DefaultCallerColor + opts.ErrTraceColor = DefaultErrTraceColor + opts.KeyColor = DefaultKeyColor + opts.ValueColor = DefaultValueColor + opts.ErrorColor = DefaultErrorColor + opts.MessageColor = DefaultMessageColor + }) + return o +} - 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") - } +// WithProd 设置可选项为生产模式 +// - 生产模式适用于生产环境,会以更简洁的方式输出日志 +func (o *Options) WithProd() *Options { + o.WithDev() + o.opts = append(o.opts, func(opts *Options) { + opts.DisableColor = true + }) + return o +} - return encoder +// WithTest 设置可选项为测试模式 +// - 测试模式适用于测试环境,测试环境与开发环境相似,但是会屏蔽掉一些不必要的信息 +func (o *Options) WithTest() *Options { + o.WithDev() + // 暂与开发模式相同 + return o +} + +// WithDevMode 设置是否为开发模式 +// - 默认值为 DefaultDevMode +// - 开发模式下将影响部分功能,例如 DPanic +func (o *Options) WithDevMode(enable bool) *Options { + o.append(func(opts *Options) { + opts.DevMode = enable + }) + return o +} + +// WithHandler 设置处理程序 +func (o *Options) WithHandler(handlers ...slog.Handler) *Options { + o.append(func(opts *Options) { + opts.Handlers = handlers + }) + return o +} + +// WithMessageColor 设置消息颜色 +// - 默认消息颜色为 DefaultMessageColor +func (o *Options) WithMessageColor(color string) *Options { + o.append(func(opts *Options) { + opts.MessageColor = color + }) + return o +} + +// WithErrorColor 设置错误颜色 +// - 默认错误颜色为 DefaultErrorColor +func (o *Options) WithErrorColor(color string) *Options { + o.append(func(opts *Options) { + opts.ErrorColor = color + }) + return o +} + +// WithValueColor 设置值颜色 +// - 默认值颜色为 DefaultValueColor +func (o *Options) WithValueColor(color string) *Options { + o.append(func(opts *Options) { + opts.ValueColor = color + }) + return o +} + +// WithKeyColor 设置键颜色 +// - 默认键颜色为 DefaultKeyColor +func (o *Options) WithKeyColor(color string) *Options { + o.append(func(opts *Options) { + opts.KeyColor = color + }) + return o +} + +// WithErrTraceColor 设置错误堆栈颜色 +// - 默认错误堆栈颜色为 DefaultErrTraceColor +func (o *Options) WithErrTraceColor(color string) *Options { + o.append(func(opts *Options) { + opts.ErrTraceColor = color + }) + return o +} + +// WithCallerColor 设置调用者信息颜色 +// - 默认调用者信息颜色为 DefaultCallerColor +func (o *Options) WithCallerColor(color string) *Options { + o.append(func(opts *Options) { + opts.CallerColor = color + }) + return o +} + +// WithLevelColor 设置日志级别颜色 +// - 默认日志级别颜色为 DefaultLevelInfoColor, DefaultLevelWarnColor, DefaultLevelErrorColor, DefaultLevelDebugColor, DefaultLevelPanicColor, DefaultLevelDPanicColor, DefaultLevelFatalColor +func (o *Options) WithLevelColor(level slog.Level, color string) *Options { + o.append(func(opts *Options) { + opts.LevelColor[level] = color + }) + return o +} + +// WithLevelText 设置日志级别文本 +// - 默认日志级别文本为 DefaultLevelInfoText, DefaultLevelWarnText, DefaultLevelErrorText, DefaultLevelDebugText, DefaultLevelPanicText, DefaultLevelDPanicText, DefaultLevelFatalText +func (o *Options) WithLevelText(level slog.Level, text string) *Options { + o.append(func(opts *Options) { + opts.LevelText[level] = text + }) + return o +} + +// WithTimeColor 设置时间颜色 +// - 默认时间颜色为 DefaultTimeColor +func (o *Options) WithTimeColor(color string) *Options { + o.append(func(opts *Options) { + opts.TimeColor = color + }) + return o +} + +// WithTimePrefixDelimiter 设置时间前缀分隔符 +// - 默认时间前缀分隔符为 DefaultTimePrefixDelimiter +func (o *Options) WithTimePrefixDelimiter(delimiter string) *Options { + o.append(func(opts *Options) { + opts.TimePrefixDelimiter = delimiter + }) + return o +} + +// WithTimePrefixDelimiterColor 设置时间前缀分隔符颜色 +// - 默认时间前缀分隔符颜色为 DefaultTimePrefixDelimiterColor +func (o *Options) WithTimePrefixDelimiterColor(color string) *Options { + o.append(func(opts *Options) { + opts.TimePrefixDelimiterColor = color + }) + return o +} + +// WithTimePrefixColor 设置时间前缀颜色 +// - 默认时间前缀颜色为 DefaultTimePrefixColor +func (o *Options) WithTimePrefixColor(color string) *Options { + o.append(func(opts *Options) { + opts.TimePrefixColor = color + }) + return o +} + +// WithDisableColor 设置是否禁用颜色 +// - 默认为 DefaultDisableColor +func (o *Options) WithDisableColor(disable bool) *Options { + o.append(func(opts *Options) { + opts.DisableColor = disable + }) + return o +} + +// WithKVDelimiter 设置键值对分隔符 +// - 默认键值对分隔符为 DefaultKVDelimiter +func (o *Options) WithKVDelimiter(delimiter string) *Options { + o.append(func(opts *Options) { + opts.KVDelimiter = delimiter + }) + return o +} + +// WithErrTraceBeauty 设置是否美化错误堆栈 +// - 默认为 DefaultErrTraceBeauty +// - 当启用错误堆栈追踪时,一切 error 字段都会转换为包含错误信息和堆栈信息的组信息 +func (o *Options) WithErrTraceBeauty(enable bool) *Options { + o.append(func(opts *Options) { + opts.ErrTraceBeauty = enable + }) + return o +} + +// WithErrTrace 设置是否显示错误堆栈 +// - 默认为 DefaultErrTrace +// - 当启用错误堆栈追踪时,一切 error 字段都会转换为包含错误信息和堆栈信息的组信息 +func (o *Options) WithErrTrace(enable bool) *Options { + o.append(func(opts *Options) { + opts.ErrTrace = enable + }) + return o } // WithLevel 设置日志级别 -func WithLevel(level Level) Option { - return func(config *Config) { - config.Level.SetLevel(level) - } +// - 默认日志级别为 DefaultLevel +func (o *Options) WithLevel(level slog.Leveler) *Options { + o.append(func(opts *Options) { + opts.Level = level + }) + return o } -// WithDevelopment 设置是否为开发模式 -func WithDevelopment(development bool) Option { - return func(config *Config) { - config.Development = development - } +// WithFieldPrefix 为所有字段设置前缀 +// - 默认字段前缀为 DefaultFieldPrefix +// - 字段前缀为空时,不会添加字段前缀 +// - 假设字段前缀为 "M",假设原本输出为 "ID=1",则日志输出为 "MID=1 +func (o *Options) WithFieldPrefix(prefix string) *Options { + o.append(func(opts *Options) { + opts.FieldPrefix = prefix + }) + return o } -// WithDisableCaller 设置是否禁用调用者 -func WithDisableCaller(disableCaller bool) Option { - return func(config *Config) { - config.DisableCaller = disableCaller - } +// WithCallerFormat 设置调用者信息格式化函数 +// - 默认格式化函数为 CallerBasicFormat +func (o *Options) WithCallerFormat(format func(file string, line int) (repFile, refLine string)) *Options { + o.append(func(opts *Options) { + opts.CallerFormat = format + }) + return o } -// WithDisableStacktrace 设置是否禁用堆栈跟踪 -func WithDisableStacktrace(disableStacktrace bool) Option { - return func(config *Config) { - config.DisableStacktrace = disableStacktrace - } +// WithCaller 设置是否显示调用者信息 +// - 默认为 DefaultCaller,且跳过 DefaultCallerSkip 层调用 +// - 当存在多个 skip 参数时,取首个参数 +func (o *Options) WithCaller(enable bool, skip ...int) *Options { + o.append(func(opts *Options) { + opts.Caller = enable + if len(skip) > 0 { + opts.CallerSkip = skip[0] + } + }) + return o } -// WithSampling 设置采样策略 -func WithSampling(sampling *zap.SamplingConfig) Option { - return func(config *Config) { - config.Sampling = sampling - } +// WithTimeLayout 设置时间格式化字符串 +// - 默认时间格式化字符串为 DefaultTimeLayout +// - 假设时间格式化字符串为 "2006-01-02 15:04:05",则日志输出为 "2020-01-01 00:00:00 ..." +func (o *Options) WithTimeLayout(layout string) *Options { + o.append(func(opts *Options) { + opts.TimeLayout = layout + }) + return o } -// WithEncoding 设置编码器 -func WithEncoding(encoding string) Option { - return func(config *Config) { - config.Encoding = encoding - } +// WithTimePrefix 设置时间前缀 +// - 默认时间前缀为 DefaultTimePrefix +// - 时间前缀为空时,不会添加时间前缀 +// - 假设时间前缀为 "TIME=",则日志输出为 "TIME=2020-01-01 00:00:00 ..." +func (o *Options) WithTimePrefix(prefix string) *Options { + o.append(func(opts *Options) { + opts.TimePrefix = prefix + }) + return o } -// WithEncoderMessageKey 设置消息键 -func WithEncoderMessageKey(encoderMessageKey string) Option { - return func(config *Config) { - config.EncoderConfig.MessageKey = encoderMessageKey +// With 初始化日志选项 +func (o *Options) With(opts ...*Options) *Options { + for _, opt := range opts { + o.append(opt.opts...) } + return o } -// WithEncoderLevelKey 设置级别键 -func WithEncoderLevelKey(encoderLevelKey string) Option { - return func(config *Config) { - config.EncoderConfig.LevelKey = encoderLevelKey +// apply 应用日志选项 +func (o *Options) apply() *Options { + for _, opt := range o.opts { + opt(o) } + return o } -// 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 - } +// append 添加日志选项 +func (o *Options) append(opts ...Option) *Options { + o.opts = append(o.opts, opts...) + return o } diff --git a/utils/log/trace.go b/utils/log/trace.go new file mode 100644 index 0000000..73e9b35 --- /dev/null +++ b/utils/log/trace.go @@ -0,0 +1,80 @@ +package log + +import ( + "fmt" + "github.com/pkg/errors" + "log/slog" + "runtime" + "strings" +) + +type beautyTrace struct { + trace []string +} + +func formatTraceError(err error, beauty bool) slog.Value { + var groupValues []slog.Attr + + if msg := err.Error(); msg != "" { + groupValues = append(groupValues, slog.Any("msg", err)) + } + + type StackTracer interface { + StackTrace() errors.StackTrace + } + + var st StackTracer + for err := err; err != nil; err = errors.Unwrap(err) { + if x, ok := err.(StackTracer); ok { + st = x + } + } + + if st == nil && err != nil { + st = errors.WithStack(err).(StackTracer) + } + + if st != nil { + if beauty { + groupValues = append(groupValues, + slog.Any("trace", &beautyTrace{traceLines(st.StackTrace())}), + ) + } else { + groupValues = append(groupValues, + slog.Any("trace", traceLines(st.StackTrace())), + ) + } + } + + return slog.GroupValue(groupValues...) +} + +func traceLines(frames errors.StackTrace) []string { + traceLines := make([]string, len(frames)) + + var skipped int + skipping := true + for i := len(frames) - 1; i >= 0; i-- { + pc := uintptr(frames[i]) - 1 + fn := runtime.FuncForPC(pc) + if fn == nil { + traceLines[i] = "unknown" + skipping = false + continue + } + + name := fn.Name() + + if skipping && strings.HasPrefix(name, "runtime.") { + skipped++ + continue + } else { + skipping = false + } + + filename, lineNr := fn.FileLine(pc) + traceLines[i] = fmt.Sprintf("%s %s:%d", name, filename, lineNr) + } + + return traceLines[:len(traceLines)-skipped] +}