Involved Source Files Package funcr implements formatting of structured log messages and
optionally captures the call site and timestamp.
The simplest way to use it is via its implementation of a
github.com/go-logr/logr.LogSink with output through an arbitrary
"write" function. See New and NewJSON for details.
# Custom LogSinks
For users who need more control, a funcr.Formatter can be embedded inside
your own custom LogSink implementation. This is useful when the LogSink
needs to implement additional methods, for example.
# Formatting
This will respect logr.Marshaler, fmt.Stringer, and error interfaces for
values which are being logged. When rendering a struct, funcr will use Go's
standard JSON tags (all except "string").slogsink.go
Code Examples
package main
import (
"fmt"
"strings"
"github.com/go-logr/logr"
"github.com/go-logr/logr/funcr"
)
// NewStdoutLogger returns a logr.Logger that prints to stdout.
// It demonstrates how to implement a custom With* function which
// controls whether INFO or ERROR are printed in front of the log
// message.
func NewStdoutLogger() logr.Logger {
l := &stdoutlogger{
Formatter: funcr.NewFormatter(funcr.Options{}),
}
return logr.New(l)
}
type stdoutlogger struct {
funcr.Formatter
logMsgType bool
}
func (l stdoutlogger) WithName(name string) logr.LogSink {
l.AddName(name)
return &l
}
func (l stdoutlogger) WithValues(kvList ...any) logr.LogSink {
l.AddValues(kvList)
return &l
}
func (l stdoutlogger) WithCallDepth(depth int) logr.LogSink {
l.AddCallDepth(depth)
return &l
}
func (l stdoutlogger) Info(level int, msg string, kvList ...any) {
prefix, args := l.FormatInfo(level, msg, kvList)
l.write("INFO", prefix, args)
}
func (l stdoutlogger) Error(err error, msg string, kvList ...any) {
prefix, args := l.FormatError(err, msg, kvList)
l.write("ERROR", prefix, args)
}
func (l stdoutlogger) write(msgType, prefix, args string) {
var parts []string
if l.logMsgType {
parts = append(parts, msgType)
}
if prefix != "" {
parts = append(parts, prefix)
}
parts = append(parts, args)
fmt.Println(strings.Join(parts, ": "))
}
// WithLogMsgType returns a copy of the logger with new settings for
// logging the message type. It returns the original logger if the
// underlying LogSink is not a stdoutlogger.
func WithLogMsgType(log logr.Logger, logMsgType bool) logr.Logger {
if l, ok := log.GetSink().(*stdoutlogger); ok {
clone := *l
clone.logMsgType = logMsgType
log = log.WithSink(&clone)
}
return log
}
// Assert conformance to the interfaces.
var _ logr.LogSink = &stdoutlogger{}
var _ logr.CallDepthLogSink = &stdoutlogger{}
func main() {
l := NewStdoutLogger()
l.Info("no message type")
WithLogMsgType(l, true).Info("with message type")
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
log := funcr.New(func(prefix, args string) {
fmt.Println(prefix, args)
}, funcr.Options{})
log = log.WithName("MyLogger")
log = log.WithValues("savedKey", "savedValue")
log.Info("the message", "key", "value")
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
log := funcr.NewJSON(func(obj string) {
fmt.Println(obj)
}, funcr.Options{})
log = log.WithName("MyLogger")
log = log.WithValues("savedKey", "savedValue")
log.Info("the message", "key", "value")
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
log := funcr.NewJSON(
func(obj string) { fmt.Println(obj) },
funcr.Options{
LogCaller: funcr.All,
Verbosity: 1, // V(2) and higher is ignored.
})
log.V(0).Info("V(0) message", "key", "value")
log.V(1).Info("V(1) message", "key", "value")
log.V(2).Info("V(2) message", "key", "value")
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
type List struct {
Next *List
}
l := List{}
l.Next = &l // recursive
log := funcr.NewJSON(
func(obj string) { fmt.Println(obj) },
funcr.Options{MaxLogDepth: 4})
log.Info("recursive", "list", l)
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
// prefix all builtin keys with "log:"
prefixSpecialKeys := func(kvList []any) []any {
for i := 0; i < len(kvList); i += 2 {
k, _ := kvList[i].(string)
kvList[i] = "log:" + k
}
return kvList
}
// present saved values as a single JSON object
valuesAsObject := func(kvList []any) []any {
return []any{"labels", funcr.PseudoStruct(kvList)}
}
log := funcr.NewJSON(
func(obj string) { fmt.Println(obj) },
funcr.Options{
RenderBuiltinsHook: prefixSpecialKeys,
RenderValuesHook: valuesAsObject,
})
log = log.WithName("MyLogger")
log = log.WithValues("savedKey1", "savedVal1")
log = log.WithValues("savedKey2", "savedVal2")
log.Info("the message", "key", "value")
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
log := funcr.NewJSON(
func(obj string) { fmt.Println(obj) },
funcr.Options{})
kv := []any{
"field1", 12345,
"field2", true,
}
log.Info("the message", "key", funcr.PseudoStruct(kv))
}
package main
import (
"fmt"
"github.com/go-logr/logr/funcr"
)
func main() {
log := funcr.New(func(prefix, args string) {
fmt.Println(prefix, args)
}, funcr.Options{})
if underlier, ok := log.GetSink().(funcr.Underlier); ok {
fn := underlier.GetUnderlying()
fn("hello", "world")
}
}
Package-Level Type Names (total 6)
/* sort by: | */
Caller represents the original call site for a log line, after considering
logr.Logger.WithCallDepth and logr.Logger.WithCallStackHelper. The File and
Line fields will always be provided, while the Func field is optional.
Users can set the render hook fields in Options to examine logged key-value
pairs, one of which will be {"caller", Caller} if the Options.LogCaller
field is enabled for the given MessageClass. File is the basename of the file for this call site. Func is the function name for this call site, or empty if
Options.LogCallerFunc is not enabled. Line is the line number in the file for this call site.
Formatter is an opaque struct which can be embedded in a LogSink
implementation. It should be constructed with NewFormatter. Some of
its methods directly implement logr.LogSink. AddCallDepth increases the number of stack-frames to skip when attributing
the log line to a file and line. AddName appends the specified name. funcr uses '/' characters to separate
name elements. Callers should not pass '/' in the provided name string, but
this library does not actually enforce that. AddValues adds key-value pairs to the set of saved values to be logged with
each log line. Enabled checks whether an info message at the given level should be logged. FormatError renders an Error log message into strings. The prefix will be
empty when no names were set (via AddNames), or when the output is
configured for JSON. FormatInfo renders an Info log message into strings. The prefix will be
empty when no names were set (via AddNames), or when the output is
configured for JSON. GetDepth returns the current depth of this Formatter. This is useful for
implementations which do their own caller attribution. Init configures this Formatter from runtime info, such as the call depth
imposed by logr itself.
Note that this receiver is a pointer, so depth can be saved.
func NewFormatter(opts Options) Formatter
func NewFormatterJSON(opts Options) Formatter
MessageClass indicates which category or categories of messages to consider.
const All
const Error
const Info
const None
Options carries parameters which influence the way logs are generated. LogCaller tells funcr to add a "caller" key to some or all log lines.
This has some overhead, so some users might not want it. LogCallerFunc tells funcr to also log the calling function name. This
has no effect if caller logging is not enabled (see Options.LogCaller). LogInfoLevel tells funcr what key to use to log the info level.
If not specified, the info level will be logged as "level".
If this is set to "", the info level will not be logged at all. LogTimestamp tells funcr to add a "ts" key to log lines. This has some
overhead, so some users might not want it. MaxLogDepth tells funcr how many levels of nested fields (e.g. a struct
that contains a struct, etc.) it may log. Every time it finds a struct,
slice, array, or map the depth is increased by one. When the maximum is
reached, the value will be converted to a string indicating that the max
depth has been exceeded. If this field is not specified, a default
value will be used. RenderArgsHook is the same as RenderBuiltinsHook, except that it is only
called for key-value pairs passed directly to Info and Error. See
RenderBuiltinsHook for more details. RenderBuiltinsHook allows users to mutate the list of key-value pairs
while a log line is being rendered. The kvList argument follows logr
conventions - each pair of slice elements is comprised of a string key
and an arbitrary value (verified and sanitized before calling this
hook). The value returned must follow the same conventions. This hook
can be used to audit or modify logged data. For example, you might want
to prefix all of funcr's built-in keys with some string. This hook is
only called for built-in (provided by funcr itself) key-value pairs.
Equivalent hooks are offered for key-value pairs saved via
logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and
for user-provided pairs (see RenderArgsHook). RenderValuesHook is the same as RenderBuiltinsHook, except that it is
only called for key-value pairs saved via logr.Logger.WithValues. See
RenderBuiltinsHook for more details. TimestampFormat tells funcr how to render timestamps when LogTimestamp
is enabled. If not specified, a default format will be used. For more
details, see docs for Go's time.Layout. Verbosity tells funcr which V logs to produce. Higher values enable
more logs. Info logs at or below this level will be written, while logs
above this level will be discarded.
func New(fn func(prefix, args string), opts Options) logr.Logger
func NewFormatter(opts Options) Formatter
func NewFormatterJSON(opts Options) Formatter
func NewJSON(fn func(obj string), opts Options) logr.Logger
PseudoStruct is a list of key-value pairs that gets logged as a struct.
Underlier exposes access to the underlying logging function. 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 a way to test type conversion.( Underlier) GetUnderlying() func(prefix, args string)
Package-Level Functions (total 4)
New returns a logr.Logger which is implemented by an arbitrary function.
NewFormatter constructs a Formatter which emits a JSON-like key=value format.
NewFormatterJSON constructs a Formatter which emits strict JSON.
NewJSON returns a logr.Logger which is implemented by an arbitrary function
and produces JSON output.
Package-Level Constants (total 4)
All considers all message classes.
Error only considers error messages.
Info only considers info messages.
None ignores all message classes.
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.