logf

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2022 License: BSD-3-Clause Imports: 14 Imported by: 0

README

logf

TODO:

  • interpolation of groups: shouldn't need to .LogValue() ...

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
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.

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

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

This section is empty.

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

Group constructs a composite Attr from a name and a list of Attrs. 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 `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:

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

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 (*Config) AddSource

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

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

func (*Config) AttrKey

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

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

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

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

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

Colors toggles TTY color encoding, using ANSI escape codes.

TODO: support cygwin escape codes.

func (*Config) ForceTTY

func (cfg *Config) ForceTTY() *Config

func (*Config) Group

func (cfg *Config) Group(color string, open Encoder[struct{}], close Encoder[int]) *Config

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

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

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

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

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

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

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

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

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

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

Message sets a color for the slog.Record.Message field.

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.Leveler) *Config

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

func (*Config) ReplaceFunc

func (cfg *Config) ReplaceFunc(replace func(a Attr) Attr) *Config

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

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

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

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

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

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

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

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

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.

func (*Config) Writer

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

Writer configures the eventual destination of log lines.

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

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
}

Handler encapsulates a slog.Handler and maintains additional state required for message interpolation.

func (*Handler) Enabled

func (h *Handler) Enabled(level Level) bool

See slog.Handler.Enabled.

func (*Handler) Handle

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

Handle performs interpolation on a slog.Record message. The record is then handled by an encapsulated slog.Handler.

func (*Handler) LogValue

func (h *Handler) LogValue() 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 Handler.

func (*Handler) SlogHandler

func (h *Handler) SlogHandler() slog.Handler

SlogHandler returns the slog.Handler encapsulated by a Handler

func (*Handler) WithAttrs

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

See slog.Handler.WithAttrs.

func (*Handler) WithGroup

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

See slog.Handler.WithGroup.

type Level

type Level = slog.Level

See slog.Level

type Logger

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

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

func (l Logger) Depth(depth int) Logger

Depth is used to modulate source file/line retrieval.

func (Logger) Err

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

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

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

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

func (l Logger) Group(name string) Logger

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

func (l Logger) Handler() slog.Handler

Handler returns a handler associated with the Logger.

func (Logger) Level

func (l Logger) Level(level slog.Level) Logger

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

func (l Logger) LogValue() slog.Value

LogValue returns the set of [Attr]s accrued by the Logger's handler.

func (Logger) Msg

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

Msg logs the given message string. No interpolation is performed.

func (Logger) Msgf

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

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

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

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

func (l Logger) Tag(tag string) Logger

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

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

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

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

func (tty *TTY) Handle(r slog.Record) 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) 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 string. 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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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