Skip to content

Commit 972f219

Browse files
authored
logging: Reversed policy of fields - from immutable to fresh winning. (#616)
* logging: Reversed policy of fields - from immutable to fresh winning. Fixes: #613 Signed-off-by: bwplotka <bwplotka@gmail.com> * Addressed comments. Signed-off-by: bwplotka <bwplotka@gmail.com> --------- Signed-off-by: bwplotka <bwplotka@gmail.com>
1 parent c93f93f commit 972f219

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-11
lines changed

interceptors/logging/interceptors.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,16 @@ func reportable(logger Logger, opts *options) interceptors.CommonReportableFunc
141141
kind = KindClientFieldValue
142142
}
143143

144-
fields := ExtractFields(ctx).WithUnique(newCommonFields(kind, c))
144+
// Field dups from context override the common fields.
145+
fields := newCommonFields(kind, c).WithUnique(ExtractFields(ctx))
145146
if !c.IsClient {
146147
if peer, ok := peer.FromContext(ctx); ok {
147148
fields = append(fields, "peer.address", peer.Addr.String())
148149
}
149150
}
150151
if opts.fieldsFromCtxFn != nil {
151-
fields = fields.AppendUnique(opts.fieldsFromCtxFn(ctx))
152+
// fieldsFromCtxFn dups override the existing fields.
153+
fields = opts.fieldsFromCtxFn(ctx).AppendUnique(fields)
152154
}
153155

154156
singleUseFields := Fields{"grpc.start_time", time.Now().Format(opts.timestampFormat)}

interceptors/logging/logging.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ NextAddField:
129129
}
130130

131131
// ExtractFields returns logging fields from the context.
132-
// Logging interceptor adds fields into context when used.
133-
// If there are no fields in the context, returns an empty Fields value.
132+
// Fields can be added from the context using InjectFields. For example logging interceptor adds some (base) fields
133+
// into context when used.
134+
// If there are no fields in the context, it returns an empty Fields value.
134135
// Extracted fields are useful to construct your own logger that has fields from gRPC interceptors.
135136
func ExtractFields(ctx context.Context) Fields {
136137
t, ok := ctx.Value(fieldsCtxMarkerKey).(Fields)
@@ -142,13 +143,14 @@ func ExtractFields(ctx context.Context) Fields {
142143
return n
143144
}
144145

145-
// InjectFields allows adding fields to any existing Fields that will be used by the logging interceptor.
146-
// For explicitness, in case of duplicates, first field occurrence wins (immutability of fields). This also
147-
// applies to all fields created by logging middleware. It uses labels from this context as a base, so fields like "grpc.service"
148-
// can be overridden if your you add custom middleware that injects "grpc.service" before logging middleware injects those.
149-
// Don't overuse overriding to avoid surprises.
146+
// InjectFields allows adding fields to any existing Fields that will be used by the logging interceptor or can be
147+
// extracted further in ExtractFields.
148+
// For explicitness, in case of duplicates, the newest field occurrence wins. This allows nested components to update
149+
// popular fields like grpc.component (e.g. server invoking gRPC client).
150+
//
151+
// Don't overuse mutation of fields to avoid surprises.
150152
func InjectFields(ctx context.Context, f Fields) context.Context {
151-
return context.WithValue(ctx, fieldsCtxMarkerKey, ExtractFields(ctx).WithUnique(f))
153+
return context.WithValue(ctx, fieldsCtxMarkerKey, f.WithUnique(ExtractFields(ctx)))
152154
}
153155

154156
// InjectLogField is like InjectFields, just for one field.

interceptors/logging/logging_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) The go-grpc-middleware Authors.
2+
// Licensed under the Apache License 2.0.
3+
4+
package logging
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestFieldsInjectExtractFromContext(t *testing.T) {
14+
c := context.Background()
15+
f := ExtractFields(c)
16+
require.Equal(t, Fields(nil), f)
17+
18+
f = f.AppendUnique([]any{"a", "1", "b", "2"})
19+
require.Equal(t, Fields{"a", "1", "b", "2"}, f)
20+
21+
c2 := InjectFields(c, f)
22+
23+
// First context should be untouched.
24+
f = ExtractFields(c)
25+
require.Equal(t, Fields(nil), f)
26+
f = ExtractFields(c2)
27+
require.Equal(t, Fields{"a", "1", "b", "2"}, f)
28+
29+
f = Fields{"a", "changed"}.WithUnique(f)
30+
require.Equal(t, Fields{"a", "changed", "b", "2"}, f)
31+
32+
c3 := InjectFields(c, f)
33+
34+
// Old contexts should be untouched.
35+
f = ExtractFields(c)
36+
require.Equal(t, Fields(nil), f)
37+
f = ExtractFields(c2)
38+
require.Equal(t, Fields{"a", "1", "b", "2"}, f)
39+
f = ExtractFields(c3)
40+
require.Equal(t, Fields{"a", "changed", "b", "2"}, f)
41+
}

interceptors/logging/options.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func DefaultClientCodeToLevel(code codes.Code) Level {
132132

133133
type fieldsFromCtxFn func(ctx context.Context) Fields
134134

135-
// WithFieldsFromContext allows adding extra fields to all log messages per given request.
135+
// WithFieldsFromContext allows overriding existing or adding extra fields to all log messages per given request.
136136
func WithFieldsFromContext(f fieldsFromCtxFn) Option {
137137
return func(o *options) {
138138
o.fieldsFromCtxFn = f

0 commit comments

Comments
 (0)