Files
vRp.CD2g_test/utils/log/handler.go
2024-01-05 00:18:32 +08:00

305 lines
7.4 KiB
Go

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()
}
return &handler{
opts: opts.apply(),
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,
opts: NewOptions().With(h.opts).apply(),
groups: h.groups,
w: h.w,
}
}
func (h *handler) Enabled(_ context.Context, level slog.Level) bool {
return level >= h.opts.Level.Load().(slog.Leveler).Level()
}
func (h *handler) Handle(_ context.Context, r slog.Record) error {
lv := h.opts.Level.Load().(slog.Leveler).Level()
if r.Level < lv {
return nil
}
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 {
pcs := make([]uintptr, 1)
runtime.CallersFrames(pcs[:runtime.Callers(h.opts.CallerSkip, pcs)])
fs := runtime.CallersFrames(pcs)
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)
if lv == DPanicLevel && h.opts.DevMode {
panic(r.Message)
}
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)
}
}