Documentation ¶
Overview ¶
Package logf is a logging package extending slog.
Included are:
- terminal output tuned for human readability (and fairly configurable)
- quick/consolidated configuration
- string interpolation/formatting methods, including error wrapping
- a smörgåsbord of little ideas that support these things (e.g., lazy Stores, JSON -> JSONValue)
Hello, world ¶
package main import "github.com/AndrewHarrisSPU/logf" func main() { log := logf.New().Logger() log.Info("Hello, Roswell") }
Interpolation ¶
Generating output similar to the earlier Hello, world progam:
log = log.With("place", "Roswell") log.Infof("Hello, {place}")
Reporting a UFO sighting:
ufo := errors.New("🛸 spotted") log.Errorf("{place}", ufo)
Generating a wrapped error:
ufo := errors.New("🛸 spotted") err := log.WrapErr("{place}", errors.New("🛸 spotted"))
TTY ¶
The TTY component is a Handler designed for logging to human eyes. It pretty-prints lines like:
▎ 15:04:05 message key:value
Various layout and formatting details are configurable.
A TTY can display tags set with [Logger.Tag] or detected by configuration ([Config.Tag] or [Config.TagEncode]). Tags can be alternative or auxilliary to long strings of attributes.
Integration with [slog] ¶
The logf-native Logger and Handler resemble slog counterparts. A logf.Logger can be built from a slog.Handler, and a logf.Handler is a valid slog.Handler.
Example usage:
Construct a Logger, which is in
log := logf.New().Logger()
The resulting logger is based on a TTY if standard output is a terminal. Otherwise, the logger is based on a slog.JSONHandler.
Passing a TTY:
tty := logf.New().TTY() slog.New(tty)
Construct a Logger, given a slog.Handler:
log := logf.UsingHandler(h)
The resulting logger may be unable interpolate over any attrbiutes set on a non-logf-Handler. In general, effort is made via type assertions to recover logf types, but recovery isn't always possible.
testlog ¶
A package [testlog] is also included in logf. It's offered more in the sense of "this is possible" rather than "this should be used".
Examples ¶
Note: in examples, some boilereplate is used to trim example output:
log := log.New(). Colors(false). // turns off colors ForceTTY(true). // forces TTY output, even though Example output doesn't write to a terminal Printer() // elides some slog Record fields
Example (Basic) ¶
package main import ( "errors" "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() // Like slog log.Info("Hello, Roswell") // Some interpolation log = log.With("place", "Roswell") log.Infof("Hello, {place}") // Errors ufo := errors.New("🛸 spotted") // Like slog log.Error("", ufo) // Logging with errors and interpolation log.Errorf("{place}", ufo) // Using a logger to wrap an error err := log.WrapErr("{place}", ufo) log.Error("", err) }
Output: Hello, Roswell Hello, Roswell 🛸 spotted Roswell: 🛸 spotted Roswell: 🛸 spotted
Example (FormattingVerbs) ¶
Formatting accepts fmt package verbs. Verbs appear after the ':' in `{key:verb}` strings.
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() log.Infof("{left-pad:%010d}", "left-pad", 1) log.Infof("pi is about {pi:%6.5f}", "pi", 355.0/113) }
Output: 0000000001 pi is about 3.14159
Example (InterpolationArguments) ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() // Unkeyed `{}` symbols parse key/value pairs in the logging call: log.Infof("The {} {} {} ...", "speed", "quick", "color", "brown", "animal", "fox", ) // Keyed `{key}` symbols interpolate on attribute keys // These attributes may exist in logger structure, or they may be provided in a logging call. log = log.With( "color", "brindle", "animal", "Boston Terrier", ) log.Infof("The {speed} {color} {animal} ...", "speed", "rocketing") }
Output: The quick brown fox ... The rocketing brindle Boston Terrier ...
Example (InterpolationArgumentsMixed) ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ShowLayout("message", "\t", "attrs"). ForceTTY(true). Logger() // The unkeyed interpolation token `{}` consumes the first agument pair ("pi", 3.14) // "greek" and "π" parse to a second attribute, which is interpolated by key log.Infof("{greek}: {}", "pi", 3.14, "greek", "π") }
Output: π: 3.14 pi:3.14 greek:π
Example (InterpolationEscapes) ¶
Interpolation can require escaping of '{', '}', and ':'
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() // A Salvador Dali mustache emoji needs no escaping - there is no interpolation log.Infof(`:-}`) // Also surreal: escaping into JSON log.Infof(`\{"{key}":"{value}"\}`, "key", "color", "value", "mauve") // A single colon is parsed as a separator between an interpolation key and a formatting verb log.Infof(`{:}`, "", "plaintext") // Escaping a common lisp keyword symbol log.Infof(`{\:keyword}`, ":keyword", "lisp") // \Slashes, "quotes", and `backticks` log.Infof("{\\\\}", `\`, `slash`) log.Infof(`{\\}`, `\`, `slash`) }
Output: :-} {"color":"mauve"} plaintext lisp slash slash
Example (InterpolationLogValuer) ¶
Interpolation of [slog.LogValuer]s is powerful, but can be subtle.
package main import ( "github.com/AndrewHarrisSPU/logf" ) type mapWithLogValueMethod map[string]any func (mv mapWithLogValueMethod) LogValue() logf.Value { var as []logf.Attr for k, v := range mv { as = append(as, logf.KV(k, v)) } return logf.GroupValue(as...) } func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() vmap := mapWithLogValueMethod{ "first": 1, "second": [2]struct{}{}, "third": "Hello, world", } log.Infof("{vmap.first}", "vmap", vmap) log.Infof("{vmap.second}", "vmap", vmap) // SUBTLE: // this won't work, becuase vmap is not associated with "vmap" log.Infof("{vmap.third}", vmap) }
Output: 1 [{} {}] !missing-match
Example (InterpolationTimeVerbs) ¶
Interpolation of time values accepts some additional verbs. See [Config.TimeFormat] for formatting of TTY time fields.
package main import ( "time" "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() log.Infof("time interpolation formatting:") log.Infof("no verb {}", time.Time{}) log.Infof("RFC3339 {:RFC3339}", time.Time{}) log.Infof("kitchen {:kitchen}", time.Time{}) log.Infof("timestamp {:stamp}", time.Time{}) log.Infof("epoch {:epoch}", time.Time{}) // custom formatting uses strings like time.ShowLayout, using a semicolon rather than ':' log.Infof("custom {:15;03;04}", time.Time{}) log.Infof("duration interpolation formatting:") d := time.Unix(1000, 0).Sub(time.Unix(1, 0)) log.Infof("no verb {}", d) log.Infof("epoch {:epoch}", d) }
Output: time interpolation formatting: no verb 1754-08-30T22:43:41.128Z RFC3339 1754-08-30T22:43:41Z kitchen 10:43PM timestamp Aug 30 22:43:41 epoch -6795364579 custom 22:10:43 duration interpolation formatting: no verb 16m39s epoch 999000000000
Example (Structure) ¶
Building attributes is essential to capturing structure. For convenience, logf aliases or reimplements some slog.Attr-forming functions.
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("message", "\t", "attrs"). ShowColor(false). ForceTTY(true). Logger() // logf.Attr <=> slog.Attr // (likewise for logf.Value) var files logf.Attr // KV <=> slog.Any files = logf.KV("files", "X") // Attrs builds a slice of attrs, munging arguments mulder := logf.Attrs( files, "title", "Special Agent", "name", "Fox Mulder", ) // Group <=> slog.Group agent := logf.Group("agent", mulder...) log = log.With(agent) log.Info("The Truth Is Out There") }
Output: The Truth Is Out There agent:{files:X title:Special Agent name:Fox Mulder}
Index ¶
- Constants
- Variables
- func Fmt(f string, args ...any) string
- func WrapErr(f string, err error, args ...any) error
- type Attr
- type Buffer
- type Config
- func (cfg *Config) AddSource(toggle bool) *Config
- func (cfg *Config) Aux(aux slog.Handler) *Config
- func (cfg *Config) ForceAux(toggle bool) *Config
- func (cfg *Config) ForceTTY(toggle bool) *Config
- func (cfg *Config) JSON() Logger
- func (cfg *Config) Logger() Logger
- func (cfg *Config) Printer() Logger
- func (cfg *Config) Ref(level *slog.LevelVar) *Config
- func (cfg *Config) ReplaceFunc(replace func(scope []string, a Attr) Attr) *Config
- func (cfg *Config) ShowAttrKey(color string, enc Encoder[string]) *Config
- func (cfg *Config) ShowAttrValue(color string, enc Encoder[Value]) *Config
- func (cfg *Config) ShowColor(toggle bool) *Config
- func (cfg *Config) ShowGroup(color string, open Encoder[int], close Encoder[int]) *Config
- func (cfg *Config) ShowLayout(fields ...string) *Config
- func (cfg *Config) ShowLevel(enc Encoder[slog.Level]) *Config
- func (cfg *Config) ShowLevelColors(debug string, info string, warn string, error string) *Config
- func (cfg *Config) ShowMessage(color string) *Config
- func (cfg *Config) ShowSource(color string, enc Encoder[SourceLine]) *Config
- func (cfg *Config) ShowTag(key string, color string) *Config
- func (cfg *Config) ShowTagEncode(key string, color string, enc Encoder[Attr]) *Config
- func (cfg *Config) ShowTime(color string, enc Encoder[time.Time]) *Config
- func (cfg *Config) TTY() *TTY
- func (cfg *Config) Text() Logger
- func (cfg *Config) Writer(w io.Writer) *Config
- type Encoder
- type Handler
- type Level
- type Logger
- func (l Logger) Debugf(msg string, args ...any)
- func (l Logger) Errorf(msg string, err error, args ...any)
- func (l Logger) Fmt(f string, args ...any) string
- func (l Logger) Infof(msg string, args ...any)
- func (l Logger) Warnf(msg string, args ...any)
- func (l Logger) With(args ...any) Logger
- func (l Logger) WithContext(ctx context.Context) Logger
- func (l Logger) WithGroup(name string) Logger
- func (l Logger) WrapErr(f string, err error, args ...any) error
- type SourceLine
- type Store
- type TTY
- func (tty *TTY) Enabled(level slog.Level) bool
- func (tty *TTY) Filter(tags ...string)
- func (tty *TTY) Handle(r slog.Record) (auxErr error)
- func (tty *TTY) LogValue() slog.Value
- func (tty *TTY) Logger() Logger
- func (tty *TTY) Printf(f string, args ...any)
- func (tty *TTY) SetRef(level slog.Level)
- func (tty *TTY) WithAttrs(as []Attr) slog.Handler
- func (tty *TTY) WithGroup(name string) slog.Handler
- func (tty *TTY) WriteString(s string) (n int, err error)
- type Value
Examples ¶
- Package (Basic)
- Package (FormattingVerbs)
- Package (InterpolationArguments)
- Package (InterpolationArgumentsMixed)
- Package (InterpolationEscapes)
- Package (InterpolationLogValuer)
- Package (InterpolationTimeVerbs)
- Package (Structure)
- Config.ShowLayout
- Encoder
- Fmt
- JSONValue
- Logger (Tag)
- Logger.Errorf
- Logger.With
- Logger.WithGroup
- Logger.WrapErr
- WrapErr
Constants ¶
const ( DEBUG = slog.DebugLevel INFO = slog.InfoLevel WARN = slog.WarnLevel ERROR = slog.ErrorLevel )
Variables ¶
var StdRef slog.LevelVar
StdRef is a global slog.LevelVar used in default-ish configurations.
Functions ¶
func Fmt ¶ added in v0.1.1
Fmt interpolates the f string with the given arguments. The arguments parse as with Attrs.
Example ¶
package main import ( "fmt" "github.com/AndrewHarrisSPU/logf" ) func main() { // (KV is equivalent to slog.Any) flavor := logf.KV("flavor", "coconut") // logf.Fmt works with slog data msg := logf.Fmt("{flavor} pie", flavor) fmt.Println(msg) }
Output: coconut pie
func WrapErr ¶ added in v0.1.1
WrapErr interpolates the f string with the given arguments and error. The arguments parse as with Attrs. The returned error matches errors.Is/errors.As behavior, as with fmt.Errorf.
Example ¶
Logging, wrapping, and bubbling errors are all possible
package main import ( "errors" "fmt" "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("message", "\t", "attrs"). ShowColor(false). ForceTTY(true). Logger() log = log.WithGroup("emails").With("user", "Strong Bad", "id", "12345") err := errors.New("the system is down") // i. logging the error log.Error("", err) // with added context log.Errorf("{emails.user}", err) // ii. wrapping the error, with no msg -> the error err2 := logf.WrapErr("", err) fmt.Println(err2.Error()) // iii. wrapping the error, with interpolated context err3 := log.WrapErr("{emails.user}", err) fmt.Println(err3.Error()) // (equivalently) err3 = logf.WrapErr("{emails.user}", err, log) fmt.Println(err3.Error()) }
Output: the system is down emails:{user:Strong Bad id:12345 err:the system is down} Strong Bad: the system is down emails:{user:Strong Bad id:12345 err:Strong Bad: the system is down} the system is down Strong Bad: the system is down Strong Bad: the system is down
Types ¶
type Attr ¶
See slog.Attr.
func Attrs ¶
Attrs constructs a slice of Attrs from a list of arguments. In a loop evaluating the first remaining element:
- A string is interpreted as a key for a following value. An Attr consuming two list elements is appended to the return.
- An Attr is appended to the return.
- A slice of Attrs is flattened into the return.
- A slog.LogValuer which resolves to a slog.Group is flattened into the return.
Malformed lists result in Attrs indicating missing arguments, keys, or values.
type Buffer ¶
type Buffer struct {
// contains filtered or unexported fields
}
Buffer offers an Encoder a way to write to a pooled resource when building TTY log lines. A Buffer is writable during TTY field encoding, and is invalid otherwise. It is not safe to store a Buffer outside of usage in EncodeFunc, and a Buffer is not safe for use in go routines.
func (Buffer) WriteString ¶
func (Buffer) WriteValue ¶
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config is a base type for Logger and TTY configuration.
To construct a Logger with an already extant slog.Handler, see UsingHandler.
If a TTY would employ a Writer that isn't a terminal, Config methods result in a slog.JSONHandler-based Logger, unless Config.ForceTTY is set. Config.Aux is available for additional configuration of auxilliary logging.
Typical usage ¶
1. The logf.New function opens a new Config instance.
2. Next, zero or more Config methods are chained to set configuration fields.
Methods applying to any handler or logger produced by the Config, and defaults:
- Config.Writer: os.Stdout
- Config.Ref: logf.StdRef
- Config.AddSource: false
- Config.ReplaceFunc: nil
Methods applying only to a TTY, or a logger based on one, and default arguments:
- Config.Aux: none
- Config.ForceAux: false
- Config.ForceTTY: false
Methods configuring the color and encoding of TTY fields:
- Config.ShowAttrKey
- Config.ShowAttrValue
- Config.ShowColor: true
- Config.ShowGroup: "dim"
- Config.ShowLayout: "level", "time", "tags", "message", "\t", "attrs"
- Config.ShowLevel: LevelBar
- Config.ShowLevelColors: "bright cyan", "bright green", "bright yellow", "bright red"
- Config.ShowMessage: ""
- Config.ShowSource: "dim", SourceAbs
- Config.ShowTag: "#", "bright magenta"
- Config.ShowTagEncode: nil
- Config.ShowTime: "dim", TimeShort
3. A Config method returning a Logger or a TTY closes the chained invocation:
- Config.TTY returns a TTY
- Config.Logger returns a Logger based on a TTY.
- Config.Printer returns a Logger, based on a TTY, with a preset layout.
- Config.JSON returns a Logger based on a slog.JSONHandler
- Config.Text returns a Logger based on a slog.TextHandler
func NewDefault ¶ added in v0.1.2
func NewDefault() *Config
NewDefault is in all ways similar to New, except that using NewDefault configures the first logger or handler produced by the configuration to become the slogging default, using slog.SetDefault.
func (*Config) AddSource ¶
AddSource configures the inclusion of source file and line information in log lines.
func (*Config) Aux ¶ added in v0.1.2
Aux configures an auxilliary handler for a TTY. The auxilliary handler is employed:
- If, the TTY's writer is not a tty devices, and Config.ForceTTY is configured false
- Or, if Config.ForceAux is configured true.
If these conditions are met but no auxilliary handler has been provided, a slog.JSONHandler writing to the configured writer is used.
func (*Config) ForceAux ¶ added in v0.1.2
ForceAux configures any TTY produced by the configuraton to always employ an auxilliary handler.
func (*Config) ForceTTY ¶
ForceTTY configures any TTY produced by the configuration to always encode with TTY output. This overrides logic that otherwise falls back to JSON output when a configured writer is not detected to be a terminal.
func (*Config) JSON ¶
JSON returns a Logger using a slog.JSONHandler for encoding.
Only Config.Writer, [Config.Level], Config.AddSource, and Config.ReplaceFunc configuration is applied.
func (*Config) Logger ¶
If the configured Writer is a terminal, the returned *Logger is TTY-based Otherwise, the returned *Logger a JSONHandler]-based
func (*Config) Printer ¶
Printer returns a TTY-based Logger that only emits tags and messages. If the configured Writer is a terminal, the returned Logger is TTY-based Otherwise, the returned Logger a JSONHandler]-based
func (*Config) Ref ¶
Ref configures the use of the given reference slog.LevelVar.
func (*Config) ReplaceFunc ¶
ReplaceAttr configures the use of the given function to replace Attrs when logging. See slog.HandlerOptions.
func (*Config) ShowAttrKey ¶ added in v0.1.2
ShowAttrKey sets a color and an encoder for slog.Attr.Key encoding. If the enc argument is nil, the configuration uses an Encoder that simply writes the slog.Attr.Key. TODO: this default does no escaping. Perhaps JSON quoting and escaping would be useful.
func (*Config) ShowAttrValue ¶ added in v0.1.2
ShowAttrValue sets a color and an encoder for slog.Attr.Value encoding. If the enc argument is nil, the configuration uses an default Encoder. TODO: this default does no escaping. Perhaps JSON quoting and escaping would be useful.
func (*Config) ShowColor ¶ added in v0.1.2
Colors toggles TTY color encoding, using ANSI escape codes.
TODO: support cygwin escape codes.
func (*Config) ShowGroup ¶ added in v0.1.2
ShowGroup sets a color and a pair of encoders for opening and closing groups. If the open or close arguments are nil, [Encoder]s that write "{" or "}" tokens are used.
func (*Config) ShowLayout ¶ added in v0.1.2
ShowLayout configures the fields encoded in a TTY log line.
ShowLayout recognizes the following strings (and ignores others):
Log fields:
- "time"
- "level"
- "message" (alt "msg")
- "attrs" (alt "attr")
- "tags" (alt "tag")
- "source" (alt "src")
Spacing:
- "\n" (results in a newline, followed by a tab)
- " "
- "\t"
If Config.AddSource is configured, source information is the last field encoded in a log line.
Example ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("level", "attrs", "message", "tag", "\n", "source"). ShowLevel(logf.LevelBar). ShowSource("", logf.SourcePkg). ShowColor(false). AddSource(true). ForceTTY(true). Logger(). With("#", "rightTag") log.Info("Hello!", "leftAttr", "here") }
Output: ▏ leftAttr:here Hello! rightTag logf
func (*Config) ShowLevel ¶ added in v0.1.2
ShowLevel sets an encoder for the slog.Record.Level field. If the enc argument is nil, the configuration uses the LevelBar function.
func (*Config) ShowLevelColors ¶ added in v0.1.2
ShowLevelColors configures four colors for DEBUG, INFO, WARN, and ERROR levels. These colors are used when a slog.Record.Level is encoded.
func (*Config) ShowMessage ¶ added in v0.1.2
ShowMessage sets a color for the slog.Record.Message field.
func (*Config) ShowSource ¶ added in v0.1.2
func (cfg *Config) ShowSource(color string, enc Encoder[SourceLine]) *Config
ShowSource sets a color and an encoder for SourceLine encoding. If the enc argument is nil, the configuration uses the SourceAbs function. Configurations must set Config.AddSource to output source annotations.
func (*Config) ShowTag ¶ added in v0.1.2
ShowTag configures tagging values with the given key. If tagged, an Attr's value appears,in the given color, in the "tags" field of the log line.
func (*Config) ShowTagEncode ¶ added in v0.1.2
ShowTag configures tagging values with the given key. If tagged, an Attr appears, in the given color, encoded by the provided Encoder, in the "tags" field of the log line.
func (*Config) ShowTime ¶ added in v0.1.2
ShowTime sets a color and an encoder for the slog.Record.Time field. If the enc argument is nil, the configuration uses the TimeShort function.
func (*Config) TTY ¶
TTY returns a new TTY. If the configured Writer is the same as [StdTTY] (default: os.Stdout), the new TTY shares a mutex with [StdTTY].
func (*Config) Text ¶
Text returns a Logger using a slog.TextHandler for encoding.
Only Config.Writer, [Config.Level], Config.AddSource, and Config.ReplaceFunc configuration is applied.
type Encoder ¶
Encoder writes values of type T to a Buffer containing a TTY log line.
Flavors of Encoder expected by TTY encoding:
- time: Encoder[time.Time]
- level: Encoder[slog.Level]
- message: Encdoer[string]
- tag: Encoder[Attr]
- attr key: Encoder[string]
- attr value: Encoder[Value]
- source: Encoder[SourceLine]
Example ¶
package main import ( "time" "github.com/AndrewHarrisSPU/logf" ) func main() { noTime := func(buf *logf.Buffer, t time.Time) { buf.WriteString("???") } log := logf.New(). ForceTTY(true). AddSource(true). ShowColor(false). ShowLevel(logf.LevelBar). ShowSource("", logf.SourceShort). ShowTime("", logf.EncodeFunc(noTime)). Logger() log.Info("...") }
Output: ▏ ??? ... example_test.go:25
var ( // a minimal Unicode depcition of log level LevelBar Encoder[slog.Level] // bullet point Unicode depiction of log level LevelBullet Encoder[slog.Level] // [slog.Level.String] text LevelText Encoder[slog.Level] // with time format "15:04:05" TimeShort Encoder[time.Time] // with time format "15:04:05" TimeRFC3339Nano Encoder[time.Time] // absolute source file path, plus line number SourceAbs Encoder[SourceLine] // just file:line SourceShort Encoder[SourceLine] // just the package SourcePkg Encoder[SourceLine] )
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
type Logger ¶
Logger embeds a slog.Logger, and offers additional formatting methods:
- Leveled / formatting: Logger.Debugf, Logger.Infof, Logger.Warnf, Logger.Errorf
- Formatting to a string or an error: Logger.Fmt, Logger.WrapErr
- Logger tagging: [Logger.Tag]
The following methods are available on a Logger by way of embedding:
- Leveled logging methods: slog.Logger.Debug, slog.Logger.Info, slog.Logger.Warn, slog.Logger.Error
- General logging methods: slog.Logger.Log, slog.Logger.LogAttrs, slog.Logger.LogDepth, slog.Logger.LogAttrsDepth
- slog.Logger.Handler
The following methds are overriden to return [Logger]s rather than [*slog.Logger]s:
- slog.Logger.Ctx
- slog.Logger.FromContext
- slog.Logger.With
- slog.Logger.WithContext
- slog.Logger.WithGroup
Example (Tag) ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("message", "attrs"). ShowColor(false). ForceTTY(true). Printer() l1 := log.With("#", "Log-9000") l2 := l1.With("#", "Log-9001") l1.Info("Hi!") l2.Info("Plus one!") }
Output: Log-9000 Hi! Log-9001 Plus one!
func FromContext ¶
FromContext employs slog.FromContext to obtain a Logger from the given context. Precisely, the function returns the result of:
UsingHandler(slog.FromContext(ctx).Handler())
func UsingHandler ¶
UsingHandler returns a Logger employing the given slog.Handler
If the given handler is not of a type native to logf, a new Handler is constructed, encapsulating the given handler.
func (Logger) Errorf ¶ added in v0.1.1
Errorf interpolates the msg string and logs at ERROR.
Example ¶
package main import ( "errors" "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() errNegative := errors.New("negative number") log.Error("", errNegative) log = log.With("component", "math") log.Errorf("{component}: square root of {}", errNegative, "n", -1) err := log.WrapErr("{component}: square root of {}", errNegative, "n", -1) log.Error("", err) }
Output: negative number math: square root of -1: negative number math: square root of -1: negative number
func (Logger) With ¶
See slog.Logger.With
Example ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("message", "attrs"). ShowColor(false). ForceTTY(true). Logger() log = log.With("species", "gopher") log.Info("") }
Output: species:gopher
func (Logger) WithContext ¶ added in v0.1.1
func (Logger) WithGroup ¶ added in v0.1.1
Example ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("message", "\t", "attrs"). ShowColor(false). ForceTTY(true). Logger() log = log. WithGroup("outer").With("x", 1). WithGroup("inner").With("x", 2). WithGroup("local") log.Infof("outer {outer.x}", "x", 3) log.Infof("inner {outer.inner.x}", "x", 3) log.Infof("local {outer.inner.local.x}", "x", 3) log.Infof("local {x}", "x", 3) }
Output: outer 1 outer:{x:1 inner:{x:2 local:{x:3}}} inner 2 outer:{x:1 inner:{x:2 local:{x:3}}} local 3 outer:{x:1 inner:{x:2 local:{x:3}}} local 3 outer:{x:1 inner:{x:2 local:{x:3}}}
func (Logger) WrapErr ¶ added in v0.1.1
WrapErr interpolates the f string, and returns an error. If geven a nil error, the resulting error.Error() string is the result of interpolating f. If given a non-nil error, the result includes the given error's string, and matches errors.Is/errors.As behavior, as with fmt.Errorf
Example ¶
package main import ( "errors" "fmt" "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowColor(false). ForceTTY(true). Printer() log = log.With("flavor", "coconut") errInvalidPizza := errors.New("invalid pizza") err := log.WrapErr("{flavor}", errInvalidPizza) fmt.Println("err:", err) if errors.Is(err, errInvalidPizza) { fmt.Println("(matched invalid pizza error)") } }
Output: err: coconut: invalid pizza (matched invalid pizza error)
type SourceLine ¶
SourceLine is the carrier of information for source annotation [Encoder]s. If source annotations aren't configured, File and Line may be "", 0
type Store ¶ added in v0.1.2
type Store struct {
// contains filtered or unexported fields
}
Store implements the `WithAttrs` and `WithGroup` methods of the slog.Handler interface. Additionally, a Store is a slog.LogValuer.
func (Store) Attrs ¶ added in v0.1.2
Attrs traverses attributes in the Store, applying the given function to each visited attribute. The first, []string-valued argument represents a stack of group keys, (same idea as replace functions given to slog.HandlerOptions). The second is an attribute encountered in traversal.
func (Store) ReplaceAttr ¶ added in v0.1.2
ReplaceAttr resembles functionality seen in slog.HandlerOptions. Unlike Store.Attrs, it can be used to mutate attributes held in the store.
type TTY ¶
type TTY struct {
// contains filtered or unexported fields
}
TTY is a component that displays log lines.
A TTY is a slog.Handler, and an io.StringWriter.
On creation, a TTY detects whether it is writing to a terminal. If not, log lines are are written to the writer by a slog.JSONHandler.
Some TTY examples can be run with files in the demo folder:
go run demo/<some demo file>.go
func (*TTY) Filter ¶ added in v0.1.2
Filter sets a filter on TTY output, using the given set of tags.
func (*TTY) Handle ¶
Handle logs the given slog.Record to TTY output.
func (*TTY) LogValue ¶
LogValue returns a slog.Value, of slog.GroupKind. The group of [Attr]s is the collection of attributes present in log lines handled by the TTY.
func (*TTY) Printf ¶ added in v0.1.2
Println formats the given string, and then writes it (with TTY.WriteString)
func (*TTY) WriteString ¶
WriteString satisfies the io.StringWriter interface. It is safe to call Write concurrently with other methods that write TTY output. A trailing newline is appended to the output. If a program detects that a TTY does not write to a terminal device, WriteString is a no-op.
type Value ¶
See slog.Value.
func JSONValue ¶ added in v0.1.2
JSONValue converst a JSON object to a Value. Array values are expanded to attributes with a key string derived from array index (i.e., the 0th element is keyed "0").
Example ¶
package main import ( "github.com/AndrewHarrisSPU/logf" ) func main() { log := logf.New(). ShowLayout("message", "attrs"). ShowColor(false). ForceTTY(true). Logger() object := `{ "vegetables": [ "tomato", "pepper", "green onion" ], "protein":"tofu" }` v, _ := logf.JSONValue(object) recipe := logf.KV("recipe", v) log.Info("", recipe) log.Info(logf.Fmt("{recipe.vegetables.1}", recipe)) }
Output: recipe:{vegetables:{0:tomato 1:pepper 2:green onion} protein:tofu} pepper