mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 18:22:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			520 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			520 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2019 The logr Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
// This design derives from Dave Cheney's blog:
 | 
						|
//     http://dave.cheney.net/2015/11/05/lets-talk-about-logging
 | 
						|
 | 
						|
// Package logr defines a general-purpose logging API and abstract interfaces
 | 
						|
// to back that API.  Packages in the Go ecosystem can depend on this package,
 | 
						|
// while callers can implement logging with whatever backend is appropriate.
 | 
						|
//
 | 
						|
// # Usage
 | 
						|
//
 | 
						|
// Logging is done using a Logger instance.  Logger is a concrete type with
 | 
						|
// methods, which defers the actual logging to a LogSink interface.  The main
 | 
						|
// methods of Logger are Info() and Error().  Arguments to Info() and Error()
 | 
						|
// are key/value pairs rather than printf-style formatted strings, emphasizing
 | 
						|
// "structured logging".
 | 
						|
//
 | 
						|
// With Go's standard log package, we might write:
 | 
						|
//
 | 
						|
//	log.Printf("setting target value %s", targetValue)
 | 
						|
//
 | 
						|
// With logr's structured logging, we'd write:
 | 
						|
//
 | 
						|
//	logger.Info("setting target", "value", targetValue)
 | 
						|
//
 | 
						|
// Errors are much the same.  Instead of:
 | 
						|
//
 | 
						|
//	log.Printf("failed to open the pod bay door for user %s: %v", user, err)
 | 
						|
//
 | 
						|
// We'd write:
 | 
						|
//
 | 
						|
//	logger.Error(err, "failed to open the pod bay door", "user", user)
 | 
						|
//
 | 
						|
// Info() and Error() are very similar, but they are separate methods so that
 | 
						|
// LogSink implementations can choose to do things like attach additional
 | 
						|
// information (such as stack traces) on calls to Error(). Error() messages are
 | 
						|
// always logged, regardless of the current verbosity.  If there is no error
 | 
						|
// instance available, passing nil is valid.
 | 
						|
//
 | 
						|
// # Verbosity
 | 
						|
//
 | 
						|
// Often we want to log information only when the application in "verbose
 | 
						|
// mode".  To write log lines that are more verbose, Logger has a V() method.
 | 
						|
// The higher the V-level of a log line, the less critical it is considered.
 | 
						|
// Log-lines with V-levels that are not enabled (as per the LogSink) will not
 | 
						|
// be written.  Level V(0) is the default, and logger.V(0).Info() has the same
 | 
						|
// meaning as logger.Info().  Negative V-levels have the same meaning as V(0).
 | 
						|
// Error messages do not have a verbosity level and are always logged.
 | 
						|
//
 | 
						|
// Where we might have written:
 | 
						|
//
 | 
						|
//	if flVerbose >= 2 {
 | 
						|
//	    log.Printf("an unusual thing happened")
 | 
						|
//	}
 | 
						|
//
 | 
						|
// We can write:
 | 
						|
//
 | 
						|
//	logger.V(2).Info("an unusual thing happened")
 | 
						|
//
 | 
						|
// # Logger Names
 | 
						|
//
 | 
						|
// Logger instances can have name strings so that all messages logged through
 | 
						|
// that instance have additional context.  For example, you might want to add
 | 
						|
// a subsystem name:
 | 
						|
//
 | 
						|
//	logger.WithName("compactor").Info("started", "time", time.Now())
 | 
						|
//
 | 
						|
// The WithName() method returns a new Logger, which can be passed to
 | 
						|
// constructors or other functions for further use.  Repeated use of WithName()
 | 
						|
// will accumulate name "segments".  These name segments will be joined in some
 | 
						|
// way by the LogSink implementation.  It is strongly recommended that name
 | 
						|
// segments contain simple identifiers (letters, digits, and hyphen), and do
 | 
						|
// not contain characters that could muddle the log output or confuse the
 | 
						|
// joining operation (e.g. whitespace, commas, periods, slashes, brackets,
 | 
						|
// quotes, etc).
 | 
						|
//
 | 
						|
// # Saved Values
 | 
						|
//
 | 
						|
// Logger instances can store any number of key/value pairs, which will be
 | 
						|
// logged alongside all messages logged through that instance.  For example,
 | 
						|
// you might want to create a Logger instance per managed object:
 | 
						|
//
 | 
						|
// With the standard log package, we might write:
 | 
						|
//
 | 
						|
//	log.Printf("decided to set field foo to value %q for object %s/%s",
 | 
						|
//	    targetValue, object.Namespace, object.Name)
 | 
						|
//
 | 
						|
// With logr we'd write:
 | 
						|
//
 | 
						|
//	// Elsewhere: set up the logger to log the object name.
 | 
						|
//	obj.logger = mainLogger.WithValues(
 | 
						|
//	    "name", obj.name, "namespace", obj.namespace)
 | 
						|
//
 | 
						|
//	// later on...
 | 
						|
//	obj.logger.Info("setting foo", "value", targetValue)
 | 
						|
//
 | 
						|
// # Best Practices
 | 
						|
//
 | 
						|
// Logger has very few hard rules, with the goal that LogSink implementations
 | 
						|
// might have a lot of freedom to differentiate.  There are, however, some
 | 
						|
// things to consider.
 | 
						|
//
 | 
						|
// The log message consists of a constant message attached to the log line.
 | 
						|
// This should generally be a simple description of what's occurring, and should
 | 
						|
// never be a format string.  Variable information can then be attached using
 | 
						|
// named values.
 | 
						|
//
 | 
						|
// Keys are arbitrary strings, but should generally be constant values.  Values
 | 
						|
// may be any Go value, but how the value is formatted is determined by the
 | 
						|
// LogSink implementation.
 | 
						|
//
 | 
						|
// Logger instances are meant to be passed around by value. Code that receives
 | 
						|
// such a value can call its methods without having to check whether the
 | 
						|
// instance is ready for use.
 | 
						|
//
 | 
						|
// The zero logger (= Logger{}) is identical to Discard() and discards all log
 | 
						|
// entries. Code that receives a Logger by value can simply call it, the methods
 | 
						|
// will never crash. For cases where passing a logger is optional, a pointer to Logger
 | 
						|
// should be used.
 | 
						|
//
 | 
						|
// # Key Naming Conventions
 | 
						|
//
 | 
						|
// Keys are not strictly required to conform to any specification or regex, but
 | 
						|
// it is recommended that they:
 | 
						|
//   - be human-readable and meaningful (not auto-generated or simple ordinals)
 | 
						|
//   - be constant (not dependent on input data)
 | 
						|
//   - contain only printable characters
 | 
						|
//   - not contain whitespace or punctuation
 | 
						|
//   - use lower case for simple keys and lowerCamelCase for more complex ones
 | 
						|
//
 | 
						|
// These guidelines help ensure that log data is processed properly regardless
 | 
						|
// of the log implementation.  For example, log implementations will try to
 | 
						|
// output JSON data or will store data for later database (e.g. SQL) queries.
 | 
						|
//
 | 
						|
// While users are generally free to use key names of their choice, it's
 | 
						|
// generally best to avoid using the following keys, as they're frequently used
 | 
						|
// by implementations:
 | 
						|
//   - "caller": the calling information (file/line) of a particular log line
 | 
						|
//   - "error": the underlying error value in the `Error` method
 | 
						|
//   - "level": the log level
 | 
						|
//   - "logger": the name of the associated logger
 | 
						|
//   - "msg": the log message
 | 
						|
//   - "stacktrace": the stack trace associated with a particular log line or
 | 
						|
//     error (often from the `Error` message)
 | 
						|
//   - "ts": the timestamp for a log line
 | 
						|
//
 | 
						|
// Implementations are encouraged to make use of these keys to represent the
 | 
						|
// above concepts, when necessary (for example, in a pure-JSON output form, it
 | 
						|
// would be necessary to represent at least message and timestamp as ordinary
 | 
						|
// named values).
 | 
						|
//
 | 
						|
// # Break Glass
 | 
						|
//
 | 
						|
// Implementations may choose to give callers access to the underlying
 | 
						|
// logging implementation.  The recommended pattern for this is:
 | 
						|
//
 | 
						|
//	// Underlier exposes access to the underlying logging implementation.
 | 
						|
//	// Since callers only have a logr.Logger, they have to know which
 | 
						|
//	// implementation is in use, so this interface is less of an abstraction
 | 
						|
//	// and more of way to test type conversion.
 | 
						|
//	type Underlier interface {
 | 
						|
//	    GetUnderlying() <underlying-type>
 | 
						|
//	}
 | 
						|
//
 | 
						|
// Logger grants access to the sink to enable type assertions like this:
 | 
						|
//
 | 
						|
//	func DoSomethingWithImpl(log logr.Logger) {
 | 
						|
//	    if underlier, ok := log.GetSink().(impl.Underlier); ok {
 | 
						|
//	       implLogger := underlier.GetUnderlying()
 | 
						|
//	       ...
 | 
						|
//	    }
 | 
						|
//	}
 | 
						|
//
 | 
						|
// Custom `With*` functions can be implemented by copying the complete
 | 
						|
// Logger struct and replacing the sink in the copy:
 | 
						|
//
 | 
						|
//	// WithFooBar changes the foobar parameter in the log sink and returns a
 | 
						|
//	// new logger with that modified sink.  It does nothing for loggers where
 | 
						|
//	// the sink doesn't support that parameter.
 | 
						|
//	func WithFoobar(log logr.Logger, foobar int) logr.Logger {
 | 
						|
//	   if foobarLogSink, ok := log.GetSink().(FoobarSink); ok {
 | 
						|
//	      log = log.WithSink(foobarLogSink.WithFooBar(foobar))
 | 
						|
//	   }
 | 
						|
//	   return log
 | 
						|
//	}
 | 
						|
//
 | 
						|
// Don't use New to construct a new Logger with a LogSink retrieved from an
 | 
						|
// existing Logger. Source code attribution might not work correctly and
 | 
						|
// unexported fields in Logger get lost.
 | 
						|
//
 | 
						|
// Beware that the same LogSink instance may be shared by different logger
 | 
						|
// instances. Calling functions that modify the LogSink will affect all of
 | 
						|
// those.
 | 
						|
package logr
 | 
						|
 | 
						|
// New returns a new Logger instance.  This is primarily used by libraries
 | 
						|
// implementing LogSink, rather than end users.  Passing a nil sink will create
 | 
						|
// a Logger which discards all log lines.
 | 
						|
func New(sink LogSink) Logger {
 | 
						|
	logger := Logger{}
 | 
						|
	logger.setSink(sink)
 | 
						|
	if sink != nil {
 | 
						|
		sink.Init(runtimeInfo)
 | 
						|
	}
 | 
						|
	return logger
 | 
						|
}
 | 
						|
 | 
						|
// setSink stores the sink and updates any related fields. It mutates the
 | 
						|
// logger and thus is only safe to use for loggers that are not currently being
 | 
						|
// used concurrently.
 | 
						|
func (l *Logger) setSink(sink LogSink) {
 | 
						|
	l.sink = sink
 | 
						|
}
 | 
						|
 | 
						|
// GetSink returns the stored sink.
 | 
						|
func (l Logger) GetSink() LogSink {
 | 
						|
	return l.sink
 | 
						|
}
 | 
						|
 | 
						|
// WithSink returns a copy of the logger with the new sink.
 | 
						|
func (l Logger) WithSink(sink LogSink) Logger {
 | 
						|
	l.setSink(sink)
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
// Logger is an interface to an abstract logging implementation.  This is a
 | 
						|
// concrete type for performance reasons, but all the real work is passed on to
 | 
						|
// a LogSink.  Implementations of LogSink should provide their own constructors
 | 
						|
// that return Logger, not LogSink.
 | 
						|
//
 | 
						|
// The underlying sink can be accessed through GetSink and be modified through
 | 
						|
// WithSink. This enables the implementation of custom extensions (see "Break
 | 
						|
// Glass" in the package documentation). Normally the sink should be used only
 | 
						|
// indirectly.
 | 
						|
type Logger struct {
 | 
						|
	sink  LogSink
 | 
						|
	level int
 | 
						|
}
 | 
						|
 | 
						|
// Enabled tests whether this Logger is enabled.  For example, commandline
 | 
						|
// flags might be used to set the logging verbosity and disable some info logs.
 | 
						|
func (l Logger) Enabled() bool {
 | 
						|
	// Some implementations of LogSink look at the caller in Enabled (e.g.
 | 
						|
	// different verbosity levels per package or file), but we only pass one
 | 
						|
	// CallDepth in (via Init).  This means that all calls from Logger to the
 | 
						|
	// LogSink's Enabled, Info, and Error methods must have the same number of
 | 
						|
	// frames.  In other words, Logger methods can't call other Logger methods
 | 
						|
	// which call these LogSink methods unless we do it the same in all paths.
 | 
						|
	return l.sink != nil && l.sink.Enabled(l.level)
 | 
						|
}
 | 
						|
 | 
						|
// Info logs a non-error message with the given key/value pairs as context.
 | 
						|
//
 | 
						|
// The msg argument should be used to add some constant description to the log
 | 
						|
// line.  The key/value pairs can then be used to add additional variable
 | 
						|
// information.  The key/value pairs must alternate string keys and arbitrary
 | 
						|
// values.
 | 
						|
func (l Logger) Info(msg string, keysAndValues ...any) {
 | 
						|
	if l.sink == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if l.sink.Enabled(l.level) { // see comment in Enabled
 | 
						|
		if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
 | 
						|
			withHelper.GetCallStackHelper()()
 | 
						|
		}
 | 
						|
		l.sink.Info(l.level, msg, keysAndValues...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Error logs an error, with the given message and key/value pairs as context.
 | 
						|
// It functions similarly to Info, but may have unique behavior, and should be
 | 
						|
// preferred for logging errors (see the package documentations for more
 | 
						|
// information). The log message will always be emitted, regardless of
 | 
						|
// verbosity level.
 | 
						|
//
 | 
						|
// The msg argument should be used to add context to any underlying error,
 | 
						|
// while the err argument should be used to attach the actual error that
 | 
						|
// triggered this log line, if present. The err parameter is optional
 | 
						|
// and nil may be passed instead of an error instance.
 | 
						|
func (l Logger) Error(err error, msg string, keysAndValues ...any) {
 | 
						|
	if l.sink == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
 | 
						|
		withHelper.GetCallStackHelper()()
 | 
						|
	}
 | 
						|
	l.sink.Error(err, msg, keysAndValues...)
 | 
						|
}
 | 
						|
 | 
						|
// V returns a new Logger instance for a specific verbosity level, relative to
 | 
						|
// this Logger.  In other words, V-levels are additive.  A higher verbosity
 | 
						|
// level means a log message is less important.  Negative V-levels are treated
 | 
						|
// as 0.
 | 
						|
func (l Logger) V(level int) Logger {
 | 
						|
	if l.sink == nil {
 | 
						|
		return l
 | 
						|
	}
 | 
						|
	if level < 0 {
 | 
						|
		level = 0
 | 
						|
	}
 | 
						|
	l.level += level
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
// GetV returns the verbosity level of the logger. If the logger's LogSink is
 | 
						|
// nil as in the Discard logger, this will always return 0.
 | 
						|
func (l Logger) GetV() int {
 | 
						|
	// 0 if l.sink nil because of the if check in V above.
 | 
						|
	return l.level
 | 
						|
}
 | 
						|
 | 
						|
// WithValues returns a new Logger instance with additional key/value pairs.
 | 
						|
// See Info for documentation on how key/value pairs work.
 | 
						|
func (l Logger) WithValues(keysAndValues ...any) Logger {
 | 
						|
	if l.sink == nil {
 | 
						|
		return l
 | 
						|
	}
 | 
						|
	l.setSink(l.sink.WithValues(keysAndValues...))
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
// WithName returns a new Logger instance with the specified name element added
 | 
						|
// to the Logger's name.  Successive calls with WithName append additional
 | 
						|
// suffixes to the Logger's name.  It's strongly recommended that name segments
 | 
						|
// contain only letters, digits, and hyphens (see the package documentation for
 | 
						|
// more information).
 | 
						|
func (l Logger) WithName(name string) Logger {
 | 
						|
	if l.sink == nil {
 | 
						|
		return l
 | 
						|
	}
 | 
						|
	l.setSink(l.sink.WithName(name))
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
// WithCallDepth returns a Logger instance that offsets the call stack by the
 | 
						|
// specified number of frames when logging call site information, if possible.
 | 
						|
// This is useful for users who have helper functions between the "real" call
 | 
						|
// site and the actual calls to Logger methods.  If depth is 0 the attribution
 | 
						|
// should be to the direct caller of this function.  If depth is 1 the
 | 
						|
// attribution should skip 1 call frame, and so on.  Successive calls to this
 | 
						|
// are additive.
 | 
						|
//
 | 
						|
// If the underlying log implementation supports a WithCallDepth(int) method,
 | 
						|
// it will be called and the result returned.  If the implementation does not
 | 
						|
// support CallDepthLogSink, the original Logger will be returned.
 | 
						|
//
 | 
						|
// To skip one level, WithCallStackHelper() should be used instead of
 | 
						|
// WithCallDepth(1) because it works with implementions that support the
 | 
						|
// CallDepthLogSink and/or CallStackHelperLogSink interfaces.
 | 
						|
func (l Logger) WithCallDepth(depth int) Logger {
 | 
						|
	if l.sink == nil {
 | 
						|
		return l
 | 
						|
	}
 | 
						|
	if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
 | 
						|
		l.setSink(withCallDepth.WithCallDepth(depth))
 | 
						|
	}
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
// WithCallStackHelper returns a new Logger instance that skips the direct
 | 
						|
// caller when logging call site information, if possible.  This is useful for
 | 
						|
// users who have helper functions between the "real" call site and the actual
 | 
						|
// calls to Logger methods and want to support loggers which depend on marking
 | 
						|
// each individual helper function, like loggers based on testing.T.
 | 
						|
//
 | 
						|
// In addition to using that new logger instance, callers also must call the
 | 
						|
// returned function.
 | 
						|
//
 | 
						|
// If the underlying log implementation supports a WithCallDepth(int) method,
 | 
						|
// WithCallDepth(1) will be called to produce a new logger. If it supports a
 | 
						|
// WithCallStackHelper() method, that will be also called. If the
 | 
						|
// implementation does not support either of these, the original Logger will be
 | 
						|
// returned.
 | 
						|
func (l Logger) WithCallStackHelper() (func(), Logger) {
 | 
						|
	if l.sink == nil {
 | 
						|
		return func() {}, l
 | 
						|
	}
 | 
						|
	var helper func()
 | 
						|
	if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
 | 
						|
		l.setSink(withCallDepth.WithCallDepth(1))
 | 
						|
	}
 | 
						|
	if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
 | 
						|
		helper = withHelper.GetCallStackHelper()
 | 
						|
	} else {
 | 
						|
		helper = func() {}
 | 
						|
	}
 | 
						|
	return helper, l
 | 
						|
}
 | 
						|
 | 
						|
// IsZero returns true if this logger is an uninitialized zero value
 | 
						|
func (l Logger) IsZero() bool {
 | 
						|
	return l.sink == nil
 | 
						|
}
 | 
						|
 | 
						|
// RuntimeInfo holds information that the logr "core" library knows which
 | 
						|
// LogSinks might want to know.
 | 
						|
type RuntimeInfo struct {
 | 
						|
	// CallDepth is the number of call frames the logr library adds between the
 | 
						|
	// end-user and the LogSink.  LogSink implementations which choose to print
 | 
						|
	// the original logging site (e.g. file & line) should climb this many
 | 
						|
	// additional frames to find it.
 | 
						|
	CallDepth int
 | 
						|
}
 | 
						|
 | 
						|
// runtimeInfo is a static global.  It must not be changed at run time.
 | 
						|
var runtimeInfo = RuntimeInfo{
 | 
						|
	CallDepth: 1,
 | 
						|
}
 | 
						|
 | 
						|
// LogSink represents a logging implementation.  End-users will generally not
 | 
						|
// interact with this type.
 | 
						|
type LogSink interface {
 | 
						|
	// Init receives optional information about the logr library for LogSink
 | 
						|
	// implementations that need it.
 | 
						|
	Init(info RuntimeInfo)
 | 
						|
 | 
						|
	// Enabled tests whether this LogSink is enabled at the specified V-level.
 | 
						|
	// For example, commandline flags might be used to set the logging
 | 
						|
	// verbosity and disable some info logs.
 | 
						|
	Enabled(level int) bool
 | 
						|
 | 
						|
	// Info logs a non-error message with the given key/value pairs as context.
 | 
						|
	// The level argument is provided for optional logging.  This method will
 | 
						|
	// only be called when Enabled(level) is true. See Logger.Info for more
 | 
						|
	// details.
 | 
						|
	Info(level int, msg string, keysAndValues ...any)
 | 
						|
 | 
						|
	// Error logs an error, with the given message and key/value pairs as
 | 
						|
	// context.  See Logger.Error for more details.
 | 
						|
	Error(err error, msg string, keysAndValues ...any)
 | 
						|
 | 
						|
	// WithValues returns a new LogSink with additional key/value pairs.  See
 | 
						|
	// Logger.WithValues for more details.
 | 
						|
	WithValues(keysAndValues ...any) LogSink
 | 
						|
 | 
						|
	// WithName returns a new LogSink with the specified name appended.  See
 | 
						|
	// Logger.WithName for more details.
 | 
						|
	WithName(name string) LogSink
 | 
						|
}
 | 
						|
 | 
						|
// CallDepthLogSink represents a LogSink that knows how to climb the call stack
 | 
						|
// to identify the original call site and can offset the depth by a specified
 | 
						|
// number of frames.  This is useful for users who have helper functions
 | 
						|
// between the "real" call site and the actual calls to Logger methods.
 | 
						|
// Implementations that log information about the call site (such as file,
 | 
						|
// function, or line) would otherwise log information about the intermediate
 | 
						|
// helper functions.
 | 
						|
//
 | 
						|
// This is an optional interface and implementations are not required to
 | 
						|
// support it.
 | 
						|
type CallDepthLogSink interface {
 | 
						|
	// WithCallDepth returns a LogSink that will offset the call
 | 
						|
	// stack by the specified number of frames when logging call
 | 
						|
	// site information.
 | 
						|
	//
 | 
						|
	// If depth is 0, the LogSink should skip exactly the number
 | 
						|
	// of call frames defined in RuntimeInfo.CallDepth when Info
 | 
						|
	// or Error are called, i.e. the attribution should be to the
 | 
						|
	// direct caller of Logger.Info or Logger.Error.
 | 
						|
	//
 | 
						|
	// If depth is 1 the attribution should skip 1 call frame, and so on.
 | 
						|
	// Successive calls to this are additive.
 | 
						|
	WithCallDepth(depth int) LogSink
 | 
						|
}
 | 
						|
 | 
						|
// CallStackHelperLogSink represents a LogSink that knows how to climb
 | 
						|
// the call stack to identify the original call site and can skip
 | 
						|
// intermediate helper functions if they mark themselves as
 | 
						|
// helper. Go's testing package uses that approach.
 | 
						|
//
 | 
						|
// This is useful for users who have helper functions between the
 | 
						|
// "real" call site and the actual calls to Logger methods.
 | 
						|
// Implementations that log information about the call site (such as
 | 
						|
// file, function, or line) would otherwise log information about the
 | 
						|
// intermediate helper functions.
 | 
						|
//
 | 
						|
// This is an optional interface and implementations are not required
 | 
						|
// to support it. Implementations that choose to support this must not
 | 
						|
// simply implement it as WithCallDepth(1), because
 | 
						|
// Logger.WithCallStackHelper will call both methods if they are
 | 
						|
// present. This should only be implemented for LogSinks that actually
 | 
						|
// need it, as with testing.T.
 | 
						|
type CallStackHelperLogSink interface {
 | 
						|
	// GetCallStackHelper returns a function that must be called
 | 
						|
	// to mark the direct caller as helper function when logging
 | 
						|
	// call site information.
 | 
						|
	GetCallStackHelper() func()
 | 
						|
}
 | 
						|
 | 
						|
// Marshaler is an optional interface that logged values may choose to
 | 
						|
// implement. Loggers with structured output, such as JSON, should
 | 
						|
// log the object return by the MarshalLog method instead of the
 | 
						|
// original value.
 | 
						|
type Marshaler interface {
 | 
						|
	// MarshalLog can be used to:
 | 
						|
	//   - ensure that structs are not logged as strings when the original
 | 
						|
	//     value has a String method: return a different type without a
 | 
						|
	//     String method
 | 
						|
	//   - select which fields of a complex type should get logged:
 | 
						|
	//     return a simpler struct with fewer fields
 | 
						|
	//   - log unexported fields: return a different struct
 | 
						|
	//     with exported fields
 | 
						|
	//
 | 
						|
	// It may return any value of any type.
 | 
						|
	MarshalLog() any
 | 
						|
}
 |