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/logger.go

280 lines
5.7 KiB

package log
import (
"fmt"
"io"
"log"
"log/slog"
"os"
"runtime"
"sync"
"sync/atomic"
"time"
"unsafe"
)
const (
Ldate = 1 << iota
Ltime
Lmicroseconds
Llongfile
Lshortfile
Lfields
Lcolor
LstdFlags = Ltime | Lmicroseconds | Lfields | Lcolor
)
type Options struct {
Flags int
Level Level
IgnorePC bool
Timezone *time.Location
Colorer Colorer
PersistWriter io.Writer
Writer io.Writer
}
type Logger interface {
Flags() int
SetFlags(flags int)
Level() Level
SetLevel(Level)
Timezone() *time.Location
SetTimezone(loc *time.Location)
IgnorePC() bool
SetIgnorePC(ignore bool)
Enabled(level Level) bool
Writer() io.Writer
SetWriter(w io.Writer)
PersistWriter() io.Writer
SetPersistWriter(w io.Writer)
With(attrs ...Attr) Logger
WithGroup(name string) Logger
Log(level Level, msg string, args ...any)
Trace(msg string, args ...any)
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
Panic(msg string, args ...any)
Fatal(msg string, args ...any)
}
var _ Logger = &logger{}
type logger struct {
flags int32
level int32
ignorePC int32
unpersist int32 // 忽略持久化写入
discard int32 // 忽略控制台输出
timezone unsafe.Pointer
mu *sync.Mutex
persistWriter io.Writer
writer io.Writer
handler slog.Handler
}
func New(o *Options) Logger {
if o.Flags == 0 {
o.Flags = LstdFlags
}
if o.Timezone == nil {
o.Timezone = time.FixedZone("CST", 8*3600) // 使用东八区时间
}
if o.Writer == nil {
o.Writer = os.Stderr
}
if o.PersistWriter == nil {
o.PersistWriter = io.Discard
}
if o.Colorer == nil {
o.Colorer = NewColorer()
}
l := &logger{mu: &sync.Mutex{}}
l.handler = &Handler{
ctx: unsafe.Pointer(l),
color: o.Colorer,
Handler: slog.NewJSONHandler(
&mutablePersistWriter{l},
&slog.HandlerOptions{
AddSource: true,
Level: &mutableLevel{l},
ReplaceAttr: createAttrReplacer(l),
},
),
l: log.New(&mutableWriter{l}, "", 0),
}
l.SetFlags(o.Flags)
l.SetLevel(o.Level)
l.SetIgnorePC(o.IgnorePC)
l.SetTimezone(o.Timezone)
l.SetPersistWriter(o.PersistWriter)
l.SetWriter(o.Writer)
return l
}
func (l *logger) Flags() int {
return int(atomic.LoadInt32(&l.flags))
}
func (l *logger) SetFlags(flags int) {
atomic.StoreInt32(&l.flags, int32(flags))
}
func (l *logger) Level() Level {
return Level(int(atomic.LoadInt32(&l.level)))
}
func (l *logger) SetLevel(level Level) {
atomic.StoreInt32(&l.level, int32(level))
}
func (l *logger) Timezone() *time.Location {
// 参考 atomic.Pointer#Load 实现
return (*time.Location)(atomic.LoadPointer(&l.timezone))
}
func (l *logger) SetTimezone(loc *time.Location) {
// 参考 atomic.Pointer#Store 实现
atomic.StorePointer(&l.timezone, unsafe.Pointer(loc))
}
func (l *logger) IgnorePC() bool {
return atomic.LoadInt32(&l.ignorePC) == 1
}
func (l *logger) SetIgnorePC(ignore bool) {
atomic.StoreInt32(&l.ignorePC, bool2int32(ignore))
}
func (l *logger) Enabled(level Level) bool {
// todo panic 和 fatal 用于开启,取消 off 等级
return l.handler.Enabled(nil, level.slog().Level())
}
func (l *logger) SetWriter(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
l.writer = w
atomic.StoreInt32(&l.discard, bool2int32(w == io.Discard))
}
func (l *logger) Writer() io.Writer {
l.mu.Lock()
defer l.mu.Unlock()
return l.writer
}
func (l *logger) SetPersistWriter(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
l.persistWriter = w
atomic.StoreInt32(&l.unpersist, bool2int32(w == io.Discard))
}
func (l *logger) PersistWriter() io.Writer {
l.mu.Lock()
defer l.mu.Unlock()
return l.persistWriter
}
func (l *logger) With(attrs ...Attr) Logger {
if len(attrs) == 0 {
return l
}
c := l.clone()
c.handler = l.handler.WithAttrs(attrs).(*Handler)
return c
}
func (l *logger) WithGroup(name string) Logger {
if name == "" {
return l
}
c := l.clone()
c.handler = l.handler.WithGroup(name).(*Handler)
return c
}
func (l *logger) clone() *logger {
c := *l
// TODO(hupeh): 测试 clone 是否报错
//c.writer = l.writer
//c.persistWriter = l.persistWriter
return &c
}
// Log logs at level.
func (l *logger) Log(level Level, msg string, args ...any) {
l.log(level, msg, args...)
}
// Trace logs at LevelTrace.
func (l *logger) Trace(msg string, args ...any) {
l.log(LevelTrace, msg, args...)
}
// Debug logs at LevelDebug.
func (l *logger) Debug(msg string, args ...any) {
l.log(LevelDebug, msg, args...)
}
// Info logs at LevelInfo.
func (l *logger) Info(msg string, args ...any) {
l.log(LevelInfo, msg, args...)
}
// Warn logs at LevelWarn.
func (l *logger) Warn(msg string, args ...any) {
l.log(LevelWarn, msg, args...)
}
// Error logs at LevelError.
func (l *logger) Error(msg string, args ...any) {
l.log(LevelError, msg, args...)
}
// Panic logs at PanicError.
func (l *logger) Panic(msg string, args ...any) {
l.log(LevelPanic, msg, args...)
}
// Fatal logs at LevelFatal.
func (l *logger) Fatal(msg string, args ...any) {
l.log(LevelFatal, msg, args...)
}
func (l *logger) log(level Level, msg string, args ...any) {
if !l.Enabled(level) {
return
}
var pc uintptr
if atomic.LoadInt32(&l.ignorePC) != 1 {
var pcs [1]uintptr
// skip [runtime.Callers, this function, this function's caller]
runtime.Callers(3, pcs[:])
pc = pcs[0]
}
r := slog.NewRecord(time.Now(), level.slog().Level(), msg, pc)
if len(args) > 0 {
var sprintfArgs []any
for _, arg := range args {
switch v := arg.(type) {
case Attr:
r.AddAttrs(v)
default:
sprintfArgs = append(sprintfArgs, arg)
}
}
if len(sprintfArgs) > 0 {
msg = fmt.Sprintf(msg, sprintfArgs...)
}
r.Message = msg
}
_ = l.handler.Handle(nil, r)
}