Awesome
logtags: key/value annotations for Go contexts
This package provides a way to attach key/value annotations
to a Go context.Context
.
This feature is used e.g. in CockroachDB to annotate contexts with user-facing details from the call stack, for use in logging output.
How to use:
-
adding k/v data to a context:
func AddTag(ctx context.Context, key string, value interface{}) context.Context
For example:
func foo(ctx context.Context) { ctx = logtags.AddTag(ctx, "foo", 123) bar(ctx) }
-
retrieving k/v data from a context:
func FromContext(ctx context.Context) *Buffer func (b *Buffer) Get() []Tag func (t *Tag) Key() string func (t *Tag) Value() interface{}
How it works:
logtags
stores the provided key/value pairs into an object of type
Buffer
, then uses Go's standard context.WithValue
to attach the
buffer.
An instance of Buffer
inside a context is immutable. When adding a
new k/v pair to a context that already carries a Buffer
, a new one
is created with all previous k/v pair, and the new k/v pair is added
to that.
The FromContext()
function retrieves the topmost (most recent)
Buffer
.
Advanced uses:
To add multiple k/v pairs in one go, without using quadratic space in
Buffer
instances:
- manually instantiate a
Buffer
. - use
func (*Buffer) Add(key string, value interface{})
to populate the buffer. - use
func AddTags(ctx context.Context, tags *Buffer) context.Context
to embark all the k/v pairs at once.
To format all the contained k/v pairs in a Buffer
, use its
String()
or FormatToString(*strings.Builder)
methods:
- when the
value
part isnil
, only the key is displayed. - when the
value
part is non-nil, and the key is just one character long, the key and value are concatenated for display. This enables e.g. printing k="n"
, v=123 asn123
. - otherwise, the key and value are printed with
=
as separator.
For example:
ctx = logtags.AddTag(ctx, "foo", 123)
ctx = logtags.AddTag(ctx, "x", 456)
ctx = logtags.AddTag(ctx, "bar", nil)
fmt.Println(logtags.FromContext(ctx).String())
// prints foo=123,x456,bar