gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

funcr.go (24487B)


      1 /*
      2 Copyright 2021 The logr Authors.
      3 
      4 Licensed under the Apache License, Version 2.0 (the "License");
      5 you may not use this file except in compliance with the License.
      6 You may obtain a copy of the License at
      7 
      8     http://www.apache.org/licenses/LICENSE-2.0
      9 
     10 Unless required by applicable law or agreed to in writing, software
     11 distributed under the License is distributed on an "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 See the License for the specific language governing permissions and
     14 limitations under the License.
     15 */
     16 
     17 // Package funcr implements formatting of structured log messages and
     18 // optionally captures the call site and timestamp.
     19 //
     20 // The simplest way to use it is via its implementation of a
     21 // github.com/go-logr/logr.LogSink with output through an arbitrary
     22 // "write" function.  See New and NewJSON for details.
     23 //
     24 // # Custom LogSinks
     25 //
     26 // For users who need more control, a funcr.Formatter can be embedded inside
     27 // your own custom LogSink implementation. This is useful when the LogSink
     28 // needs to implement additional methods, for example.
     29 //
     30 // # Formatting
     31 //
     32 // This will respect logr.Marshaler, fmt.Stringer, and error interfaces for
     33 // values which are being logged.  When rendering a struct, funcr will use Go's
     34 // standard JSON tags (all except "string").
     35 package funcr
     36 
     37 import (
     38 	"bytes"
     39 	"encoding"
     40 	"encoding/json"
     41 	"fmt"
     42 	"path/filepath"
     43 	"reflect"
     44 	"runtime"
     45 	"strconv"
     46 	"strings"
     47 	"time"
     48 
     49 	"github.com/go-logr/logr"
     50 )
     51 
     52 // New returns a logr.Logger which is implemented by an arbitrary function.
     53 func New(fn func(prefix, args string), opts Options) logr.Logger {
     54 	return logr.New(newSink(fn, NewFormatter(opts)))
     55 }
     56 
     57 // NewJSON returns a logr.Logger which is implemented by an arbitrary function
     58 // and produces JSON output.
     59 func NewJSON(fn func(obj string), opts Options) logr.Logger {
     60 	fnWrapper := func(_, obj string) {
     61 		fn(obj)
     62 	}
     63 	return logr.New(newSink(fnWrapper, NewFormatterJSON(opts)))
     64 }
     65 
     66 // Underlier exposes access to the underlying logging function. Since
     67 // callers only have a logr.Logger, they have to know which
     68 // implementation is in use, so this interface is less of an
     69 // abstraction and more of a way to test type conversion.
     70 type Underlier interface {
     71 	GetUnderlying() func(prefix, args string)
     72 }
     73 
     74 func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink {
     75 	l := &fnlogger{
     76 		Formatter: formatter,
     77 		write:     fn,
     78 	}
     79 	// For skipping fnlogger.Info and fnlogger.Error.
     80 	l.Formatter.AddCallDepth(1)
     81 	return l
     82 }
     83 
     84 // Options carries parameters which influence the way logs are generated.
     85 type Options struct {
     86 	// LogCaller tells funcr to add a "caller" key to some or all log lines.
     87 	// This has some overhead, so some users might not want it.
     88 	LogCaller MessageClass
     89 
     90 	// LogCallerFunc tells funcr to also log the calling function name.  This
     91 	// has no effect if caller logging is not enabled (see Options.LogCaller).
     92 	LogCallerFunc bool
     93 
     94 	// LogTimestamp tells funcr to add a "ts" key to log lines.  This has some
     95 	// overhead, so some users might not want it.
     96 	LogTimestamp bool
     97 
     98 	// TimestampFormat tells funcr how to render timestamps when LogTimestamp
     99 	// is enabled.  If not specified, a default format will be used.  For more
    100 	// details, see docs for Go's time.Layout.
    101 	TimestampFormat string
    102 
    103 	// Verbosity tells funcr which V logs to produce.  Higher values enable
    104 	// more logs.  Info logs at or below this level will be written, while logs
    105 	// above this level will be discarded.
    106 	Verbosity int
    107 
    108 	// RenderBuiltinsHook allows users to mutate the list of key-value pairs
    109 	// while a log line is being rendered.  The kvList argument follows logr
    110 	// conventions - each pair of slice elements is comprised of a string key
    111 	// and an arbitrary value (verified and sanitized before calling this
    112 	// hook).  The value returned must follow the same conventions.  This hook
    113 	// can be used to audit or modify logged data.  For example, you might want
    114 	// to prefix all of funcr's built-in keys with some string.  This hook is
    115 	// only called for built-in (provided by funcr itself) key-value pairs.
    116 	// Equivalent hooks are offered for key-value pairs saved via
    117 	// logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and
    118 	// for user-provided pairs (see RenderArgsHook).
    119 	RenderBuiltinsHook func(kvList []interface{}) []interface{}
    120 
    121 	// RenderValuesHook is the same as RenderBuiltinsHook, except that it is
    122 	// only called for key-value pairs saved via logr.Logger.WithValues.  See
    123 	// RenderBuiltinsHook for more details.
    124 	RenderValuesHook func(kvList []interface{}) []interface{}
    125 
    126 	// RenderArgsHook is the same as RenderBuiltinsHook, except that it is only
    127 	// called for key-value pairs passed directly to Info and Error.  See
    128 	// RenderBuiltinsHook for more details.
    129 	RenderArgsHook func(kvList []interface{}) []interface{}
    130 
    131 	// MaxLogDepth tells funcr how many levels of nested fields (e.g. a struct
    132 	// that contains a struct, etc.) it may log.  Every time it finds a struct,
    133 	// slice, array, or map the depth is increased by one.  When the maximum is
    134 	// reached, the value will be converted to a string indicating that the max
    135 	// depth has been exceeded.  If this field is not specified, a default
    136 	// value will be used.
    137 	MaxLogDepth int
    138 }
    139 
    140 // MessageClass indicates which category or categories of messages to consider.
    141 type MessageClass int
    142 
    143 const (
    144 	// None ignores all message classes.
    145 	None MessageClass = iota
    146 	// All considers all message classes.
    147 	All
    148 	// Info only considers info messages.
    149 	Info
    150 	// Error only considers error messages.
    151 	Error
    152 )
    153 
    154 // fnlogger inherits some of its LogSink implementation from Formatter
    155 // and just needs to add some glue code.
    156 type fnlogger struct {
    157 	Formatter
    158 	write func(prefix, args string)
    159 }
    160 
    161 func (l fnlogger) WithName(name string) logr.LogSink {
    162 	l.Formatter.AddName(name)
    163 	return &l
    164 }
    165 
    166 func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
    167 	l.Formatter.AddValues(kvList)
    168 	return &l
    169 }
    170 
    171 func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
    172 	l.Formatter.AddCallDepth(depth)
    173 	return &l
    174 }
    175 
    176 func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
    177 	prefix, args := l.FormatInfo(level, msg, kvList)
    178 	l.write(prefix, args)
    179 }
    180 
    181 func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
    182 	prefix, args := l.FormatError(err, msg, kvList)
    183 	l.write(prefix, args)
    184 }
    185 
    186 func (l fnlogger) GetUnderlying() func(prefix, args string) {
    187 	return l.write
    188 }
    189 
    190 // Assert conformance to the interfaces.
    191 var _ logr.LogSink = &fnlogger{}
    192 var _ logr.CallDepthLogSink = &fnlogger{}
    193 var _ Underlier = &fnlogger{}
    194 
    195 // NewFormatter constructs a Formatter which emits a JSON-like key=value format.
    196 func NewFormatter(opts Options) Formatter {
    197 	return newFormatter(opts, outputKeyValue)
    198 }
    199 
    200 // NewFormatterJSON constructs a Formatter which emits strict JSON.
    201 func NewFormatterJSON(opts Options) Formatter {
    202 	return newFormatter(opts, outputJSON)
    203 }
    204 
    205 // Defaults for Options.
    206 const defaultTimestampFormat = "2006-01-02 15:04:05.000000"
    207 const defaultMaxLogDepth = 16
    208 
    209 func newFormatter(opts Options, outfmt outputFormat) Formatter {
    210 	if opts.TimestampFormat == "" {
    211 		opts.TimestampFormat = defaultTimestampFormat
    212 	}
    213 	if opts.MaxLogDepth == 0 {
    214 		opts.MaxLogDepth = defaultMaxLogDepth
    215 	}
    216 	f := Formatter{
    217 		outputFormat: outfmt,
    218 		prefix:       "",
    219 		values:       nil,
    220 		depth:        0,
    221 		opts:         &opts,
    222 	}
    223 	return f
    224 }
    225 
    226 // Formatter is an opaque struct which can be embedded in a LogSink
    227 // implementation. It should be constructed with NewFormatter. Some of
    228 // its methods directly implement logr.LogSink.
    229 type Formatter struct {
    230 	outputFormat outputFormat
    231 	prefix       string
    232 	values       []interface{}
    233 	valuesStr    string
    234 	depth        int
    235 	opts         *Options
    236 }
    237 
    238 // outputFormat indicates which outputFormat to use.
    239 type outputFormat int
    240 
    241 const (
    242 	// outputKeyValue emits a JSON-like key=value format, but not strict JSON.
    243 	outputKeyValue outputFormat = iota
    244 	// outputJSON emits strict JSON.
    245 	outputJSON
    246 )
    247 
    248 // PseudoStruct is a list of key-value pairs that gets logged as a struct.
    249 type PseudoStruct []interface{}
    250 
    251 // render produces a log line, ready to use.
    252 func (f Formatter) render(builtins, args []interface{}) string {
    253 	// Empirically bytes.Buffer is faster than strings.Builder for this.
    254 	buf := bytes.NewBuffer(make([]byte, 0, 1024))
    255 	if f.outputFormat == outputJSON {
    256 		buf.WriteByte('{')
    257 	}
    258 	vals := builtins
    259 	if hook := f.opts.RenderBuiltinsHook; hook != nil {
    260 		vals = hook(f.sanitize(vals))
    261 	}
    262 	f.flatten(buf, vals, false, false) // keys are ours, no need to escape
    263 	continuing := len(builtins) > 0
    264 	if len(f.valuesStr) > 0 {
    265 		if continuing {
    266 			if f.outputFormat == outputJSON {
    267 				buf.WriteByte(',')
    268 			} else {
    269 				buf.WriteByte(' ')
    270 			}
    271 		}
    272 		continuing = true
    273 		buf.WriteString(f.valuesStr)
    274 	}
    275 	vals = args
    276 	if hook := f.opts.RenderArgsHook; hook != nil {
    277 		vals = hook(f.sanitize(vals))
    278 	}
    279 	f.flatten(buf, vals, continuing, true) // escape user-provided keys
    280 	if f.outputFormat == outputJSON {
    281 		buf.WriteByte('}')
    282 	}
    283 	return buf.String()
    284 }
    285 
    286 // flatten renders a list of key-value pairs into a buffer.  If continuing is
    287 // true, it assumes that the buffer has previous values and will emit a
    288 // separator (which depends on the output format) before the first pair it
    289 // writes.  If escapeKeys is true, the keys are assumed to have
    290 // non-JSON-compatible characters in them and must be evaluated for escapes.
    291 //
    292 // This function returns a potentially modified version of kvList, which
    293 // ensures that there is a value for every key (adding a value if needed) and
    294 // that each key is a string (substituting a key if needed).
    295 func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool, escapeKeys bool) []interface{} {
    296 	// This logic overlaps with sanitize() but saves one type-cast per key,
    297 	// which can be measurable.
    298 	if len(kvList)%2 != 0 {
    299 		kvList = append(kvList, noValue)
    300 	}
    301 	for i := 0; i < len(kvList); i += 2 {
    302 		k, ok := kvList[i].(string)
    303 		if !ok {
    304 			k = f.nonStringKey(kvList[i])
    305 			kvList[i] = k
    306 		}
    307 		v := kvList[i+1]
    308 
    309 		if i > 0 || continuing {
    310 			if f.outputFormat == outputJSON {
    311 				buf.WriteByte(',')
    312 			} else {
    313 				// In theory the format could be something we don't understand.  In
    314 				// practice, we control it, so it won't be.
    315 				buf.WriteByte(' ')
    316 			}
    317 		}
    318 
    319 		if escapeKeys {
    320 			buf.WriteString(prettyString(k))
    321 		} else {
    322 			// this is faster
    323 			buf.WriteByte('"')
    324 			buf.WriteString(k)
    325 			buf.WriteByte('"')
    326 		}
    327 		if f.outputFormat == outputJSON {
    328 			buf.WriteByte(':')
    329 		} else {
    330 			buf.WriteByte('=')
    331 		}
    332 		buf.WriteString(f.pretty(v))
    333 	}
    334 	return kvList
    335 }
    336 
    337 func (f Formatter) pretty(value interface{}) string {
    338 	return f.prettyWithFlags(value, 0, 0)
    339 }
    340 
    341 const (
    342 	flagRawStruct = 0x1 // do not print braces on structs
    343 )
    344 
    345 // TODO: This is not fast. Most of the overhead goes here.
    346 func (f Formatter) prettyWithFlags(value interface{}, flags uint32, depth int) string {
    347 	if depth > f.opts.MaxLogDepth {
    348 		return `"<max-log-depth-exceeded>"`
    349 	}
    350 
    351 	// Handle types that take full control of logging.
    352 	if v, ok := value.(logr.Marshaler); ok {
    353 		// Replace the value with what the type wants to get logged.
    354 		// That then gets handled below via reflection.
    355 		value = invokeMarshaler(v)
    356 	}
    357 
    358 	// Handle types that want to format themselves.
    359 	switch v := value.(type) {
    360 	case fmt.Stringer:
    361 		value = invokeStringer(v)
    362 	case error:
    363 		value = invokeError(v)
    364 	}
    365 
    366 	// Handling the most common types without reflect is a small perf win.
    367 	switch v := value.(type) {
    368 	case bool:
    369 		return strconv.FormatBool(v)
    370 	case string:
    371 		return prettyString(v)
    372 	case int:
    373 		return strconv.FormatInt(int64(v), 10)
    374 	case int8:
    375 		return strconv.FormatInt(int64(v), 10)
    376 	case int16:
    377 		return strconv.FormatInt(int64(v), 10)
    378 	case int32:
    379 		return strconv.FormatInt(int64(v), 10)
    380 	case int64:
    381 		return strconv.FormatInt(int64(v), 10)
    382 	case uint:
    383 		return strconv.FormatUint(uint64(v), 10)
    384 	case uint8:
    385 		return strconv.FormatUint(uint64(v), 10)
    386 	case uint16:
    387 		return strconv.FormatUint(uint64(v), 10)
    388 	case uint32:
    389 		return strconv.FormatUint(uint64(v), 10)
    390 	case uint64:
    391 		return strconv.FormatUint(v, 10)
    392 	case uintptr:
    393 		return strconv.FormatUint(uint64(v), 10)
    394 	case float32:
    395 		return strconv.FormatFloat(float64(v), 'f', -1, 32)
    396 	case float64:
    397 		return strconv.FormatFloat(v, 'f', -1, 64)
    398 	case complex64:
    399 		return `"` + strconv.FormatComplex(complex128(v), 'f', -1, 64) + `"`
    400 	case complex128:
    401 		return `"` + strconv.FormatComplex(v, 'f', -1, 128) + `"`
    402 	case PseudoStruct:
    403 		buf := bytes.NewBuffer(make([]byte, 0, 1024))
    404 		v = f.sanitize(v)
    405 		if flags&flagRawStruct == 0 {
    406 			buf.WriteByte('{')
    407 		}
    408 		for i := 0; i < len(v); i += 2 {
    409 			if i > 0 {
    410 				buf.WriteByte(',')
    411 			}
    412 			k, _ := v[i].(string) // sanitize() above means no need to check success
    413 			// arbitrary keys might need escaping
    414 			buf.WriteString(prettyString(k))
    415 			buf.WriteByte(':')
    416 			buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1))
    417 		}
    418 		if flags&flagRawStruct == 0 {
    419 			buf.WriteByte('}')
    420 		}
    421 		return buf.String()
    422 	}
    423 
    424 	buf := bytes.NewBuffer(make([]byte, 0, 256))
    425 	t := reflect.TypeOf(value)
    426 	if t == nil {
    427 		return "null"
    428 	}
    429 	v := reflect.ValueOf(value)
    430 	switch t.Kind() {
    431 	case reflect.Bool:
    432 		return strconv.FormatBool(v.Bool())
    433 	case reflect.String:
    434 		return prettyString(v.String())
    435 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    436 		return strconv.FormatInt(int64(v.Int()), 10)
    437 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    438 		return strconv.FormatUint(uint64(v.Uint()), 10)
    439 	case reflect.Float32:
    440 		return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32)
    441 	case reflect.Float64:
    442 		return strconv.FormatFloat(v.Float(), 'f', -1, 64)
    443 	case reflect.Complex64:
    444 		return `"` + strconv.FormatComplex(complex128(v.Complex()), 'f', -1, 64) + `"`
    445 	case reflect.Complex128:
    446 		return `"` + strconv.FormatComplex(v.Complex(), 'f', -1, 128) + `"`
    447 	case reflect.Struct:
    448 		if flags&flagRawStruct == 0 {
    449 			buf.WriteByte('{')
    450 		}
    451 		printComma := false // testing i>0 is not enough because of JSON omitted fields
    452 		for i := 0; i < t.NumField(); i++ {
    453 			fld := t.Field(i)
    454 			if fld.PkgPath != "" {
    455 				// reflect says this field is only defined for non-exported fields.
    456 				continue
    457 			}
    458 			if !v.Field(i).CanInterface() {
    459 				// reflect isn't clear exactly what this means, but we can't use it.
    460 				continue
    461 			}
    462 			name := ""
    463 			omitempty := false
    464 			if tag, found := fld.Tag.Lookup("json"); found {
    465 				if tag == "-" {
    466 					continue
    467 				}
    468 				if comma := strings.Index(tag, ","); comma != -1 {
    469 					if n := tag[:comma]; n != "" {
    470 						name = n
    471 					}
    472 					rest := tag[comma:]
    473 					if strings.Contains(rest, ",omitempty,") || strings.HasSuffix(rest, ",omitempty") {
    474 						omitempty = true
    475 					}
    476 				} else {
    477 					name = tag
    478 				}
    479 			}
    480 			if omitempty && isEmpty(v.Field(i)) {
    481 				continue
    482 			}
    483 			if printComma {
    484 				buf.WriteByte(',')
    485 			}
    486 			printComma = true // if we got here, we are rendering a field
    487 			if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
    488 				buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1))
    489 				continue
    490 			}
    491 			if name == "" {
    492 				name = fld.Name
    493 			}
    494 			// field names can't contain characters which need escaping
    495 			buf.WriteByte('"')
    496 			buf.WriteString(name)
    497 			buf.WriteByte('"')
    498 			buf.WriteByte(':')
    499 			buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1))
    500 		}
    501 		if flags&flagRawStruct == 0 {
    502 			buf.WriteByte('}')
    503 		}
    504 		return buf.String()
    505 	case reflect.Slice, reflect.Array:
    506 		// If this is outputing as JSON make sure this isn't really a json.RawMessage.
    507 		// If so just emit "as-is" and don't pretty it as that will just print
    508 		// it as [X,Y,Z,...] which isn't terribly useful vs the string form you really want.
    509 		if f.outputFormat == outputJSON {
    510 			if rm, ok := value.(json.RawMessage); ok {
    511 				// If it's empty make sure we emit an empty value as the array style would below.
    512 				if len(rm) > 0 {
    513 					buf.Write(rm)
    514 				} else {
    515 					buf.WriteString("null")
    516 				}
    517 				return buf.String()
    518 			}
    519 		}
    520 		buf.WriteByte('[')
    521 		for i := 0; i < v.Len(); i++ {
    522 			if i > 0 {
    523 				buf.WriteByte(',')
    524 			}
    525 			e := v.Index(i)
    526 			buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1))
    527 		}
    528 		buf.WriteByte(']')
    529 		return buf.String()
    530 	case reflect.Map:
    531 		buf.WriteByte('{')
    532 		// This does not sort the map keys, for best perf.
    533 		it := v.MapRange()
    534 		i := 0
    535 		for it.Next() {
    536 			if i > 0 {
    537 				buf.WriteByte(',')
    538 			}
    539 			// If a map key supports TextMarshaler, use it.
    540 			keystr := ""
    541 			if m, ok := it.Key().Interface().(encoding.TextMarshaler); ok {
    542 				txt, err := m.MarshalText()
    543 				if err != nil {
    544 					keystr = fmt.Sprintf("<error-MarshalText: %s>", err.Error())
    545 				} else {
    546 					keystr = string(txt)
    547 				}
    548 				keystr = prettyString(keystr)
    549 			} else {
    550 				// prettyWithFlags will produce already-escaped values
    551 				keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1)
    552 				if t.Key().Kind() != reflect.String {
    553 					// JSON only does string keys.  Unlike Go's standard JSON, we'll
    554 					// convert just about anything to a string.
    555 					keystr = prettyString(keystr)
    556 				}
    557 			}
    558 			buf.WriteString(keystr)
    559 			buf.WriteByte(':')
    560 			buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1))
    561 			i++
    562 		}
    563 		buf.WriteByte('}')
    564 		return buf.String()
    565 	case reflect.Ptr, reflect.Interface:
    566 		if v.IsNil() {
    567 			return "null"
    568 		}
    569 		return f.prettyWithFlags(v.Elem().Interface(), 0, depth)
    570 	}
    571 	return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
    572 }
    573 
    574 func prettyString(s string) string {
    575 	// Avoid escaping (which does allocations) if we can.
    576 	if needsEscape(s) {
    577 		return strconv.Quote(s)
    578 	}
    579 	b := bytes.NewBuffer(make([]byte, 0, 1024))
    580 	b.WriteByte('"')
    581 	b.WriteString(s)
    582 	b.WriteByte('"')
    583 	return b.String()
    584 }
    585 
    586 // needsEscape determines whether the input string needs to be escaped or not,
    587 // without doing any allocations.
    588 func needsEscape(s string) bool {
    589 	for _, r := range s {
    590 		if !strconv.IsPrint(r) || r == '\\' || r == '"' {
    591 			return true
    592 		}
    593 	}
    594 	return false
    595 }
    596 
    597 func isEmpty(v reflect.Value) bool {
    598 	switch v.Kind() {
    599 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
    600 		return v.Len() == 0
    601 	case reflect.Bool:
    602 		return !v.Bool()
    603 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    604 		return v.Int() == 0
    605 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    606 		return v.Uint() == 0
    607 	case reflect.Float32, reflect.Float64:
    608 		return v.Float() == 0
    609 	case reflect.Complex64, reflect.Complex128:
    610 		return v.Complex() == 0
    611 	case reflect.Interface, reflect.Ptr:
    612 		return v.IsNil()
    613 	}
    614 	return false
    615 }
    616 
    617 func invokeMarshaler(m logr.Marshaler) (ret interface{}) {
    618 	defer func() {
    619 		if r := recover(); r != nil {
    620 			ret = fmt.Sprintf("<panic: %s>", r)
    621 		}
    622 	}()
    623 	return m.MarshalLog()
    624 }
    625 
    626 func invokeStringer(s fmt.Stringer) (ret string) {
    627 	defer func() {
    628 		if r := recover(); r != nil {
    629 			ret = fmt.Sprintf("<panic: %s>", r)
    630 		}
    631 	}()
    632 	return s.String()
    633 }
    634 
    635 func invokeError(e error) (ret string) {
    636 	defer func() {
    637 		if r := recover(); r != nil {
    638 			ret = fmt.Sprintf("<panic: %s>", r)
    639 		}
    640 	}()
    641 	return e.Error()
    642 }
    643 
    644 // Caller represents the original call site for a log line, after considering
    645 // logr.Logger.WithCallDepth and logr.Logger.WithCallStackHelper.  The File and
    646 // Line fields will always be provided, while the Func field is optional.
    647 // Users can set the render hook fields in Options to examine logged key-value
    648 // pairs, one of which will be {"caller", Caller} if the Options.LogCaller
    649 // field is enabled for the given MessageClass.
    650 type Caller struct {
    651 	// File is the basename of the file for this call site.
    652 	File string `json:"file"`
    653 	// Line is the line number in the file for this call site.
    654 	Line int `json:"line"`
    655 	// Func is the function name for this call site, or empty if
    656 	// Options.LogCallerFunc is not enabled.
    657 	Func string `json:"function,omitempty"`
    658 }
    659 
    660 func (f Formatter) caller() Caller {
    661 	// +1 for this frame, +1 for Info/Error.
    662 	pc, file, line, ok := runtime.Caller(f.depth + 2)
    663 	if !ok {
    664 		return Caller{"<unknown>", 0, ""}
    665 	}
    666 	fn := ""
    667 	if f.opts.LogCallerFunc {
    668 		if fp := runtime.FuncForPC(pc); fp != nil {
    669 			fn = fp.Name()
    670 		}
    671 	}
    672 
    673 	return Caller{filepath.Base(file), line, fn}
    674 }
    675 
    676 const noValue = "<no-value>"
    677 
    678 func (f Formatter) nonStringKey(v interface{}) string {
    679 	return fmt.Sprintf("<non-string-key: %s>", f.snippet(v))
    680 }
    681 
    682 // snippet produces a short snippet string of an arbitrary value.
    683 func (f Formatter) snippet(v interface{}) string {
    684 	const snipLen = 16
    685 
    686 	snip := f.pretty(v)
    687 	if len(snip) > snipLen {
    688 		snip = snip[:snipLen]
    689 	}
    690 	return snip
    691 }
    692 
    693 // sanitize ensures that a list of key-value pairs has a value for every key
    694 // (adding a value if needed) and that each key is a string (substituting a key
    695 // if needed).
    696 func (f Formatter) sanitize(kvList []interface{}) []interface{} {
    697 	if len(kvList)%2 != 0 {
    698 		kvList = append(kvList, noValue)
    699 	}
    700 	for i := 0; i < len(kvList); i += 2 {
    701 		_, ok := kvList[i].(string)
    702 		if !ok {
    703 			kvList[i] = f.nonStringKey(kvList[i])
    704 		}
    705 	}
    706 	return kvList
    707 }
    708 
    709 // Init configures this Formatter from runtime info, such as the call depth
    710 // imposed by logr itself.
    711 // Note that this receiver is a pointer, so depth can be saved.
    712 func (f *Formatter) Init(info logr.RuntimeInfo) {
    713 	f.depth += info.CallDepth
    714 }
    715 
    716 // Enabled checks whether an info message at the given level should be logged.
    717 func (f Formatter) Enabled(level int) bool {
    718 	return level <= f.opts.Verbosity
    719 }
    720 
    721 // GetDepth returns the current depth of this Formatter.  This is useful for
    722 // implementations which do their own caller attribution.
    723 func (f Formatter) GetDepth() int {
    724 	return f.depth
    725 }
    726 
    727 // FormatInfo renders an Info log message into strings.  The prefix will be
    728 // empty when no names were set (via AddNames), or when the output is
    729 // configured for JSON.
    730 func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) {
    731 	args := make([]interface{}, 0, 64) // using a constant here impacts perf
    732 	prefix = f.prefix
    733 	if f.outputFormat == outputJSON {
    734 		args = append(args, "logger", prefix)
    735 		prefix = ""
    736 	}
    737 	if f.opts.LogTimestamp {
    738 		args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat))
    739 	}
    740 	if policy := f.opts.LogCaller; policy == All || policy == Info {
    741 		args = append(args, "caller", f.caller())
    742 	}
    743 	args = append(args, "level", level, "msg", msg)
    744 	return prefix, f.render(args, kvList)
    745 }
    746 
    747 // FormatError renders an Error log message into strings.  The prefix will be
    748 // empty when no names were set (via AddNames),  or when the output is
    749 // configured for JSON.
    750 func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) {
    751 	args := make([]interface{}, 0, 64) // using a constant here impacts perf
    752 	prefix = f.prefix
    753 	if f.outputFormat == outputJSON {
    754 		args = append(args, "logger", prefix)
    755 		prefix = ""
    756 	}
    757 	if f.opts.LogTimestamp {
    758 		args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat))
    759 	}
    760 	if policy := f.opts.LogCaller; policy == All || policy == Error {
    761 		args = append(args, "caller", f.caller())
    762 	}
    763 	args = append(args, "msg", msg)
    764 	var loggableErr interface{}
    765 	if err != nil {
    766 		loggableErr = err.Error()
    767 	}
    768 	args = append(args, "error", loggableErr)
    769 	return f.prefix, f.render(args, kvList)
    770 }
    771 
    772 // AddName appends the specified name.  funcr uses '/' characters to separate
    773 // name elements.  Callers should not pass '/' in the provided name string, but
    774 // this library does not actually enforce that.
    775 func (f *Formatter) AddName(name string) {
    776 	if len(f.prefix) > 0 {
    777 		f.prefix += "/"
    778 	}
    779 	f.prefix += name
    780 }
    781 
    782 // AddValues adds key-value pairs to the set of saved values to be logged with
    783 // each log line.
    784 func (f *Formatter) AddValues(kvList []interface{}) {
    785 	// Three slice args forces a copy.
    786 	n := len(f.values)
    787 	f.values = append(f.values[:n:n], kvList...)
    788 
    789 	vals := f.values
    790 	if hook := f.opts.RenderValuesHook; hook != nil {
    791 		vals = hook(f.sanitize(vals))
    792 	}
    793 
    794 	// Pre-render values, so we don't have to do it on each Info/Error call.
    795 	buf := bytes.NewBuffer(make([]byte, 0, 1024))
    796 	f.flatten(buf, vals, false, true) // escape user-provided keys
    797 	f.valuesStr = buf.String()
    798 }
    799 
    800 // AddCallDepth increases the number of stack-frames to skip when attributing
    801 // the log line to a file and line.
    802 func (f *Formatter) AddCallDepth(depth int) {
    803 	f.depth += depth
    804 }