github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/log/log.go (about) 1 package log 2 3 import ( 4 "context" 5 "io" 6 "log/slog" 7 "os" 8 "path/filepath" 9 "runtime" 10 "sync" 11 "time" 12 13 protolog "github.com/Asutorufa/yuhaiin/pkg/protos/config/log" 14 ) 15 16 //go:generate protoc --go_out=. --go_opt=paths=source_relative log.proto 17 18 type Logger interface { 19 Debug(string, ...any) 20 Info(string, ...any) 21 Warn(string, ...any) 22 Error(string, ...any) 23 Output(depth int, lev slog.Level, msg string, v ...any) 24 } 25 26 var DefaultLogger Logger = NewSLogger(1) 27 var OutputStderr bool = true 28 29 var writer *FileWriter 30 var mu sync.Mutex 31 32 func Set(config *protolog.Logcat, path string) { 33 mu.Lock() 34 defer mu.Unlock() 35 36 if logger, ok := DefaultLogger.(interface{ SetLevel(l slog.Level) }); ok { 37 logger.SetLevel(config.Level.SLogLevel()) 38 } 39 40 logger, ok := DefaultLogger.(interface{ SetOutput(io.Writer) }) 41 if !ok { 42 return 43 } 44 45 if !config.Save && writer != nil { 46 logger.SetOutput(os.Stdout) 47 writer.Close() 48 writer = nil 49 } 50 51 if config.Save && writer == nil { 52 writer = NewLogWriter(path) 53 if OutputStderr { 54 logger.SetOutput(io.MultiWriter(writer, os.Stderr)) 55 } else { 56 logger.SetOutput(writer) 57 } 58 } 59 } 60 61 func Close() error { 62 mu.Lock() 63 defer mu.Unlock() 64 if writer != nil { 65 writer.Close() 66 } 67 68 return nil 69 } 70 71 func Debug(msg string, v ...any) { DefaultLogger.Debug(msg, v...) } 72 func Info(msg string, v ...any) { DefaultLogger.Info(msg, v...) } 73 func Warn(msg string, v ...any) { DefaultLogger.Warn(msg, v...) } 74 func Error(msg string, v ...any) { DefaultLogger.Error(msg, v...) } 75 func Output(depth int, lev slog.Level, format string, v ...any) { 76 DefaultLogger.Output(depth, lev, format, v...) 77 } 78 func IfErr(msg string, f func() error, ignoreErr ...error) { 79 if err := f(); err != nil { 80 DefaultLogger.Error(msg+" failed", "err", err) 81 } 82 } 83 84 type slogger struct { 85 depth int 86 87 io.Writer 88 level slog.Level 89 *slog.Logger 90 } 91 92 func NewSLogger(depth int) Logger { 93 s := &slogger{ 94 Writer: os.Stdout, 95 depth: 1 + depth, 96 } 97 h := &slog.HandlerOptions{ 98 AddSource: true, 99 Level: s, 100 ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { 101 // Remove time. 102 // if a.Key == slog.TimeKey && len(groups) == 0 { 103 // a.Key = "" 104 // } 105 106 // Remove the directory from the source's filename. 107 if a.Key == slog.SourceKey { 108 source, ok := a.Value.Any().(*slog.Source) 109 if ok { 110 source.Function = "" 111 source.File = filepath.Base(source.File) 112 a.Value = slog.AnyValue(source) 113 } 114 } 115 116 return a 117 }, 118 } 119 120 s.Logger = slog.New(slog.NewTextHandler(s, h)) 121 return s 122 123 } 124 func (l *slogger) SetLevel(z slog.Level) { l.level = z } 125 func (l *slogger) Level() slog.Level { return l.level } 126 func (l *slogger) Debug(msg string, v ...any) { l.Output(1, slog.LevelDebug, msg, v...) } 127 func (l *slogger) Info(msg string, v ...any) { l.Output(1, slog.LevelInfo, msg, v...) } 128 func (l *slogger) Warn(msg string, v ...any) { l.Output(1, slog.LevelWarn, msg, v...) } 129 func (l *slogger) Error(msg string, v ...any) { l.Output(1, slog.LevelError, msg, v...) } 130 func (l *slogger) Output(depth int, level slog.Level, msg string, v ...any) { 131 ctx := context.Background() 132 133 if !l.Enabled(ctx, level) { 134 return 135 } 136 137 var pcs [1]uintptr 138 runtime.Callers(l.depth+depth+1, pcs[:]) // skip [Callers, Infof] 139 r := slog.NewRecord(time.Now(), level, msg, pcs[0]) 140 r.Add(v...) 141 142 _ = l.Logger.Handler().Handle(ctx, r) 143 } 144 145 func (l *slogger) SetOutput(w io.Writer) { l.Writer = w }