refactor: 重构 log 包,由 zap 改为 slog

This commit is contained in:
kercylan98 2024-01-05 00:18:32 +08:00
parent bb06cbfeb0
commit 71a3b34304
17 changed files with 1815 additions and 488 deletions

6
go.mod
View File

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

View File

@ -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 服务初始化

56
utils/log/buffer.go Normal file
View File

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

11
utils/log/caller.go Normal file
View File

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

338
utils/log/color.go Normal file
View File

@ -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: {},
}

230
utils/log/color_test.go Normal file
View File

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

80
utils/log/default.go Normal file
View File

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

View File

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

View File

@ -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 序列化为 JSONReflect 将在最终日志输出中包含错误消息
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)
}

17
utils/log/field_test.go Normal file
View File

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

304
utils/log/handler.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

80
utils/log/trace.go Normal file
View File

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