go项目脚手架
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sorbet/pkg/log/handler.go

164 lines
3.4 KiB

package log
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log"
"log/slog"
"runtime"
"strings"
"sync/atomic"
"time"
"unsafe"
)
type Handler struct {
ctx unsafe.Pointer // 结构体 logger
color Colorer
slog.Handler
attrs []Attr
l *log.Logger
}
func (h *Handler) WithAttrs(attrs []Attr) slog.Handler {
return &Handler{
ctx: h.ctx,
color: h.color,
Handler: h.Handler.WithAttrs(attrs),
attrs: append(h.attrs, attrs...),
l: h.l,
}
}
func (h *Handler) WithGroup(name string) slog.Handler {
return &Handler{
ctx: h.ctx,
color: h.color,
Handler: h.Handler.WithGroup(name),
l: h.l,
}
}
func (h *Handler) Handle(ctx context.Context, r slog.Record) error {
// 参考 atomic.Pointer#Load 实现
l := (*logger)(atomic.LoadPointer(&h.ctx))
if atomic.LoadInt32(&l.discard) != 1 {
if len(h.attrs) > 0 {
x := slog.NewRecord(r.Time, r.Level, r.Message, r.PC)
r.Attrs(func(attr slog.Attr) bool {
x.AddAttrs(attr)
return true
})
x.AddAttrs(h.attrs...)
if err := h.Print(l, x); err != nil {
return err
}
} else if err := h.Print(l, r); err != nil {
return err
}
}
if atomic.LoadInt32(&l.unpersist) != 1 {
return h.Handler.Handle(ctx, r)
}
return nil
}
func (h *Handler) Print(l *logger, r slog.Record) error {
flags := l.Flags()
colorful := flags&Lcolor != 0
level := parseSlogLevel(r.Level)
levelStr := level.String()
var fields map[string]any
if numAttrs := r.NumAttrs(); numAttrs > 0 {
fields = make(map[string]any, numAttrs)
r.Attrs(func(a Attr) bool {
switch a.Key {
case rawTimeKey:
r.Time = a.Value.Any().(time.Time)
case rawLevelKey:
levelStr = a.Value.Any().(string)
default:
fields[a.Key] = a.Value.Any()
}
return true
})
}
var output string
var sep string
write := func(s string) {
if sep == "" {
sep = " "
} else {
output += sep
}
output += s
}
if flags&(Ldate|Ltime|Lmicroseconds) != 0 {
t := r.Time.In(l.Timezone()) // todo 原子操作
if flags&Ldate != 0 {
write(t.Format("2006/01/02"))
}
if flags&(Ltime|Lmicroseconds) != 0 {
if flags&Lmicroseconds != 0 {
write(t.Format("15:04:05.000"))
} else {
write(t.Format("15:04:05"))
}
}
}
if colorful {
// TODO(hupeh): 重新设计不同颜色
colorize := identify
switch level {
case LevelDebug:
colorize = h.color.Cyan
case LevelInfo:
colorize = h.color.Blue
case LevelWarn:
colorize = h.color.Yellow
case LevelError:
colorize = h.color.Red
case LevelFatal, LevelPanic:
colorize = h.color.Magenta
}
write(h.color.Grey("[") + colorize(levelStr) + h.color.Grey("]"))
write(colorize(r.Message))
} else {
write("[" + levelStr + "]")
write(r.Message)
}
if flags&(Lshortfile|Llongfile) != 0 && r.PC > 0 {
var fileStr string
fs := runtime.CallersFrames([]uintptr{r.PC})
f, _ := fs.Next()
file := f.File
if flags&Lshortfile != 0 {
i := strings.LastIndexAny(file, "\\/")
if i > -1 {
file = file[i+1:]
}
}
fileStr = fmt.Sprintf("%s:%s:%d", f.Function, file, f.Line)
if colorful {
fileStr = h.color.Green(fileStr)
}
write(fileStr)
}
if flags&Lfields != 0 && len(fields) > 0 {
b, err := json.Marshal(fields)
if err != nil {
return err
}
fieldsStr := string(bytes.TrimSpace(b))
if fieldsStr != "" {
if colorful {
fieldsStr = h.color.White(fieldsStr)
}
write(fieldsStr)
}
}
h.l.Println(strings.TrimSpace(output))
return nil
}