Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/img/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/keys.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/prefix.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 15 additions & 2 deletions examples/demo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,30 @@ func main() {
slog.Int("temp", 42),
slog.Duration("time", 2*time.Minute),
)

sleep()

logger.Info(
"Choosing wine pairing",
slog.Any("choices", []string{"merlot", "malbec", "rioja"}),
)

sleep()

logger.Error("No malbec left!")

sleep()
logger.Warn("Falling back to second choice", slog.String("fallback", "rioja"))

logger.Info("Eating steak", slog.String("cut", "sirloin"), slog.Bool("enjoying", true))
logger.Warn(
"Falling back to second choice",
slog.String("fallback", "rioja"),
)

logger.Info(
"Eating steak",
slog.String("cut", "sirloin"),
slog.Bool("enjoying", true),
)
}

func sleep() {
Expand Down
7 changes: 6 additions & 1 deletion examples/keys/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ func main() {
slog.Duration("duration", 30*time.Second),
slog.Int("number", 42),
)

sleep()

sub := logger.With(slog.Bool("sub", true))
sub.Info("Hello from the sub logger", slog.String("subkey", "yes"))

sub.Info(
"Hello from the sub logger",
slog.String("subkey", "yes"),
)
}

func sleep() {
Expand Down
10 changes: 9 additions & 1 deletion examples/prefix/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@ func main() {
logger := log.New(os.Stderr)
prefixed := logger.Prefixed("http")

logger.Info("Calling GitHub API", slog.String("url", "https://api.github.com/"))
logger.Info(
"Calling GitHub API",
slog.String("url", "https://api.github.com/"),
)

sleep()

prefixed.Warn(
"Slow endpoint",
slog.String("endpoint", "users/slow"),
slog.Duration("duration", 10*time.Second),
)

sleep()

prefixed.Info(
"Response from get repos",
slog.Int("status", http.StatusOK),
slog.Duration("duration", 500*time.Millisecond),
)

sleep()

prefixed.Error(
"Response from something else",
slog.Int("status", http.StatusBadRequest),
Expand Down
27 changes: 0 additions & 27 deletions log.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ package log // import "go.followtheprocess.codes/log"

import (
"bytes"
"context"
"io"
"log/slog"
"os"
"slices"
"strconv"
"strings"
Expand All @@ -37,12 +35,6 @@ const (
errorStyle = hue.Red | hue.Bold
)

// ctxKey is the unexported type used for context key so this key never collides with another.
type ctxKey struct{}

// contextKey is the actual key used to store and retrieve a Logger from a Context.
var contextKey = ctxKey{}

// Logger is a command line logger. It is safe to use across concurrently
// executing goroutines.
type Logger struct {
Expand Down Expand Up @@ -78,25 +70,6 @@ func New(w io.Writer, options ...Option) *Logger {
return logger
}

// WithContext stores the given logger in a [context.Context].
//
// The logger may be retrieved from the context with [FromContext].
func WithContext(ctx context.Context, logger *Logger) context.Context {
return context.WithValue(ctx, contextKey, logger)
}

// FromContext returns the [Logger] from a [context.Context].
//
// If the context does not contain a logger, a default logger is returned.
func FromContext(ctx context.Context) *Logger {
logger, ok := ctx.Value(contextKey).(*Logger)
if !ok || logger == nil {
return New(os.Stderr)
}

return logger
}

// With returns a new [Logger] with the given persistent key value pairs.
//
// The returned logger is otherwise an exact clone of the caller.
Expand Down
43 changes: 1 addition & 42 deletions log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func TestVisual(t *testing.T) {
hue.Enabled(true) // Force colour

logger := log.New(os.Stdout, log.WithLevel(log.LevelDebug))

prefixed := logger.Prefixed("cooking")

logger.Debug("Doing some debuggy things")
Expand Down Expand Up @@ -192,7 +191,7 @@ func TestRace(t *testing.T) {
logger := log.New(buf, log.TimeFunc(fixedTime))
sub := logger.Prefixed("sub")

const n = 5
const n = 1000

var wg sync.WaitGroup
wg.Add(n)
Expand Down Expand Up @@ -224,46 +223,6 @@ func TestRace(t *testing.T) {
test.Equal(t, len(lines), n*2, test.Context("expected %d log lines", n*2))
}

func TestContext(t *testing.T) {
t.Run("present", func(t *testing.T) {
buf := &bytes.Buffer{}

// Constantly return the same time
fixedTime := func() time.Time {
fixed, err := time.Parse(time.RFC3339, "2025-04-01T13:34:03Z")
test.Ok(t, err)

return fixed
}

// Configure it a bit so we know we're getting the right one
logger := log.New(buf, log.TimeFunc(fixedTime), log.TimeFormat(time.Kitchen))

logger.Info("Before")

ctx := t.Context()

ctx = log.WithContext(ctx, logger)

after := log.FromContext(ctx)

after.Info("After")

got := buf.String()

test.Diff(t, got, "1:34PM INFO: Before\n1:34PM INFO: After\n")
})

t.Run("missing", func(t *testing.T) {
_, stderr := test.CaptureOutput(t, func() error {
log.FromContext(t.Context()).Info("FromContext")
return nil
})

test.True(t, strings.Contains(stderr, "FromContext"))
})
}

func BenchmarkLogger(b *testing.B) {
hue.Enabled(true) // Force colour

Expand Down
Loading