github.com/searKing/golang/go@v1.2.117/log/slog/attrs.go (about)

     1  // Copyright 2023 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package slog
     6  
     7  import (
     8  	"fmt"
     9  	"log/slog"
    10  	"slices"
    11  	"strings"
    12  	"time"
    13  
    14  	strings_ "github.com/searKing/golang/go/strings"
    15  )
    16  
    17  // Error returns an Attr for an error value.
    18  func Error(err error) slog.Attr {
    19  	return slog.Any(ErrorKey, err)
    20  }
    21  
    22  // isEmptyAttr reports whether a has an empty key and a nil value.
    23  // That can be written as Attr{} or Any("", nil).
    24  func isEmptyAttr(a slog.Attr) bool {
    25  	if a.Key == "" {
    26  		return true
    27  	}
    28  	switch a.Value.Kind() {
    29  	case slog.KindAny:
    30  		return a.Equal(slog.Any("", nil))
    31  	case slog.KindBool:
    32  		return a.Equal(slog.Bool("", false))
    33  	case slog.KindDuration:
    34  		return a.Equal(slog.Duration("", 0))
    35  	case slog.KindFloat64:
    36  		return a.Equal(slog.Float64("", 0))
    37  	case slog.KindInt64:
    38  		return a.Equal(slog.Int64("", 0))
    39  	case slog.KindString:
    40  		return a.Equal(slog.String("", ""))
    41  	case slog.KindTime:
    42  		return a.Equal(slog.Time("", time.Time{}))
    43  	case slog.KindUint64:
    44  		return a.Equal(slog.Uint64("", 0))
    45  	case slog.KindGroup:
    46  		return a.Equal(slog.Group(""))
    47  	default:
    48  		s := a.String()
    49  		return s == "" || s == "<nil>"
    50  	}
    51  }
    52  
    53  // ReplaceAttrTruncate returns [ReplaceAttr] which shrinks attr's key and value[string]'s len to n at most.
    54  func ReplaceAttrTruncate(n int) func(groups []string, a slog.Attr) slog.Attr {
    55  	return func(groups []string, a slog.Attr) slog.Attr {
    56  		if n > 0 {
    57  			k := truncate(a.Key, n)
    58  			switch a.Value.Kind() {
    59  			case slog.KindString:
    60  				return slog.String(k, truncate(a.Value.String(), n))
    61  			default:
    62  				return a
    63  			}
    64  		}
    65  		return a
    66  	}
    67  }
    68  
    69  // ReplaceAttrKeys allows customization of the key names for default fields.
    70  type ReplaceAttrKeys map[string]string
    71  
    72  func (f ReplaceAttrKeys) resolve(key string) string {
    73  	if k, ok := f[key]; ok {
    74  		return k
    75  	}
    76  	return key
    77  }
    78  
    79  // This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
    80  // dumping it. If this code wasn't there doing:
    81  //
    82  //	slog.With("level", 1).Info("hello")
    83  //
    84  // Would just silently drop the user provided level. Instead with this code
    85  // it'll logged as:
    86  //
    87  //	{"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
    88  //
    89  // It's not exported because it's still using Data in an opinionated way. It's to
    90  // avoid code duplication between the two default formatters.
    91  func prefixAttrClashes(attrs []slog.Attr, builtinAttrKeys ReplaceAttrKeys) []slog.Attr {
    92  	if len(builtinAttrKeys) == 0 {
    93  		return attrs
    94  	}
    95  	attrs = replaceAttrClash(attrs, builtinAttrKeys.resolve(slog.TimeKey))
    96  	attrs = replaceAttrClash(attrs, builtinAttrKeys.resolve(slog.MessageKey))
    97  	attrs = replaceAttrClash(attrs, builtinAttrKeys.resolve(slog.LevelKey))
    98  	attrs = replaceAttrClash(attrs, builtinAttrKeys.resolve(slog.SourceKey))
    99  	return attrs
   100  }
   101  
   102  func replaceAttrClash(attrs []slog.Attr, key string) []slog.Attr {
   103  	var val slog.Value
   104  	if !slices.ContainsFunc(attrs, func(attr slog.Attr) bool {
   105  		return attr.Key == key
   106  	}) {
   107  		return attrs
   108  	}
   109  	attrs = append(attrs, slog.Attr{
   110  		Key:   "fields." + key,
   111  		Value: val,
   112  	})
   113  	return slices.DeleteFunc(attrs, func(attr slog.Attr) bool {
   114  		return attr.Key == key
   115  	})
   116  }
   117  
   118  func truncate(s string, n int) string {
   119  	if len(s) <= n {
   120  		return s
   121  	}
   122  	var buf strings.Builder
   123  	buf.WriteString(fmt.Sprintf("size: %d, string: ", len(s)))
   124  	buf.WriteString(strings_.Truncate(s, n))
   125  	return buf.String()
   126  }