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.Msg("Hello, Roswell")
}
Interpolation ¶
Generating output similar to the earlier Hello, world progam:
log = log.With("place", "Roswell")
log.Msgf("Hello, {place}")
Generating a wrapped error with the error string "Roswell: 🛸 spotted":
err := log.NewErr("{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.
As an alternative or an auxilliary mode to key:value pairs, a TTY also prints tags.
Tags ¶
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, given a slog.Handler:
log := logf.UsingHandler(h)
Construct a Logger, given a context.Context
log := logf.FromContext(ctx)
Passing a TTY:
tty := logf.New().TTY() slog.New(tty)
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 ¶
Example (FormattingVerbs) ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
log.Msgf("{left-pad:%010d}", "left-pad", 1)
log.Msgf("pi is about {pi:%6.5f}", "pi", 355.0/113)
}
Output: 0000000001 pi is about 3.14159
Example (InerpolationTimeVerbs) ¶
Interpolation of time values in message strings. This is distinct from how [Config.TimeFormat], which affects TTY time fields.
package main
import (
"time"
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
log.Msgf("time interpolation formatting:")
log.Msgf("no verb {}", time.Time{})
log.Msgf("RFC3339 {:RFC3339}", time.Time{})
log.Msgf("kitchen {:kitchen}", time.Time{})
log.Msgf("timestamp {:stamp}", time.Time{})
log.Msgf("epoch {:epoch}", time.Time{})
// custom formatting uses strings like time.Layout, using a semicolon rather than ':'
log.Msgf("custom {:15;03;04}", time.Time{})
log.Msgf("duration interpolation formatting:")
d := time.Unix(1000, 0).Sub(time.Unix(1, 0))
log.Msgf("no verb {}", d)
log.Msgf("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 (InterpolationArguments) ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
// Unkeyed `{}` symbols draw one argument each from a logging call:
log.Msgf("The {} {} {} ...",
"quick",
"brown",
"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.Msgf("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()
// Because only 3.14 is used for unkeyed interpolation,
// "greek" and "π" parse to an attribute
log.Msgf("{greek}: {}", 3.14, "greek", "π")
}
Output: π: 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.Msgf(`:-}`)
// Also surreal: escaping into JSON
log.Msgf(`\{"{key}":"{value}"\}`, "key", "color", "value", "mauve")
// A single colon is parsed as a separator between an interpolation key and a formatting verb
log.Msgf(`{:}`, "plaintext")
// Escaping a common lisp keyword symbol
log.Msgf(`{\:keyword}`, ":keyword", "lisp")
// \Slashes, "quotes", and `backticks`
log.Msgf("{\\\\}", `\`, `slash`)
log.Msgf(`{\\}`, `\`, `slash`)
}
Output: :-} {"color":"mauve"} plaintext lisp slash slash
Example (InterpolatonLogValuer) ¶
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.Msgf("{vmap.first}", "vmap", vmap)
log.Msgf("{vmap.second}", "vmap", vmap)
// VERY SUBTLE:
// this won't work, becuase vmap is not associated with "vmap"
log.Msgf("{vmap.third}", vmap)
// this works
log.Msgf("{third}", vmap)
}
Output: 1 [{} {}] !missing-attr Hello, world
Example (Structure) ¶
Building attributes is essential to capturing structure. Mostly to avoid needing to import slog, but also to offer a few tweaked behaviors, logf repackages Attr constructors.
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
Logger()
// 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.Msgf("The Truth Is Out There")
// A Logger is a LogValuer, and the value is a slog.Group
print := logf.New().
Colors(false).
ForceTTY().
Printer()
print.Msgf("{}", log)
}
Output: The Truth Is Out There agent:{files:X title:Special Agent name:Fox Mulder} [agent=[files=X title=Special Agent name=Fox Mulder]]
Example (StructureErrors) ¶
With a logf.Logger and interpolation, there are a variety of ways to handle an error
package main
import (
"errors"
"fmt"
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
Logger()
log = log.Group("emails").With("user", "Strong Bad", "id", "12345")
err := errors.New("the system is down")
// i. logging the error
log.Err("", err)
// ii. wrapping the error, with no msg -> the error
err2 := log.NewErr("", err)
fmt.Println(err2.Error())
// iii. wrapping the error, with interpolated context
err3 := log.NewErr("{emails.user}", err)
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 := log.NewErr("{}", err, log)
fmt.Println(err4.Error())
}
Output: the system is down emails:{user:Strong Bad id:12345} the system is down Strong Bad: the system is down [emails.user=Strong Bad emails.id=12345]: the system is down
Index ¶
- Constants
- Variables
- 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) Depth(depth int) Logger
- func (l Logger) Err(msg string, err error, args ...any)
- func (l Logger) Fmt(msg string, args ...any) string
- func (l Logger) Group(name string) Logger
- func (l Logger) Handler() slog.Handler
- func (l Logger) Level(level slog.Level) Logger
- func (l Logger) LogValue() slog.Value
- func (l Logger) Msg(msg string, args ...any)
- func (l Logger) Msgf(msg string, args ...any)
- func (l Logger) NewErr(msg string, err error, args ...any) error
- func (l Logger) Tag(tag string) Logger
- func (l Logger) With(args ...any) Logger
- 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 (FormattingVerbs)
- Package (InerpolationTimeVerbs)
- Package (InterpolationArguments)
- Package (InterpolationArgumentsMixed)
- Package (InterpolationEscapes)
- Package (InterpolatonLogValuer)
- Package (Structure)
- Package (StructureErrors)
- Config.Layout
- Encoder
- Logger.Err
- Logger.Fmt
- Logger.Group
- Logger.Level
- Logger.Msgf
- Logger.NewErr
- Logger.Tag
- Logger.With
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 ¶
This section is empty.
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.
func Group ¶
Group constructs a composite Attr from a name and a list of Attrs. See slog.Group.
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 `logf` handler and logger configuration.
To construct a Logger with an already extant slog.Handler, see UsingHandler.
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.Level: INFO
- 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.AttrKey: "dim cyan"
- Config.AttrValue: "cyan"
- Config.ForceTTY: false
- Config.Group: "dim"
- Config.Level: LevelBar
- Config.LevelColors: "bright cyan", "bright green", "bright yellow", "bright red"
- Config.Message: ""
- Config.Source: "dim", SourceAbs
- Config.Tag: "#", "bright magenta"
- [Config.TagEncoce]: nil
- Config.Time: "dim", TimeShort
- Config.Colors: true
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"
- "attrs"
- "tags"
- "source"
Spacing:
- "\n"
- " "
- "\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().
Colors(false).
Layout("attrs", "message").
ForceTTY().
Logger()
log.Msg("Hello!", "left", "here")
}
Output: left:here Hello!
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.Msg("...")
}
Output: ▕▎ ??? ... example_test.go:421
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) Handle ¶
Handle performs interpolation on a slog.Record message. The record is then handled by an encapsulated slog.Handler.
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 ¶
type Logger struct {
// contains filtered or unexported fields
}
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) Err ¶
Err logs a message, appending the error string to the message text. Interpolation is performed on the given message string.
Example ¶
package main
import (
"errors"
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
errNegative := errors.New("negative number")
log.Err("", errNegative)
log = log.With("component", "math")
err := log.NewErr("{component}: square root of {}", errNegative, -1)
log.Err("", err)
}
Output: negative number math: square root of -1: negative number
func (Logger) Fmt ¶
Fmt applies interpolation to the given message string. The resulting string is returned, rather than logged.
Example ¶
package main
import (
"fmt"
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
log = log.With("flavor", "coconut")
msg := log.Fmt("{flavor} pie", nil)
fmt.Println("msg:", msg)
}
Output: msg: coconut pie
func (Logger) Group ¶
Group calls slog.Logger.WithGroup on a Logger's handler.
Example ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
Layout("message", "\t", "attrs").
ForceTTY().
Logger()
log = log.Group("outer").With("x", 1).
Group("inner").With("x", 2).
Group("local")
log.Msgf("outer {outer.x}", "x", 3)
log.Msgf("inner {outer.inner.x}", "x", 3)
log.Msgf("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) Level ¶
Level returns a logger emitting at the given level.
Example ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
Ref(logf.INFO). // the reference level (receiver type is *Config)
ForceTTY().
Printer().
Level(logf.INFO) // the logger level (receiver type is Logger)
// not visible, because logger level is less than reference level
log.Level(logf.DEBUG).Msg("i'm hiding")
// visible, because the receiver of the previous call was a new Logger created by log.Level
log.Msg("back to INFO level")
// not visible, because the Logger returned by log.Level is assigned to log
log = log.Level(logf.DEBUG)
log.Msg("now i'm invisible")
}
Output: back to INFO level
func (Logger) Msgf ¶
Msgf performs interpolation on the given message string, and logs.
Example ¶
package main
import (
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
log = log.With("aliens", "Kang and Kodos, the Conquerors of Rigel VII")
log.Msg("Hello, world")
log.Msgf("{}", "Hello, world")
log.Msgf("With menace, {aliens} uttered \"{}\"", "Hello, world")
}
Output: Hello, world Hello, world With menace, Kang and Kodos, the Conquerors of Rigel VII uttered "Hello, world"
func (Logger) NewErr ¶
NewErr applies interpolation and formatting to wrap an error. If the given err is nil, a new error is constructed. The resulting error is returned, rather than logged.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/AndrewHarrisSPU/logf"
)
func main() {
log := logf.New().
Colors(false).
ForceTTY().
Printer()
log = log.With("flavor", "coconut")
errInvalidPizza := errors.New("invalid pizza")
err := log.NewErr("{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)
func (Logger) Tag ¶
Tag configures a tag that appears in TTY log output. Tags set by this method override; only one is set per logger.
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.Msg("Hi!")
l2.Msg("Plus one!")
}
Output: Log-9000 Hi! Log-9001 Plus one!
func (Logger) With ¶
With appends attributes held in a Logger's handler. Arguments are converted to attributes with Attrs.
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.Msg("")
}
Output: species:gopher
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.