logf

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 12, 2023 License: BSD-3-Clause Imports: 16 Imported by: 0

README

logf

Go Reference

Current status

Core functionality seems complete.

API is still being explored. Particularly:

  • Some nuances with expanding namespacing, Groups, and LogValuers are subtle - the behavior needs to feel right, is evolving.
  • Context and slog integration should be straightforward but hasn't been expiremented with much.

What's where

file stuff
alias.go aliases to slog stuff, as well as borrowed std lib code
attrs.go procuring and munging attrs
config.go configuration, from New
encoder.go TTY encoding logic
fmt.go package-level formatting functions
handler.go Handler
interpolate.go splicer interpolation routines
logger.go Logger
splicer.go splicer lifecycle and writing routines
styles.go TTY styling gadgets
tty.go the TTY device
demo go run-able TTY demos
testlog testing gadgets

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

Examples

Constants

View Source
const (
	DEBUG = slog.DebugLevel
	INFO  = slog.InfoLevel
	WARN  = slog.WarnLevel
	ERROR = slog.ErrorLevel
)

Variables

StdRef is a global slog.LevelVar used in default-ish configurations.

Functions

func Fmt added in v0.1.1

func Fmt(f string, args ...any) string

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

func WrapErr(f string, err error, args ...any) error

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

type Attr = slog.Attr

See slog.Attr.

func Attrs

func Attrs(args ...any) (as []Attr)

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

func Group(name string, as ...Attr) Attr

See slog.Group.

func KV

func KV(key string, value any) Attr

KV constructs an Attr from a key string and a value. See slog.Any.

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) Write

func (s Buffer) Write(p []byte) (int, error)

func (Buffer) WriteByte

func (s Buffer) WriteByte(c byte) error

func (Buffer) WriteString

func (s Buffer) WriteString(m string) (int, error)

func (Buffer) WriteValue

func (s Buffer) WriteValue(v slog.Value, verb []byte)

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:

Methods applying only to a TTY, or a logger based on one, and default arguments:

Methods configuring the color and encoding of TTY fields:

3. A Config method returning a Logger or a TTY closes the chained invocation:

func New

func New() *Config

New opens a Config with default values.

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

func (cfg *Config) AddSource(toggle bool) *Config

AddSource configures the inclusion of source file and line information in log lines.

func (*Config) Aux added in v0.1.2

func (cfg *Config) Aux(aux slog.Handler) *Config

Aux configures an auxilliary handler for a TTY. The auxilliary handler is employed:

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

func (cfg *Config) ForceAux(toggle bool) *Config

ForceAux configures any TTY produced by the configuraton to always employ an auxilliary handler.

func (*Config) ForceTTY

func (cfg *Config) ForceTTY(toggle bool) *Config

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

func (cfg *Config) JSON() Logger

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

func (cfg *Config) Logger() Logger

If the configured Writer is a terminal, the returned *Logger is TTY-based Otherwise, the returned *Logger a JSONHandler]-based

func (*Config) Printer

func (cfg *Config) Printer() Logger

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

func (cfg *Config) Ref(level *slog.LevelVar) *Config

Ref configures the use of the given reference slog.LevelVar.

func (*Config) ReplaceFunc

func (cfg *Config) ReplaceFunc(replace func(scope []string, a Attr) Attr) *Config

ReplaceAttr configures the use of the given function to replace Attrs when logging. See slog.HandlerOptions.

func (*Config) ShowAttrKey added in v0.1.2

func (cfg *Config) ShowAttrKey(color string, enc Encoder[string]) *Config

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

func (cfg *Config) ShowAttrValue(color string, enc Encoder[Value]) *Config

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

func (cfg *Config) ShowColor(toggle bool) *Config

Colors toggles TTY color encoding, using ANSI escape codes.

TODO: support cygwin escape codes.

func (*Config) ShowGroup added in v0.1.2

func (cfg *Config) ShowGroup(color string, open Encoder[int], close Encoder[int]) *Config

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

func (cfg *Config) ShowLayout(fields ...string) *Config

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

func (cfg *Config) ShowLevel(enc Encoder[slog.Level]) *Config

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

func (cfg *Config) ShowLevelColors(debug string, info string, warn string, error string) *Config

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

func (cfg *Config) ShowMessage(color string) *Config

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

func (cfg *Config) ShowTag(key string, color string) *Config

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

func (cfg *Config) ShowTagEncode(key string, color string, enc Encoder[Attr]) *Config

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

func (cfg *Config) ShowTime(color string, enc Encoder[time.Time]) *Config

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

func (cfg *Config) TTY() *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

func (cfg *Config) Text() Logger

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) Writer

func (cfg *Config) Writer(w io.Writer) *Config

Writer configures the eventual destination of log lines. Configuring a new writer creates a new mutex guarding it.

type Encoder

type Encoder[T any] interface {
	Encode(*Buffer, T)
}

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]
)

func EncodeFunc

func EncodeFunc[T any](fn func(*Buffer, T)) Encoder[T]

EncodeFunc returns a T-flavored Encoder from a compatible function.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

func (*Handler) Enabled

func (h *Handler) Enabled(l slog.Level) bool

func (*Handler) Handle

func (h *Handler) Handle(r slog.Record) error

func (*Handler) LogValue

func (h *Handler) LogValue() Value

iterates out through stored handlerFrames, LIFO

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(as []Attr) slog.Handler

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

type Level

type Level = slog.Level

See slog.Level

type Logger

type Logger struct {
	*slog.Logger
}

Logger embeds a slog.Logger, and offers additional formatting methods:

The following methods are available on a Logger by way of embedding:

The following methds are overriden to return [Logger]s rather than [*slog.Logger]s:

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 Ctx added in v0.1.1

func Ctx(ctx context.Context) Logger

Ctx returns FromContext(ctx).WithContext(ctx), with logf flavors.

func FromContext

func FromContext(ctx context.Context) Logger

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

func UsingHandler(h slog.Handler) Logger

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) Debugf added in v0.1.1

func (l Logger) Debugf(msg string, args ...any)

Debugf interpolates the msg string and logs at DEBUG.

func (Logger) Errorf added in v0.1.1

func (l Logger) Errorf(msg string, err error, args ...any)

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) Fmt

func (l Logger) Fmt(f string, args ...any) string

Fmt interpolates the f string and returns the result.

func (Logger) Infof added in v0.1.1

func (l Logger) Infof(msg string, args ...any)

Infof interpolates the msg string and logs at INFO.

func (Logger) Warnf added in v0.1.1

func (l Logger) Warnf(msg string, args ...any)

Warnf interpolates the msg string and logs at WARN.

func (Logger) With

func (l Logger) With(args ...any) Logger

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 (l Logger) WithContext(ctx context.Context) Logger

See slog.Logger.WithContext

func (Logger) WithGroup added in v0.1.1

func (l Logger) WithGroup(name string) Logger

See slog.Logger.WithGroup

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

func (l Logger) WrapErr(f string, err error, args ...any) error

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

type SourceLine struct {
	File string
	Line int
}

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

func (store Store) Attrs(f func([]string, Attr))

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) LogValue added in v0.1.2

func (store Store) LogValue() Value

LogValue returns a Value

func (Store) ReplaceAttr added in v0.1.2

func (store Store) ReplaceAttr(f func([]string, Attr) Attr)

ReplaceAttr resembles functionality seen in slog.HandlerOptions. Unlike Store.Attrs, it can be used to mutate attributes held in the store.

func (Store) WithAttrs added in v0.1.2

func (store Store) WithAttrs(as []Attr) Store

WithAttrs commits attributes to the Store.

func (Store) WithGroup added in v0.1.2

func (store Store) WithGroup(name string) Store

WithGroup opens a new group 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) Enabled

func (tty *TTY) Enabled(level slog.Level) bool

Enabled reports whether the TTY is enabled for logging at the given level.

func (*TTY) Filter added in v0.1.2

func (tty *TTY) Filter(tags ...string)

Filter sets a filter on TTY output, using the given set of tags.

func (*TTY) Handle

func (tty *TTY) Handle(r slog.Record) (auxErr error)

Handle logs the given slog.Record to TTY output.

func (*TTY) LogValue

func (tty *TTY) LogValue() slog.Value

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) Logger

func (tty *TTY) Logger() Logger

Logger returns a Logger that uses the TTY as a handler.

func (*TTY) Printf added in v0.1.2

func (tty *TTY) Printf(f string, args ...any)

Println formats the given string, and then writes it (with TTY.WriteString)

func (*TTY) SetRef added in v0.1.2

func (tty *TTY) SetRef(level slog.Level)

func (*TTY) WithAttrs

func (tty *TTY) WithAttrs(as []Attr) slog.Handler

See slog.WithAttrs.

func (*TTY) WithGroup

func (tty *TTY) WithGroup(name string) slog.Handler

See slog.Handler.WithGroup.

func (*TTY) WriteString

func (tty *TTY) WriteString(s string) (n int, err error)

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

type Value = slog.Value

See slog.Value.

func GroupValue

func GroupValue(as ...Attr) Value

See slog.GroupValue

func JSONValue added in v0.1.2

func JSONValue(object string) (Value, error)

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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL