Skip to content

Commit 3ce33cf

Browse files
logging: add AddFields (#739)
* logging: add AddFields Support the similar helpers from the v1 introduced in #91: - [ctxzap.AddFields](https://github.com/grpc-ecosystem/go-grpc-middleware/blob/v1/logging/zap/ctxzap/context.go#L23-L32) - [ctxlogrus.AddFields](https://github.com/grpc-ecosystem/go-grpc-middleware/blob/v1/logging/logrus/ctxlogrus/context.go#L22-L30) - [ctxkit.AddFields](https://github.com/grpc-ecosystem/go-grpc-middleware/blob/v1/logging/kit/ctxkit/context.go#L22-L28) Signed-off-by: Olivier Cano <ocano@scaleway.com> * Update interceptors/logging/logging.go Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> --------- Signed-off-by: Olivier Cano <ocano@scaleway.com> Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
1 parent 66bb7d8 commit 3ce33cf

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

interceptors/logging/logging.go

+23-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ var (
2222
)
2323

2424
type fieldsCtxMarker struct{}
25+
type fieldsCtxValue struct {
26+
fields Fields
27+
}
2528

2629
var (
2730
// fieldsCtxMarkerKey is the Context value marker that is used by logging middleware to read and write logging fields into context.
@@ -161,30 +164,45 @@ NextAddField:
161164
// If there are no fields in the context, it returns an empty Fields value.
162165
// Extracted fields are useful to construct your own logger that has fields from gRPC interceptors.
163166
func ExtractFields(ctx context.Context) Fields {
164-
t, ok := ctx.Value(fieldsCtxMarkerKey).(Fields)
167+
t, ok := ctx.Value(fieldsCtxMarkerKey).(*fieldsCtxValue)
165168
if !ok {
166169
return nil
167170
}
168-
n := make(Fields, len(t))
169-
copy(n, t)
171+
n := make(Fields, len(t.fields))
172+
copy(n, t.fields)
170173
return n
171174
}
172175

173-
// InjectFields allows adding fields to any existing Fields that will be used by the logging interceptor or can be
176+
// InjectFields returns a new context with merged fields that will be used by the logging interceptor or can be
174177
// extracted further in ExtractFields.
175178
// For explicitness, in case of duplicates, the newest field occurrence wins. This allows nested components to update
176179
// popular fields like grpc.component (e.g. server invoking gRPC client).
177180
//
178181
// Don't overuse mutation of fields to avoid surprises.
179182
func InjectFields(ctx context.Context, f Fields) context.Context {
180-
return context.WithValue(ctx, fieldsCtxMarkerKey, f.WithUnique(ExtractFields(ctx)))
183+
return context.WithValue(ctx, fieldsCtxMarkerKey, &fieldsCtxValue{fields: f.WithUnique(ExtractFields(ctx))})
181184
}
182185

183186
// InjectLogField is like InjectFields, just for one field.
184187
func InjectLogField(ctx context.Context, key string, val any) context.Context {
185188
return InjectFields(ctx, Fields{key, val})
186189
}
187190

191+
// AddFields updates the fields already in the context that will be used by the logging interceptor or can be
192+
// extracted further in ExtractFields. For explicitness, in case of duplicates, the newest field occurrence wins.
193+
//
194+
// InjectFields should be used instead of AddFields where possible, as it does not require mutating the values
195+
// already in the context.
196+
//
197+
// This function is not safe to call concurrently.
198+
func AddFields(ctx context.Context, f Fields) {
199+
t, ok := ctx.Value(fieldsCtxMarkerKey).(*fieldsCtxValue)
200+
if !ok {
201+
return
202+
}
203+
t.fields = f.AppendUnique(t.fields)
204+
}
205+
188206
// Logger requires Log method, similar to experimental slog, allowing logging interceptor to be interoperable. Official
189207
// adapters for popular loggers are in `provider/` directory (separate modules). It's totally ok to copy simple function
190208
// implementation over.

interceptors/logging/logging_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,14 @@ func TestFieldsDelete(t *testing.T) {
4949
f.Delete("c")
5050
require.Equal(t, Fields{}, f)
5151
}
52+
53+
func TestAddFields(t *testing.T) {
54+
c := InjectFields(context.Background(), Fields{"a", "2", "c", "3"})
55+
f := ExtractFields(c)
56+
require.Equal(t, Fields{"a", "2", "c", "3"}, f)
57+
AddFields(c, Fields{"a", "1", "b", "2"})
58+
59+
// First context should have updated values.
60+
f = ExtractFields(c)
61+
require.Equal(t, Fields{"a", "1", "b", "2", "c", "3"}, f)
62+
}

0 commit comments

Comments
 (0)