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 }