Documentation
¶
Overview ¶
Package logf is a logging package extending slog.
Often, a structured logging API embeds the expectation that it isn't logging for human eyes. This is unambiguously a good idea in a lot of situations. Automated processing of log lines is powerful.
Still, logf extends slog in the other direction, to be nice for human readers. It's an interesting problem to work out what the trade-offs are, and how to provide an opt-in API that allows for varying kinds of functionality here without demanding them or paying for them at runtime.
Work in progress!
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().
Colors(false).
ForceTTY().
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().
Colors(false).
ForceTTY().
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().
Colors(false).
ForceTTY().
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().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
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().
Colors(false).
ForceTTY().
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().
Colors(false).
ForceTTY().
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().
Colors(false).
ForceTTY().
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.Layout, 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().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
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) AttrKey(color string, enc Encoder[string]) *Config
- func (cfg *Config) AttrValue(color string, enc Encoder[Value]) *Config
- func (cfg *Config) Colors(toggle bool) *Config
- func (cfg *Config) ForceTTY() *Config
- func (cfg *Config) Group(color string, open Encoder[struct{}], close Encoder[int]) *Config
- func (cfg *Config) JSON() Logger
- func (cfg *Config) Layout(fields ...string) *Config
- func (cfg *Config) Level(enc Encoder[slog.Level]) *Config
- func (cfg *Config) LevelColors(debug string, info string, warn string, error string) *Config
- func (cfg *Config) Logger() Logger
- func (cfg *Config) Message(color string) *Config
- func (cfg *Config) Printer() Logger
- func (cfg *Config) Ref(level slog.Leveler) *Config
- func (cfg *Config) ReplaceFunc(replace func(a Attr) Attr) *Config
- func (cfg *Config) Source(color string, enc Encoder[SourceLine]) *Config
- func (cfg *Config) TTY() *TTY
- func (cfg *Config) Tag(key string, color string) *Config
- func (cfg *Config) TagEncode(key string, color string, enc Encoder[Attr]) *Config
- func (cfg *Config) Text() Logger
- func (cfg *Config) Time(color string, enc Encoder[time.Time]) *Config
- 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) Tag(name string) Logger
- 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 TTY
- func (tty *TTY) Enabled(level slog.Level) bool
- func (tty *TTY) Handle(r slog.Record) error
- func (tty *TTY) LogValue() slog.Value
- func (tty *TTY) Logger() Logger
- 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.Layout
- Encoder
- Fmt
- Logger.Errorf
- Logger.Tag
- 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().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
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)
// 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())
// iv. wrapping the error, with all available structure
// - log's type is logf.Logger
// - a logf.Logger is also a slog.LogValuer
// - "{}" consumes log's LogValue
err4 := logf.WrapErr("{}", err, log)
fmt.Println(err4.Error())
}
Output: the system is down emails:{user:Strong Bad id:12345 err:the system is down} the system is down Strong Bad: the system is down Strong Bad: the system is down [emails.user=Strong Bad emails.id=12345]: 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.
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.Layout: "level", "time", "tags", "message", "\t", "attrs"
- Config.Colors: true
- Config.ForceTTY: false
Methods configured TTY encoding:
- Config.Level: LevelBar
- Config.LevelColors: "bright cyan", "bright green", "bright yellow", "bright red"
- Config.Time: "dim", TimeShort
- Config.Tag: "#", "bright magenta"
- Config.TagEncode: nil
- Config.Message: ""
- Config.AttrKey: "dim cyan"
- Config.AttrValue: "cyan"
- Config.Group: "dim"
- Config.Source: "dim", SourceAbs
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 (*Config) AddSource ¶
AddSource configures the inclusion of source file and line information in log lines.
func (*Config) AttrKey ¶
AttrKey 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) AttrValue ¶
AttrValue 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) Colors ¶
Colors toggles TTY color encoding, using ANSI escape codes.
TODO: support cygwin escape codes.
func (*Config) Group ¶
Group 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) 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) Layout ¶
Layout configures the fields encoded in a TTY log line.
Layout 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().
ForceTTY().
Layout("level", "attrs", "message", "tag", "\n", "source").
Level(logf.LevelBar).
Source("", logf.SourcePkg).
AddSource(true).
Colors(false).
Logger().
Tag("rightTag")
log.Info("Hello!", "leftAttr", "here")
}
Output: ▕▎ leftAttr:here Hello! rightTag logf
func (*Config) Level ¶
Level sets an encoder for the slog.Record.Level field. If the enc argument is nil, the configuration uses the LevelBar function.
func (*Config) LevelColors ¶
LevelColors configures four colors for DEBUG, INFO, WARN, and ERROR levels. These colors are used when a slog.Record.Level is encoded.
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) Message ¶
Message sets a color for the slog.Record.Message field.
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.Leveler.
func (*Config) ReplaceFunc ¶
ReplaceAttr configures the use of the given function to replace Attrs when logging. See slog.HandlerOptions.
func (*Config) Source ¶
func (cfg *Config) Source(color string, enc Encoder[SourceLine]) *Config
Source 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) 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) Tag ¶
Tag 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) TagEncode ¶
Tag 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) Text ¶
Text returns a Logger using a slog.TextHandler for encoding.
Only Config.Writer, Config.Level, Config.AddSource, and Config.ReplaceFunc configuration is applied.
func (*Config) Time ¶
Time sets a color and an encoder for the slog.Record.Time field. If the enc argument is nil, the configuration uses the TimeShort function.
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().
Colors(false).
ForceTTY().
Level(logf.LevelBar).
Source("", logf.SourceShort).
AddSource(true).
Time("", logf.EncodeFunc(noTime)).
Logger()
log.Info("...")
}
Output: ▕▎ ??? ... example_test.go:25
var ( // a minimal Unicode depcition of log level LevelBar Encoder[slog.Level] // [slog.Level.String] text LevelText Encoder[slog.Level] // with time format "15:04:05" TimeShort 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
}
Handler encapsulates a slog.Handler and maintains additional state required for message interpolation.
func (*Handler) 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 Handler.
func (*Handler) SlogHandler ¶
SlogHandler returns the slog.Handler encapsulated by a Handler
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
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().
Colors(false).
ForceTTY().
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) Tag ¶
Tag sets a unique tag on the returned Logger. Unlike attributes set with Logger.With, only one tag set by [Tag] appears in output.
Example ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
l1 := log.Tag("Log-9000")
l2 := l1.Tag("Log-9001")
l1.Info("Hi!")
l2.Info("Plus one!")
}
Output: Log-9000 Hi! Log-9001 Plus one!
func (Logger) With ¶
See slog.Logger.With
Example ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
Layout("message", "attrs").
ForceTTY().
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().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
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)
}
Output: outer 1 outer:{x:1 inner: {x:2 x:3}}} inner 2 outer:{x:1 inner: {x:2 x:3}}} local 3 outer:{x:1 inner: {x:2 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().
ForceTTY().
Colors(false).
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 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) 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) 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 string. If a program detects that a TTY does not write to a terminal device, WriteString is a no-op.
type Value ¶
See slog.Value.