Skip to content

Commit ec9d7cc

Browse files
authored
Merge pull request #3 from cabify/colega/context-logging-with-baggage-values
Context logging with baggage values & go mod
2 parents 27144f5 + 2b800ad commit ec9d7cc

File tree

8 files changed

+309
-0
lines changed

8 files changed

+309
-0
lines changed

baggage.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package log
2+
3+
import (
4+
"context"
5+
"crypto/md5"
6+
"fmt"
7+
"math/rand"
8+
"strconv"
9+
10+
"github.com/cabify/go-logging/internal"
11+
)
12+
13+
const loggerRequestIDLength = 7
14+
15+
// WithBaggageValue returns a context with the added key value pair in the baggage store.
16+
func WithBaggageValue(ctx context.Context, key, value string) context.Context {
17+
oldBaggage, ok := ctx.Value(internal.BaggageContextKey).(internal.Baggage)
18+
19+
if !ok {
20+
return context.WithValue(ctx, internal.BaggageContextKey, internal.Baggage{key: value})
21+
}
22+
23+
newBaggage := make(internal.Baggage, len(oldBaggage)+1)
24+
for oldKey, oldValue := range oldBaggage {
25+
newBaggage[oldKey] = oldValue
26+
}
27+
newBaggage[key] = value
28+
29+
return context.WithValue(ctx, internal.BaggageContextKey, newBaggage)
30+
}
31+
32+
// WithBaggageValues returns a context with all key value pairs added to the baggage store.
33+
func WithBaggageValues(ctx context.Context, keyValue map[string]string) context.Context {
34+
oldBaggage, ok := ctx.Value(internal.BaggageContextKey).(internal.Baggage)
35+
if !ok {
36+
return context.WithValue(ctx, internal.BaggageContextKey, internal.Baggage(keyValue))
37+
}
38+
39+
newBaggage := make(internal.Baggage, len(oldBaggage)+len(keyValue))
40+
for oldKey, oldValue := range oldBaggage {
41+
newBaggage[oldKey] = oldValue
42+
}
43+
44+
for newKey, newValue := range keyValue {
45+
newBaggage[newKey] = newValue
46+
}
47+
48+
return context.WithValue(ctx, internal.BaggageContextKey, newBaggage)
49+
}
50+
51+
// NewContextWithBaggageFrom returns a new context with baggage values obtained from another context
52+
func NewContextWithBaggageFrom(ctx context.Context) context.Context {
53+
oldBaggage, ok := ctx.Value(internal.BaggageContextKey).(internal.Baggage)
54+
if !ok {
55+
return context.Background()
56+
}
57+
return context.WithValue(context.Background(), internal.BaggageContextKey, oldBaggage)
58+
}
59+
60+
// NewContextFromWithValue creates a new context with baggage values from ctx plus the provided one
61+
// it's just a shorthand for the composition of NewContextWithBaggageFrom and WithBaggageValue
62+
func NewContextFromWithValue(ctx context.Context, k, v string) context.Context {
63+
return NewContextWithBaggageFrom(
64+
WithBaggageValue(ctx, k, v),
65+
)
66+
}
67+
68+
// NewID returns a random id to follow the log traces
69+
func NewID() string {
70+
data := rand.Int63()
71+
encoded := md5.Sum([]byte(strconv.FormatInt(data, 16)))
72+
return fmt.Sprintf("%x", encoded)[:loggerRequestIDLength]
73+
}

baggage_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package log
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func TestNewID(t *testing.T) {
9+
t.Run("has correct length", func(t *testing.T) {
10+
id := NewID()
11+
assert.Equal(t, loggerRequestIDLength, len(id))
12+
})
13+
}

context_logger.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package log
2+
3+
import (
4+
"context"
5+
6+
"github.com/cabify/go-logging/internal"
7+
)
8+
9+
type baggageLogger struct {
10+
Logger
11+
ctx context.Context
12+
}
13+
14+
func newBaggageLogger(ctx context.Context, base Logger) baggageLogger {
15+
return baggageLogger{
16+
Logger: base,
17+
ctx: ctx,
18+
}
19+
}
20+
21+
func (l baggageLogger) getContextString() string {
22+
baggage, ok := l.ctx.Value(internal.BaggageContextKey).(internal.Baggage)
23+
if !ok {
24+
return ""
25+
}
26+
27+
return baggage.String() + ": "
28+
}
29+
30+
func (l baggageLogger) Fatal(args ...interface{}) {
31+
l.Logger.Fatal(append([]interface{}{l.getContextString()}, args...)...)
32+
}
33+
34+
func (l baggageLogger) Fatalf(format string, args ...interface{}) {
35+
l.Logger.Fatalf(l.getContextString()+format, args...)
36+
}
37+
38+
func (l baggageLogger) Fatalln(args ...interface{}) {
39+
l.Logger.Fatalln(append([]interface{}{l.getContextString()}, args...)...)
40+
}
41+
42+
func (l baggageLogger) Panic(args ...interface{}) {
43+
l.Logger.Panic(append([]interface{}{l.getContextString()}, args...)...)
44+
}
45+
46+
func (l baggageLogger) Panicf(format string, args ...interface{}) {
47+
l.Logger.Panicf(l.getContextString()+format, args...)
48+
}
49+
50+
func (l baggageLogger) Panicln(args ...interface{}) {
51+
l.Logger.Panicln(append([]interface{}{l.getContextString()}, args...)...)
52+
}
53+
54+
func (l baggageLogger) Critical(args ...interface{}) {
55+
l.Logger.Critical(append([]interface{}{l.getContextString()}, args...)...)
56+
}
57+
58+
func (l baggageLogger) Criticalf(format string, args ...interface{}) {
59+
l.Logger.Criticalf(l.getContextString()+format, args...)
60+
}
61+
62+
func (l baggageLogger) Criticalln(args ...interface{}) {
63+
l.Logger.Criticalln(append([]interface{}{l.getContextString()}, args...)...)
64+
}
65+
66+
func (l baggageLogger) Error(args ...interface{}) {
67+
l.Logger.Error(append([]interface{}{l.getContextString()}, args...)...)
68+
}
69+
70+
func (l baggageLogger) Errorf(format string, args ...interface{}) {
71+
l.Logger.Errorf(l.getContextString()+format, args...)
72+
}
73+
74+
func (l baggageLogger) Errorln(args ...interface{}) {
75+
l.Logger.Errorln(append([]interface{}{l.getContextString()}, args...)...)
76+
}
77+
78+
func (l baggageLogger) Warning(args ...interface{}) {
79+
l.Logger.Warning(append([]interface{}{l.getContextString()}, args...)...)
80+
}
81+
82+
func (l baggageLogger) Warningf(format string, args ...interface{}) {
83+
l.Logger.Warningf(l.getContextString()+format, args...)
84+
}
85+
86+
func (l baggageLogger) Warningln(args ...interface{}) {
87+
l.Logger.Warningln(append([]interface{}{l.getContextString()}, args...)...)
88+
}
89+
90+
func (l baggageLogger) Notice(args ...interface{}) {
91+
l.Logger.Notice(append([]interface{}{l.getContextString()}, args...)...)
92+
}
93+
94+
func (l baggageLogger) Noticef(format string, args ...interface{}) {
95+
l.Logger.Noticef(l.getContextString()+format, args...)
96+
}
97+
98+
func (l baggageLogger) Noticeln(args ...interface{}) {
99+
l.Logger.Noticeln(append([]interface{}{l.getContextString()}, args...)...)
100+
}
101+
102+
func (l baggageLogger) Info(args ...interface{}) {
103+
l.Logger.Info(append([]interface{}{l.getContextString()}, args...)...)
104+
}
105+
106+
func (l baggageLogger) Infof(format string, args ...interface{}) {
107+
l.Logger.Infof(l.getContextString()+format, args...)
108+
}
109+
110+
func (l baggageLogger) Infoln(args ...interface{}) {
111+
l.Logger.Infoln(append([]interface{}{l.getContextString()}, args...)...)
112+
}
113+
114+
func (l baggageLogger) Debug(args ...interface{}) {
115+
l.Logger.Debug(append([]interface{}{l.getContextString()}, args...)...)
116+
}
117+
118+
func (l baggageLogger) Debugf(format string, args ...interface{}) {
119+
l.Logger.Debugf(l.getContextString()+format, args...)
120+
}
121+
122+
func (l baggageLogger) Debugln(args ...interface{}) {
123+
l.Logger.Debugln(append([]interface{}{l.getContextString()}, args...)...)
124+
}

factory.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package log
2+
3+
import (
4+
"context"
5+
6+
)
7+
8+
// Factory provides context aware loggers.
9+
type Factory struct {
10+
baseLogger Logger
11+
}
12+
13+
// NewFactory instantiates a factory with the default logger.
14+
func NewFactory() Factory {
15+
return Factory{
16+
baseLogger: DefaultLogger,
17+
}
18+
}
19+
20+
// For provides a logger which is aware of the passed context and will prepend the context baggage values.
21+
func (f Factory) For(ctx context.Context) Logger {
22+
return newBaggageLogger(ctx, f.baseLogger)
23+
}
24+
25+
// For provides a logger which is aware of the passed context and will prepend
26+
// the context baggage values, using DefaultLogger as base logger.
27+
func For(ctx context.Context) Logger {
28+
return newBaggageLogger(ctx, DefaultLogger)
29+
}

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/cabify/go-logging
2+
3+
require (
4+
github.com/hashicorp/go-multierror v1.0.0
5+
github.com/mattn/go-isatty v0.0.4
6+
github.com/stretchr/testify v1.3.0
7+
golang.org/x/sys v0.0.0-20190219092855-153ac476189d // indirect
8+
)

go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
4+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
5+
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
6+
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
7+
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
8+
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
9+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
10+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
12+
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
13+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
14+
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
15+
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

internal/baggage.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package internal
2+
3+
import (
4+
"sort"
5+
"strings"
6+
)
7+
8+
// BaggageContextKey is the key to be used for the Baggage map in
9+
// context.*Value.
10+
const BaggageContextKey ContextKey = "baggage"
11+
12+
// ContextKey is the type of BaggageContextKey.
13+
type ContextKey string
14+
15+
// Baggage is the type of the value associated with BaggageContextKey.
16+
type Baggage map[string]string
17+
18+
// String returns baggages contents as a string of key value pairs ordered alphabetically.
19+
func (b Baggage) String() string {
20+
var kvPairs []string
21+
for key, value := range b {
22+
kvPairs = append(kvPairs, key+":"+value)
23+
}
24+
25+
sort.Strings(kvPairs)
26+
27+
return strings.Join(kvPairs, ": ")
28+
}

logtest/baggage.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
Package logtest is intended to be used by tests for checking that baggage values have been correctly set
3+
*/
4+
package logtest
5+
6+
import (
7+
"context"
8+
9+
"github.com/cabify/go-logging/internal"
10+
)
11+
12+
// HasBaggageValue returns whether the context contains a baggage value.
13+
func HasBaggageValue(ctx context.Context, key, value string) bool {
14+
baggage, ok := ctx.Value(internal.BaggageContextKey).(internal.Baggage)
15+
if !ok {
16+
return false
17+
}
18+
return baggage[key] == value
19+
}

0 commit comments

Comments
 (0)