1package log
2
3import (
4 "context"
5 "log/slog"
6 "os"
7
8 "github.com/charmbracelet/log"
9)
10
11func NewHandler(name string) slog.Handler {
12 return log.NewWithOptions(os.Stderr, log.Options{
13 ReportTimestamp: true,
14 Prefix: name,
15 Level: log.DebugLevel,
16 })
17}
18
19func New(name string) *slog.Logger {
20 return slog.New(NewHandler(name))
21}
22
23func NewContext(ctx context.Context, name string) context.Context {
24 return IntoContext(ctx, New(name))
25}
26
27type ctxKey struct{}
28
29// IntoContext adds a logger to a context. Use FromContext to
30// pull the logger out.
31func IntoContext(ctx context.Context, l *slog.Logger) context.Context {
32 return context.WithValue(ctx, ctxKey{}, l)
33}
34
35// FromContext returns a logger from a context.Context;
36// if the passed context is nil, we return the default slog
37// logger.
38func FromContext(ctx context.Context) *slog.Logger {
39 if ctx != nil {
40 v := ctx.Value(ctxKey{})
41 if v == nil {
42 return slog.Default()
43 }
44 return v.(*slog.Logger)
45 }
46
47 return slog.Default()
48}
49
50// sublogger derives a new logger from an existing one by appending a suffix to its prefix.
51func SubLogger(base *slog.Logger, suffix string) *slog.Logger {
52 // try to get the underlying charmbracelet logger
53 if cl, ok := base.Handler().(*log.Logger); ok {
54 prefix := cl.GetPrefix()
55 if prefix != "" {
56 prefix = prefix + "/" + suffix
57 } else {
58 prefix = suffix
59 }
60 return slog.New(NewHandler(prefix))
61 }
62
63 // Fallback: no known handler type
64 return slog.New(NewHandler(suffix))
65}