@@ -10,8 +10,10 @@ package log
1010
1111import (
1212 "bytes"
13+ "context"
1314 "fmt"
1415 "io"
16+ "os"
1517 "slices"
1618 "strconv"
1719 "sync"
@@ -34,8 +36,15 @@ const (
3436 errorStyle = hue .Red | hue .Bold
3537)
3638
39+ // missingValue is the placeholder text for a missing value in a log line's key value pair.
3740const missingValue = "<MISSING>"
3841
42+ // ctxKey is the unexported type used for context key so this key never collides with another.
43+ type ctxKey struct {}
44+
45+ // contextKey is the actual key used to store and retrieve a Logger from a Context.
46+ var contextKey = ctxKey {}
47+
3948// Logger is a command line logger. It is safe to use across concurrently
4049// executing goroutines.
4150type Logger struct {
@@ -58,7 +67,7 @@ func New(w io.Writer, options ...Option) *Logger {
5867 w : w ,
5968 level : LevelInfo ,
6069 timeFormat : time .RFC3339 ,
61- timeFunc : now ,
70+ timeFunc : func () time. Time { return time . Now (). UTC () } ,
6271 mu : & sync.Mutex {},
6372 }
6473
@@ -71,6 +80,25 @@ func New(w io.Writer, options ...Option) *Logger {
7180 return logger
7281}
7382
83+ // WithContext stores the given logger in a [context.Context].
84+ //
85+ // The logger may be retrieved from the context with [FromContext].
86+ func WithContext (ctx context.Context , logger * Logger ) context.Context {
87+ return context .WithValue (ctx , contextKey , logger )
88+ }
89+
90+ // FromContext returns the [Logger] from a [context.Context].
91+ //
92+ // If the context does not contain a logger, a default logger is returned.
93+ func FromContext (ctx context.Context ) * Logger {
94+ logger , ok := ctx .Value (contextKey ).(* Logger )
95+ if ! ok || logger == nil {
96+ return New (os .Stderr )
97+ }
98+
99+ return logger
100+ }
101+
74102// With returns a new [Logger] with the given persistent key value pairs.
75103//
76104// The returned logger is otherwise an exact clone of the caller.
@@ -220,11 +248,6 @@ func putBuffer(buf *bytes.Buffer) {
220248 bufPool .Put (buf )
221249}
222250
223- // now returns the current time with UTC.
224- func now () time.Time {
225- return time .Now ().UTC ()
226- }
227-
228251// needsQuotes returns whether s should be displayed as "s".
229252func needsQuotes (s string ) bool {
230253 for _ , char := range s {
0 commit comments