github.com/oarkflow/log@v1.0.78/logger_std_slog.go (about)

     1  //go:build go1.21
     2  // +build go1.21
     3  
     4  package log
     5  
     6  import (
     7  	"context"
     8  	"log/slog"
     9  )
    10  
    11  func slogAttrEval(e *Entry, a slog.Attr) *Entry {
    12  	if a.Equal(slog.Attr{}) {
    13  		return e
    14  	}
    15  	value := a.Value.Resolve()
    16  	switch value.Kind() {
    17  	case slog.KindGroup:
    18  		if len(value.Group()) == 0 {
    19  			return e
    20  		}
    21  		if a.Key == "" {
    22  			for _, attr := range value.Group() {
    23  				e = slogAttrEval(e, attr)
    24  			}
    25  			return e
    26  		}
    27  		e.buf = append(e.buf, ',', '"')
    28  		e.buf = append(e.buf, a.Key...)
    29  		e.buf = append(e.buf, '"', ':')
    30  		i := len(e.buf)
    31  		for _, attr := range value.Group() {
    32  			e = slogAttrEval(e, attr)
    33  		}
    34  		e.buf[i] = '{'
    35  		e.buf = append(e.buf, '}')
    36  		return e
    37  	case slog.KindBool:
    38  		return e.Bool(a.Key, value.Bool())
    39  	case slog.KindDuration:
    40  		return e.Dur(a.Key, value.Duration())
    41  	case slog.KindFloat64:
    42  		return e.Float64(a.Key, value.Float64())
    43  	case slog.KindInt64:
    44  		return e.Int64(a.Key, value.Int64())
    45  	case slog.KindString:
    46  		return e.Str(a.Key, value.String())
    47  	case slog.KindTime:
    48  		return e.Time(a.Key, value.Time())
    49  	case slog.KindUint64:
    50  		return e.Uint64(a.Key, value.Uint64())
    51  	case slog.KindAny:
    52  		fallthrough
    53  	default:
    54  		return e.Any(a.Key, value.Any())
    55  	}
    56  }
    57  
    58  type slogHandler struct {
    59  	logger   Logger
    60  	caller   int
    61  	grouping bool
    62  	groups   int
    63  	entry    Entry
    64  }
    65  
    66  func (h slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
    67  	if len(attrs) == 0 {
    68  		return &h
    69  	}
    70  	i := len(h.entry.buf)
    71  	for _, attr := range attrs {
    72  		h.entry = *slogAttrEval(&h.entry, attr)
    73  	}
    74  	if h.grouping {
    75  		h.entry.buf[i] = '{'
    76  	}
    77  	h.grouping = false
    78  	return &h
    79  }
    80  
    81  func (h slogHandler) WithGroup(name string) slog.Handler {
    82  	if name != "" {
    83  		if h.grouping {
    84  			h.entry.buf = append(h.entry.buf, '{')
    85  		} else {
    86  			h.entry.buf = append(h.entry.buf, ',')
    87  		}
    88  		h.entry.buf = append(h.entry.buf, '"')
    89  		h.entry.buf = append(h.entry.buf, name...)
    90  		h.entry.buf = append(h.entry.buf, '"', ':')
    91  		h.grouping = true
    92  		h.groups++
    93  	}
    94  	return &h
    95  }
    96  
    97  func (h *slogHandler) Enabled(_ context.Context, level slog.Level) bool {
    98  	switch level {
    99  	case slog.LevelDebug:
   100  		return h.logger.Level <= DebugLevel
   101  	case slog.LevelInfo:
   102  		return h.logger.Level <= InfoLevel
   103  	case slog.LevelWarn:
   104  		return h.logger.Level <= WarnLevel
   105  	case slog.LevelError:
   106  		return h.logger.Level <= ErrorLevel
   107  	}
   108  	return false
   109  }
   110  
   111  func (h *slogHandler) Handle(_ context.Context, r slog.Record) error {
   112  	var e *Entry
   113  	switch r.Level {
   114  	case slog.LevelDebug:
   115  		e = h.logger.Debug()
   116  	case slog.LevelInfo:
   117  		e = h.logger.Info()
   118  	case slog.LevelWarn:
   119  		e = h.logger.Warn()
   120  	case slog.LevelError:
   121  		e = h.logger.Error()
   122  	default:
   123  		e = h.logger.Log()
   124  	}
   125  
   126  	if h.caller != 0 {
   127  		e.caller(1, r.PC, h.caller < 0)
   128  	}
   129  
   130  	// msg
   131  	e = e.Str("message", r.Message)
   132  
   133  	// with
   134  	if b := h.entry.buf; len(b) != 0 {
   135  		e = e.Context(b)
   136  	}
   137  	i := len(e.buf)
   138  
   139  	// attrs
   140  	r.Attrs(func(attr slog.Attr) bool {
   141  		e = slogAttrEval(e, attr)
   142  		return true
   143  	})
   144  
   145  	lastindex := func(buf []byte) int {
   146  		for i := len(buf) - 3; i >= 1; i-- {
   147  			if buf[i] == '"' && (buf[i-1] == ',' || buf[i-1] == '{') {
   148  				return i
   149  			}
   150  		}
   151  		return -1
   152  	}
   153  
   154  	// group attrs
   155  	if h.grouping {
   156  		if r.NumAttrs() > 0 {
   157  			e.buf[i] = '{'
   158  		} else if i = lastindex(e.buf); i > 0 {
   159  			e.buf = e.buf[:i-1]
   160  			h.groups--
   161  			for e.buf[len(e.buf)-1] == ':' {
   162  				if i = lastindex(e.buf); i > 0 {
   163  					e.buf = e.buf[:i-1]
   164  					h.groups--
   165  				}
   166  			}
   167  		} else {
   168  			e.buf = append(e.buf, '{')
   169  		}
   170  	}
   171  
   172  	// brackets closing
   173  	switch h.groups {
   174  	case 0:
   175  		break
   176  	case 1:
   177  		e.buf = append(e.buf, '}')
   178  	case 2:
   179  		e.buf = append(e.buf, '}', '}')
   180  	case 3:
   181  		e.buf = append(e.buf, '}', '}', '}')
   182  	case 4:
   183  		e.buf = append(e.buf, '}', '}', '}', '}')
   184  	default:
   185  		for i := 0; i < h.groups; i++ {
   186  			e.buf = append(e.buf, '}')
   187  		}
   188  	}
   189  
   190  	e.Msg("")
   191  	return nil
   192  }
   193  
   194  // Slog wraps the Logger to provide *slog.Logger
   195  func (l *Logger) Slog() *slog.Logger {
   196  	h := &slogHandler{
   197  		logger: *l,
   198  		caller: l.Caller,
   199  	}
   200  
   201  	h.logger.Caller = 0
   202  
   203  	return slog.New(h)
   204  }