Skip to content

Commit b256bbe

Browse files
chressiestapelberg
authored andcommittedNov 22, 2023·
glog: add context variants to most log functions
We export this new API to make the internal and external versions identical. The context is currently plumbed through to the internal/logsink package, but effectively discarded there. cl/560684897 (google-internal) cl/579771826 (google-internal)
1 parent bcf3184 commit b256bbe

File tree

4 files changed

+376
-70
lines changed

4 files changed

+376
-70
lines changed
 

‎glog.go

+193-8
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,26 @@
1515
// limitations under the License.
1616

1717
// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
18-
// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
19-
// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
18+
// It provides functions that have a name matched by regex:
19+
//
20+
// (Info|Warning|Error|Fatal)(Context)?(Depth)?(f)?
21+
//
22+
// If Context is present, function takes context.Context argument. The
23+
// context is used to pass through the Trace Context to log sinks that can make use
24+
// of it.
25+
// It is recommended to use the context variant of the functions over the non-context
26+
// variants if a context is available to make sure the Trace Contexts are present
27+
// in logs.
28+
//
29+
// If Depth is present, this function calls log from a different depth in the call stack.
30+
// This enables a callee to emit logs that use the callsite information of its caller
31+
// or any other callers in the stack. When depth == 0, the original callee's line
32+
// information is emitted. When depth > 0, depth frames are skipped in the call stack
33+
// and the final frame is treated like the original callee to Info.
34+
//
35+
// If 'f' is present, function formats according to a format specifier.
36+
//
37+
// This package also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
2038
//
2139
// Basic examples:
2240
//
@@ -82,6 +100,7 @@ package glog
82100

83101
import (
84102
"bytes"
103+
"context"
85104
"errors"
86105
"fmt"
87106
stdLog "log"
@@ -182,9 +201,14 @@ func appendBacktrace(depth int, format string, args []any) (string, []any) {
182201
return format, args
183202
}
184203

185-
// logf writes a log message for a log function call (or log function wrapper)
186-
// at the given depth in the current goroutine's stack.
204+
// logf acts as ctxlogf, but doesn't expect a context.
187205
func logf(depth int, severity logsink.Severity, verbose bool, stack stack, format string, args ...any) {
206+
ctxlogf(nil, depth+1, severity, verbose, stack, format, args...)
207+
}
208+
209+
// ctxlogf writes a log message for a log function call (or log function wrapper)
210+
// at the given depth in the current goroutine's stack.
211+
func ctxlogf(ctx context.Context, depth int, severity logsink.Severity, verbose bool, stack stack, format string, args ...any) {
188212
now := timeNow()
189213
_, file, line, ok := runtime.Caller(depth + 1)
190214
if !ok {
@@ -198,6 +222,7 @@ func logf(depth int, severity logsink.Severity, verbose bool, stack stack, forma
198222

199223
metai, meta := metaPoolGet()
200224
*meta = logsink.Meta{
225+
Context: ctx,
201226
Time: now,
202227
File: file,
203228
Line: line,
@@ -207,6 +232,9 @@ func logf(depth int, severity logsink.Severity, verbose bool, stack stack, forma
207232
Thread: int64(pid),
208233
}
209234
sinkf(meta, format, args...)
235+
// Clear pointer fields so they can be garbage collected early.
236+
meta.Context = nil
237+
meta.Stack = nil
210238
metaPool.Put(metai)
211239
}
212240

@@ -418,6 +446,36 @@ func (v Verbose) Infof(format string, args ...any) {
418446
}
419447
}
420448

449+
// InfoContext is equivalent to the global InfoContext function, guarded by the value of v.
450+
// See the documentation of V for usage.
451+
func (v Verbose) InfoContext(ctx context.Context, args ...any) {
452+
v.InfoContextDepth(ctx, 1, args...)
453+
}
454+
455+
// InfoContextf is equivalent to the global InfoContextf function, guarded by the value of v.
456+
// See the documentation of V for usage.
457+
func (v Verbose) InfoContextf(ctx context.Context, format string, args ...any) {
458+
if v {
459+
ctxlogf(ctx, 1, logsink.Info, true, noStack, format, args...)
460+
}
461+
}
462+
463+
// InfoContextDepth is equivalent to the global InfoContextDepth function, guarded by the value of v.
464+
// See the documentation of V for usage.
465+
func (v Verbose) InfoContextDepth(ctx context.Context, depth int, args ...any) {
466+
if v {
467+
ctxlogf(ctx, depth+1, logsink.Info, true, noStack, defaultFormat(args), args...)
468+
}
469+
}
470+
471+
// InfoContextDepthf is equivalent to the global InfoContextDepthf function, guarded by the value of v.
472+
// See the documentation of V for usage.
473+
func (v Verbose) InfoContextDepthf(ctx context.Context, depth int, format string, args ...any) {
474+
if v {
475+
ctxlogf(ctx, depth+1, logsink.Info, true, noStack, format, args...)
476+
}
477+
}
478+
421479
// Info logs to the INFO log.
422480
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
423481
func Info(args ...any) {
@@ -450,6 +508,30 @@ func Infof(format string, args ...any) {
450508
logf(1, logsink.Info, false, noStack, format, args...)
451509
}
452510

511+
// InfoContext is like [Info], but with an extra [context.Context] parameter. The
512+
// context is used to pass the Trace Context to log sinks.
513+
func InfoContext(ctx context.Context, args ...any) {
514+
InfoContextDepth(ctx, 1, args...)
515+
}
516+
517+
// InfoContextf is like [Infof], but with an extra [context.Context] parameter. The
518+
// context is used to pass the Trace Context to log sinks.
519+
func InfoContextf(ctx context.Context, format string, args ...any) {
520+
ctxlogf(ctx, 1, logsink.Info, false, noStack, format, args...)
521+
}
522+
523+
// InfoContextDepth is like [InfoDepth], but with an extra [context.Context] parameter. The
524+
// context is used to pass the Trace Context to log sinks.
525+
func InfoContextDepth(ctx context.Context, depth int, args ...any) {
526+
ctxlogf(ctx, depth+1, logsink.Info, false, noStack, defaultFormat(args), args...)
527+
}
528+
529+
// InfoContextDepthf is like [InfoDepthf], but with an extra [context.Context] parameter. The
530+
// context is used to pass the Trace Context to log sinks.
531+
func InfoContextDepthf(ctx context.Context, depth int, format string, args ...any) {
532+
ctxlogf(ctx, depth+1, logsink.Info, false, noStack, format, args...)
533+
}
534+
453535
// Warning logs to the WARNING and INFO logs.
454536
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
455537
func Warning(args ...any) {
@@ -480,6 +562,30 @@ func Warningf(format string, args ...any) {
480562
logf(1, logsink.Warning, false, noStack, format, args...)
481563
}
482564

565+
// WarningContext is like [Warning], but with an extra [context.Context] parameter. The
566+
// context is used to pass the Trace Context to log sinks.
567+
func WarningContext(ctx context.Context, args ...any) {
568+
WarningContextDepth(ctx, 1, args...)
569+
}
570+
571+
// WarningContextf is like [Warningf], but with an extra [context.Context] parameter. The
572+
// context is used to pass the Trace Context to log sinks.
573+
func WarningContextf(ctx context.Context, format string, args ...any) {
574+
ctxlogf(ctx, 1, logsink.Warning, false, noStack, format, args...)
575+
}
576+
577+
// WarningContextDepth is like [WarningDepth], but with an extra [context.Context] parameter. The
578+
// context is used to pass the Trace Context to log sinks.
579+
func WarningContextDepth(ctx context.Context, depth int, args ...any) {
580+
ctxlogf(ctx, depth+1, logsink.Warning, false, noStack, defaultFormat(args), args...)
581+
}
582+
583+
// WarningContextDepthf is like [WarningDepthf], but with an extra [context.Context] parameter. The
584+
// context is used to pass the Trace Context to log sinks.
585+
func WarningContextDepthf(ctx context.Context, depth int, format string, args ...any) {
586+
ctxlogf(ctx, depth+1, logsink.Warning, false, noStack, format, args...)
587+
}
588+
483589
// Error logs to the ERROR, WARNING, and INFO logs.
484590
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
485591
func Error(args ...any) {
@@ -510,8 +616,32 @@ func Errorf(format string, args ...any) {
510616
logf(1, logsink.Error, false, noStack, format, args...)
511617
}
512618

513-
func fatalf(depth int, format string, args ...any) {
514-
logf(depth+1, logsink.Fatal, false, withStack, format, args...)
619+
// ErrorContext is like [Error], but with an extra [context.Context] parameter. The
620+
// context is used to pass the Trace Context to log sinks.
621+
func ErrorContext(ctx context.Context, args ...any) {
622+
ErrorContextDepth(ctx, 1, args...)
623+
}
624+
625+
// ErrorContextf is like [Errorf], but with an extra [context.Context] parameter. The
626+
// context is used to pass the Trace Context to log sinks.
627+
func ErrorContextf(ctx context.Context, format string, args ...any) {
628+
ctxlogf(ctx, 1, logsink.Error, false, noStack, format, args...)
629+
}
630+
631+
// ErrorContextDepth is like [ErrorDepth], but with an extra [context.Context] parameter. The
632+
// context is used to pass the Trace Context to log sinks.
633+
func ErrorContextDepth(ctx context.Context, depth int, args ...any) {
634+
ctxlogf(ctx, depth+1, logsink.Error, false, noStack, defaultFormat(args), args...)
635+
}
636+
637+
// ErrorContextDepthf is like [ErrorDepthf], but with an extra [context.Context] parameter. The
638+
// context is used to pass the Trace Context to log sinks.
639+
func ErrorContextDepthf(ctx context.Context, depth int, format string, args ...any) {
640+
ctxlogf(ctx, depth+1, logsink.Error, false, noStack, format, args...)
641+
}
642+
643+
func ctxfatalf(ctx context.Context, depth int, format string, args ...any) {
644+
ctxlogf(ctx, depth+1, logsink.Fatal, false, withStack, format, args...)
515645
sinks.file.Flush()
516646

517647
err := abortProcess() // Should not return.
@@ -523,6 +653,10 @@ func fatalf(depth int, format string, args ...any) {
523653
os.Exit(2) // Exit with the same code as the default SIGABRT handler.
524654
}
525655

656+
func fatalf(depth int, format string, args ...any) {
657+
ctxfatalf(nil, depth+1, format, args...)
658+
}
659+
526660
// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
527661
// including a stack trace of all running goroutines, then calls os.Exit(2).
528662
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
@@ -556,12 +690,39 @@ func Fatalf(format string, args ...any) {
556690
fatalf(1, format, args...)
557691
}
558692

559-
func exitf(depth int, format string, args ...any) {
560-
logf(depth+1, logsink.Fatal, false, noStack, format, args...)
693+
// FatalContext is like [Fatal], but with an extra [context.Context] parameter. The
694+
// context is used to pass the Trace Context to log sinks.
695+
func FatalContext(ctx context.Context, args ...any) {
696+
FatalContextDepth(ctx, 1, args...)
697+
}
698+
699+
// FatalContextf is like [Fatalf], but with an extra [context.Context] parameter. The
700+
// context is used to pass the Trace Context to log sinks.
701+
func FatalContextf(ctx context.Context, format string, args ...any) {
702+
ctxfatalf(ctx, 1, format, args...)
703+
}
704+
705+
// FatalContextDepth is like [FatalDepth], but with an extra [context.Context] parameter. The
706+
// context is used to pass the Trace Context to log sinks.
707+
func FatalContextDepth(ctx context.Context, depth int, args ...any) {
708+
ctxfatalf(ctx, depth+1, defaultFormat(args), args...)
709+
}
710+
711+
// FatalContextDepthf is like [FatalDepthf], but with an extra [context.Context] parameter.
712+
func FatalContextDepthf(ctx context.Context, depth int, format string, args ...any) {
713+
ctxfatalf(ctx, depth+1, format, args...)
714+
}
715+
716+
func ctxexitf(ctx context.Context, depth int, format string, args ...any) {
717+
ctxlogf(ctx, depth+1, logsink.Fatal, false, noStack, format, args...)
561718
sinks.file.Flush()
562719
os.Exit(1)
563720
}
564721

722+
func exitf(depth int, format string, args ...any) {
723+
ctxexitf(nil, depth+1, format, args...)
724+
}
725+
565726
// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
566727
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
567728
func Exit(args ...any) {
@@ -590,3 +751,27 @@ func Exitln(args ...any) {
590751
func Exitf(format string, args ...any) {
591752
exitf(1, format, args...)
592753
}
754+
755+
// ExitContext is like [Exit], but with an extra [context.Context] parameter. The
756+
// context is used to pass the Trace Context to log sinks.
757+
func ExitContext(ctx context.Context, args ...any) {
758+
ExitContextDepth(ctx, 1, args...)
759+
}
760+
761+
// ExitContextf is like [Exitf], but with an extra [context.Context] parameter. The
762+
// context is used to pass the Trace Context to log sinks.
763+
func ExitContextf(ctx context.Context, format string, args ...any) {
764+
ctxexitf(ctx, 1, format, args...)
765+
}
766+
767+
// ExitContextDepth is like [ExitDepth], but with an extra [context.Context] parameter. The
768+
// context is used to pass the Trace Context to log sinks.
769+
func ExitContextDepth(ctx context.Context, depth int, args ...any) {
770+
ctxexitf(ctx, depth+1, defaultFormat(args), args...)
771+
}
772+
773+
// ExitContextDepthf is like [ExitDepthf], but with an extra [context.Context] parameter. The
774+
// context is used to pass the Trace Context to log sinks.
775+
func ExitContextDepthf(ctx context.Context, depth int, format string, args ...any) {
776+
ctxexitf(ctx, depth+1, format, args...)
777+
}

‎glog_context_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package glog
2+
3+
import (
4+
"context"
5+
"flag"
6+
"testing"
7+
8+
"github.com/golang/glog/internal/logsink"
9+
)
10+
11+
type contextKey string
12+
type fakeLogSink struct {
13+
context context.Context
14+
}
15+
16+
var ctxKey = contextKey("key")
17+
var ctxValue = "some-value"
18+
var originalSinks = logsink.StructuredSinks
19+
20+
func (s *fakeLogSink) Printf(meta *logsink.Meta, format string, args ...any) (int, error) {
21+
s.context = meta.Context
22+
return 0, nil
23+
}
24+
25+
// Test that log.(Info|Error|Warning)Context functions behave the same as non context variants
26+
// and pass right context.
27+
func TestLogContext(t *testing.T) {
28+
fakeLogSink := &fakeLogSink{}
29+
logsink.StructuredSinks = append([]logsink.Structured{fakeLogSink}, originalSinks...)
30+
31+
funcs := map[string]func(ctx context.Context, args ...any){
32+
"InfoContext": InfoContext,
33+
"InfoContextDepth": func(ctx context.Context, args ...any) { InfoContextDepth(ctx, 2, args) },
34+
"ErrorContext": ErrorContext,
35+
"WarningContext": WarningContext,
36+
}
37+
38+
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
39+
for name, f := range funcs {
40+
f(ctx, "test")
41+
want := ctxValue
42+
if got := fakeLogSink.context.Value(ctxKey); got != want {
43+
t.Errorf("%s: context value unexpectedly missing: got %q, want %q", name, got, want)
44+
}
45+
}
46+
}
47+
48+
// Test that V.InfoContext behaves the same as V.Info and passes right context.
49+
func TestVInfoContext(t *testing.T) {
50+
fakeLogSink := &fakeLogSink{}
51+
logsink.StructuredSinks = append([]logsink.Structured{fakeLogSink}, originalSinks...)
52+
if err := flag.Lookup("v").Value.Set("2"); err != nil {
53+
t.Fatalf("Failed to set -v=2: %v", err)
54+
}
55+
defer flag.Lookup("v").Value.Set("0")
56+
ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
57+
V(2).InfoContext(ctx, "test")
58+
want := ctxValue
59+
if got := fakeLogSink.context.Value(ctxKey); got != want {
60+
t.Errorf("V.InfoContext: context value unexpectedly missing: got %q, want %q", got, want)
61+
}
62+
}

‎glog_test.go

+115-62
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package glog
22

33
import (
44
"bytes"
5+
"context"
56
"flag"
67
"fmt"
78
"io/ioutil"
@@ -36,6 +37,7 @@ type flushBuffer struct {
3637
}
3738

3839
func (f *flushBuffer) Flush() error {
40+
f.Buffer.Reset()
3941
return nil
4042
}
4143

@@ -63,6 +65,16 @@ func (s *fileSink) newBuffers() severityWriters {
6365
return s.swap(severityWriters{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
6466
}
6567

68+
func (s *fileSink) resetBuffers() {
69+
s.mu.Lock()
70+
defer s.mu.Unlock()
71+
for _, buf := range s.file {
72+
if buf != nil {
73+
buf.Flush()
74+
}
75+
}
76+
}
77+
6678
// contents returns the specified log value as a string.
6779
func contents(s logsink.Severity) string {
6880
return sinks.file.file[s].(*flushBuffer).String()
@@ -82,55 +94,71 @@ func setFlags() {
8294
func TestInfo(t *testing.T) {
8395
setFlags()
8496
defer sinks.file.swap(sinks.file.newBuffers())
85-
Info("test")
86-
if !contains(logsink.Info, "I", t) {
87-
t.Errorf("Info has wrong character: %q", contents(logsink.Info))
97+
funcs := []func(args ...any){
98+
Info,
99+
func(args ...any) { InfoContext(context.Background(), args) },
88100
}
89-
if !contains(logsink.Info, "test", t) {
90-
t.Error("Info failed")
101+
102+
for _, f := range funcs {
103+
sinks.file.resetBuffers()
104+
f("test")
105+
if !contains(logsink.Info, "I", t) {
106+
t.Errorf("Info has wrong character: %q", contents(logsink.Info))
107+
}
108+
if !contains(logsink.Info, "test", t) {
109+
t.Error("Info failed")
110+
}
91111
}
92112
}
93113

94114
func TestInfoDepth(t *testing.T) {
95115
setFlags()
96116
defer sinks.file.swap(sinks.file.newBuffers())
97117

98-
f := func() { InfoDepth(1, "depth-test1") }
118+
funcs := []func(d int, args ...any){
119+
InfoDepth,
120+
func(d int, args ...any) { InfoContextDepth(context.Background(), d+1, args) },
121+
}
99122

100-
// The next three lines must stay together
101-
_, _, wantLine, _ := runtime.Caller(0)
102-
InfoDepth(0, "depth-test0")
103-
f()
123+
for _, infoDepth := range funcs {
124+
sinks.file.resetBuffers()
125+
f := func() { infoDepth(1, "depth-test1") }
104126

105-
msgs := strings.Split(strings.TrimSuffix(contents(logsink.Info), "\n"), "\n")
106-
if len(msgs) != 2 {
107-
t.Fatalf("Got %d lines, expected 2", len(msgs))
108-
}
127+
// The next three lines must stay together
128+
_, _, wantLine, _ := runtime.Caller(0)
129+
infoDepth(0, "depth-test0")
130+
f()
109131

110-
for i, m := range msgs {
111-
if !strings.HasPrefix(m, "I") {
112-
t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
113-
}
114-
w := fmt.Sprintf("depth-test%d", i)
115-
if !strings.Contains(m, w) {
116-
t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
132+
msgs := strings.Split(strings.TrimSuffix(contents(logsink.Info), "\n"), "\n")
133+
if len(msgs) != 2 {
134+
t.Fatalf("Got %d lines, expected 2", len(msgs))
117135
}
118136

119-
// pull out the line number (between : and ])
120-
msg := m[strings.LastIndex(m, ":")+1:]
121-
x := strings.Index(msg, "]")
122-
if x < 0 {
123-
t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
124-
continue
125-
}
126-
line, err := strconv.Atoi(msg[:x])
127-
if err != nil {
128-
t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
129-
continue
130-
}
131-
wantLine++
132-
if wantLine != line {
133-
t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
137+
for i, m := range msgs {
138+
if !strings.HasPrefix(m, "I") {
139+
t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
140+
}
141+
w := fmt.Sprintf("depth-test%d", i)
142+
if !strings.Contains(m, w) {
143+
t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
144+
}
145+
146+
// pull out the line number (between : and ])
147+
msg := m[strings.LastIndex(m, ":")+1:]
148+
x := strings.Index(msg, "]")
149+
if x < 0 {
150+
t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
151+
continue
152+
}
153+
line, err := strconv.Atoi(msg[:x])
154+
if err != nil {
155+
t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
156+
continue
157+
}
158+
wantLine++
159+
if wantLine != line {
160+
t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
161+
}
134162
}
135163
}
136164
}
@@ -204,19 +232,28 @@ func TestHeader(t *testing.T) {
204232
func TestError(t *testing.T) {
205233
setFlags()
206234
defer sinks.file.swap(sinks.file.newBuffers())
207-
Error("test")
208-
if !contains(logsink.Error, "E", t) {
209-
t.Errorf("Error has wrong character: %q", contents(logsink.Error))
210-
}
211-
if !contains(logsink.Error, "test", t) {
212-
t.Error("Error failed")
213-
}
214-
str := contents(logsink.Error)
215-
if !contains(logsink.Warning, str, t) {
216-
t.Error("Warning failed")
235+
236+
funcs := []func(args ...any){
237+
Error,
238+
func(args ...any) { ErrorContext(context.Background(), args) },
217239
}
218-
if !contains(logsink.Info, str, t) {
219-
t.Error("Info failed")
240+
241+
for _, error := range funcs {
242+
sinks.file.resetBuffers()
243+
error("test")
244+
if !contains(logsink.Error, "E", t) {
245+
t.Errorf("Error has wrong character: %q", contents(logsink.Error))
246+
}
247+
if !contains(logsink.Error, "test", t) {
248+
t.Error("Error failed")
249+
}
250+
str := contents(logsink.Error)
251+
if !contains(logsink.Warning, str, t) {
252+
t.Error("Warning failed")
253+
}
254+
if !contains(logsink.Info, str, t) {
255+
t.Error("Info failed")
256+
}
220257
}
221258
}
222259

@@ -226,16 +263,25 @@ func TestError(t *testing.T) {
226263
func TestWarning(t *testing.T) {
227264
setFlags()
228265
defer sinks.file.swap(sinks.file.newBuffers())
229-
Warning("test")
230-
if !contains(logsink.Warning, "W", t) {
231-
t.Errorf("Warning has wrong character: %q", contents(logsink.Warning))
232-
}
233-
if !contains(logsink.Warning, "test", t) {
234-
t.Error("Warning failed")
266+
267+
funcs := []func(args ...any){
268+
Warning,
269+
func(args ...any) { WarningContext(context.Background(), args) },
235270
}
236-
str := contents(logsink.Warning)
237-
if !contains(logsink.Info, str, t) {
238-
t.Error("Info failed")
271+
272+
for _, warning := range funcs {
273+
sinks.file.resetBuffers()
274+
warning("test")
275+
if !contains(logsink.Warning, "W", t) {
276+
t.Errorf("Warning has wrong character: %q", contents(logsink.Warning))
277+
}
278+
if !contains(logsink.Warning, "test", t) {
279+
t.Error("Warning failed")
280+
}
281+
str := contents(logsink.Warning)
282+
if !contains(logsink.Info, str, t) {
283+
t.Error("Info failed")
284+
}
239285
}
240286
}
241287

@@ -248,12 +294,19 @@ func TestV(t *testing.T) {
248294
}
249295
defer flag.Lookup("v").Value.Set("0")
250296

251-
V(2).Info("test")
252-
if !contains(logsink.Info, "I", t) {
253-
t.Errorf("Info has wrong character: %q", contents(logsink.Info))
297+
funcs := []func(args ...any){
298+
V(2).Info,
299+
func(args ...any) { V(2).InfoContext(context.Background(), args) },
254300
}
255-
if !contains(logsink.Info, "test", t) {
256-
t.Error("Info failed")
301+
for _, info := range funcs {
302+
sinks.file.resetBuffers()
303+
info("test")
304+
if !contains(logsink.Info, "I", t) {
305+
t.Errorf("Info has wrong character: %q", contents(logsink.Info))
306+
}
307+
if !contains(logsink.Info, "test", t) {
308+
t.Error("Info failed")
309+
}
257310
}
258311
}
259312

‎internal/logsink/logsink.go

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package logsink
1616

1717
import (
1818
"bytes"
19+
"context"
1920
"fmt"
2021
"strconv"
2122
"strings"
@@ -77,6 +78,11 @@ func ParseSeverity(name string) (Severity, error) {
7778

7879
// Meta is metadata about a logging call.
7980
type Meta struct {
81+
// The context with which the log call was made (or nil). If set, the context
82+
// is only valid during the logsink.Structured.Printf call, it should not be
83+
// retained.
84+
Context context.Context
85+
8086
// Time is the time at which the log call was made.
8187
Time time.Time
8288

0 commit comments

Comments
 (0)
Please sign in to comment.