slog.go raw

   1  package log
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"io"
   7  	"log/slog"
   8  	"os"
   9  )
  10  
  11  var (
  12  	// slogNOPHandler is a discarding slog.Handler
  13  	slogNOPHandler = slog.NewTextHandler(io.Discard, nil)
  14  	// slogDefaultHandler is default slog.Handler
  15  	slogDefaultHandler = NewSlogHandler(os.Stderr, &slog.HandlerOptions{
  16  		Level: slog.LevelInfo,
  17  	})
  18  )
  19  
  20  const (
  21  	// LevelTrace is custom trace slog level
  22  	LevelTrace = slog.Level(-8)
  23  	// LevelFatal is custom fatal slog level
  24  	LevelFatal = slog.Level(12)
  25  )
  26  
  27  // NewSlogAdapter returns SlogAdapter with specified slog.Handler
  28  func NewSlogAdapter(h slog.Handler) SlogAdapter {
  29  	l := slog.New(h)
  30  	return SlogAdapter{l}
  31  }
  32  
  33  // SlogAdapter is wrapper for slog.Logger
  34  type SlogAdapter struct {
  35  	*slog.Logger
  36  }
  37  
  38  // Trace logs at trace level
  39  // The attribute arguments are processed as follows:
  40  // If an argument is a string and this is not the last argument, the following argument is treated as the value and the two are combined into an key - value pair.
  41  // Otherwise, the argument is treated as a value with key "!BADKEY".
  42  func (l SlogAdapter) Trace(msg string, args ...any) {
  43  	l.Logger.Log(context.Background(), LevelTrace, msg, args...)
  44  }
  45  
  46  // Debug logs at debug level,
  47  // The attribute arguments are processed as follows:
  48  // If an argument is a string and this is not the last argument, the following argument is treated as the value and the two are combined into an key - value pair.
  49  // Otherwise, the argument is treated as a value with key "!BADKEY".
  50  func (l SlogAdapter) Debug(msg string, args ...any) {
  51  	l.Logger.Debug(msg, logArgs(args)...)
  52  }
  53  
  54  // Info logs at info level
  55  // The attribute arguments are processed as follows:
  56  // If an argument is a string and this is not the last argument, the following argument is treated as the value and the two are combined into an key - value pair.
  57  // Otherwise, the argument is treated as a value with key "!BADKEY".
  58  func (l SlogAdapter) Info(msg string, args ...any) {
  59  	l.Logger.Info(msg, logArgs(args)...)
  60  }
  61  
  62  // Warn logs at warn level
  63  // The attribute arguments are processed as follows:
  64  // If an argument is a string and this is not the last argument, the following argument is treated as the value and the two are combined into an key - value pair.
  65  // Otherwise, the argument is treated as a value with key "!BADKEY".
  66  func (l SlogAdapter) Warn(msg string, args ...any) {
  67  	l.Logger.Warn(msg, logArgs(args)...)
  68  }
  69  
  70  // Error logs at error level
  71  // The attribute arguments are processed as follows:
  72  // If an argument is a string and this is not the last argument, the following argument is treated as the value and the two are combined into an key - value pair.
  73  // Otherwise, the argument is treated as a value with key "!BADKEY".
  74  func (l SlogAdapter) Error(msg string, args ...any) {
  75  	l.Logger.Error(msg, logArgs(args)...)
  76  }
  77  
  78  // Fatal logs at fatal level, followed by an exit.
  79  // The attribute arguments are processed as follows:
  80  // If an argument is a string and this is not the last argument, the following argument is treated as the value and the two are combined into an key - value pair.
  81  // Otherwise, the argument is treated as a value with key "!BADKEY".
  82  func (l SlogAdapter) Fatal(msg string, args ...any) {
  83  	l.Logger.Log(context.Background(), LevelFatal, msg, args...)
  84  	os.Exit(1)
  85  }
  86  
  87  // Tracef formats according to a format specifier and logs the resulting string at trace level
  88  func (l SlogAdapter) Tracef(msg string, args ...any) {
  89  	m := fmt.Sprintf(msg, args...)
  90  	l.Logger.Log(context.Background(), LevelTrace, m)
  91  }
  92  
  93  // Debugf formats according to a format specifier and logs the resulting string at debug level
  94  func (l SlogAdapter) Debugf(msg string, args ...any) {
  95  	m := fmt.Sprintf(msg, args...)
  96  	l.Logger.Debug(m)
  97  }
  98  
  99  // Infof formats according to a format specifier and logs the resulting string at info level
 100  func (l SlogAdapter) Infof(msg string, args ...any) {
 101  	m := fmt.Sprintf(msg, args...)
 102  	l.Logger.Info(m)
 103  }
 104  
 105  // Warnf formats according to a format specifier and logs the resulting string at warn level
 106  func (l SlogAdapter) Warnf(msg string, args ...any) {
 107  	m := fmt.Sprintf(msg, args...)
 108  	l.Logger.Warn(m)
 109  }
 110  
 111  // Errorf formats according to a format specifier and logs the resulting string at error level
 112  func (l SlogAdapter) Errorf(msg string, args ...any) {
 113  	m := fmt.Sprintf(msg, args...)
 114  	l.Logger.Error(m)
 115  }
 116  
 117  // Fatalf formats according to a format specifier and logs the resulting string at fatal level, followed by an exit.
 118  func (l SlogAdapter) Fatalf(msg string, args ...any) {
 119  	m := fmt.Sprintf(msg, args...)
 120  	l.Logger.Log(context.Background(), LevelFatal, m)
 121  	os.Exit(1)
 122  }
 123  
 124  // With returns new Interface that includes given fields with each operation
 125  // returned logger will log each field on every logging operation it will be used
 126  func (l SlogAdapter) With(key string, fields Fields) Interface {
 127  	newLogger := l.Logger.With(key, fields)
 128  	return SlogAdapter{newLogger}
 129  }
 130  
 131  func logArgs(args []any) []any {
 132  	result := []any{}
 133  	for _, arg := range args {
 134  		switch v := arg.(type) {
 135  		case Fields:
 136  			result = append(result, v.Fields().Get()...)
 137  		default:
 138  			result = append(result, arg)
 139  		}
 140  	}
 141  	return result
 142  }
 143