-
Notifications
You must be signed in to change notification settings - Fork 0
/
gokitlogr.go
182 lines (164 loc) · 5.83 KB
/
gokitlogr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Package gokitlogr defines an implementation of the github.com/go-logr/logr
// interfaces built on top of go-kit/log (https://github.com/go-kit/log).
//
// Usage
//
// A new logr.Logger can be constructed from an existing log.Logger using
// the New function:
//
// log := gokitlogr.New(someGoKitLogger)
//
// Implementation Details
//
// For the most part, concepts in go-kit/log correspond directly with those in
// logr.
//
// Levels in logr correspond to custom debug levels in go-kit/log.
// V(0) and V(1) are equivalent to go-kit/log's Info level, while V(2) is
// equvalent to go-kit/log's Debug level. The Warn level is unused.
package gokitlogr
import (
"fmt"
kitlog "github.com/go-kit/log"
kitlevel "github.com/go-kit/log/level"
"github.com/go-logr/logr"
)
// TODO: as options, see:
// https://github.com/go-logr/zapr/blob/master/zapr.go#L245
var (
// NameFieldKey is the field key for logr.WithName
NameFieldKey = "logger"
// NameSeparator separates names for logr.WithName
NameSeparator = "/"
// ErrorFieldKey is the field key for logr.Error
ErrorFieldKey = "error"
// CallerFieldKey is the field key for call site information
// When using LogfmtLogger, this should be set to a different key
// than configured in go-kit/log.Logger to represent the true value
// of WithCallDepth call sites.
// When using JSONLogger, this should be set to the same key
// configured in go-kit/log.Logger to overwrite it with the true value
// of WithCallDepth call sites.
CallerFieldKey = "caller"
)
var (
_ logr.LogSink = &kitlogger{}
_ logr.CallDepthLogSink = &kitlogger{}
_ Underlier = &kitlogger{}
)
// New returns a logr.Logger with logr.LogSink implemented by go-kit/log.
func New(l *kitlog.Logger) logr.Logger {
ls := newKitLogger(l)
return logr.New(ls)
}
// kitlogger implements the LogSink interface.
type kitlogger struct {
kl *kitlog.Logger
name string
values []interface{}
depth int
}
// newKitLogger returns a logr.LogSink implemented by go-kit/log.
func newKitLogger(l *kitlog.Logger) *kitlogger {
return &kitlogger{kl: l}
}
// Enabled tests whether this LogSink is enabled at the specified V-level.
func (l kitlogger) Enabled(level int) bool {
// Optimization: Info() will check level internally.
const debugLevel = 2
return level <= debugLevel
}
// WithName returns a new LogSink with the specified name appended in NameFieldName.
// Name elements are separated by NameSeparator.
func (l kitlogger) WithName(name string) logr.LogSink {
if l.name != "" {
l.name += NameSeparator + name
} else {
l.name = name
}
return &l
}
// WithValues returns a new LogSink with additional key/value pairs.
// NOTE: look at "github.com/go-logr/logr/funcr".Formatter.AddValues for a more exhaustive implementation.
func (l kitlogger) WithValues(keysAndValues ...interface{}) logr.LogSink {
l.values = append(l.values, keysAndValues...)
return &l
}
// Info logs a non-error message at specified V-level with the given key/value pairs as context.
// Duplicate key/values are not allowed for JSONLogger, and last key overwrites previous values.
func (l *kitlogger) Info(level int, msg string, keysAndValues ...interface{}) {
kvs := append(l.values, keysAndValues...)
kvs = append(kvs, "msg", msg)
if l.name != "" {
kvs = append(kvs, NameFieldKey, l.name)
}
kvs = defaultRender(kvs)
if level > 1 {
kitlevel.Debug(*l.kl).Log(kvs...)
// NOTE: WON'T DO
// } else if level == 0 {
// kitlevel.Warn(*l.kl).Log(kvs...)
} else {
kitlevel.Info(*l.kl).Log(kvs...)
}
}
// Error logs an error, with the given message and key/value pairs as context.
// Duplicate key/values are not allowed for JSONLogger, and last key overwrites previous values.
func (l *kitlogger) Error(err error, msg string, keysAndValues ...interface{}) {
kvs := append(l.values, keysAndValues...)
kvs = append(kvs, "msg", msg, ErrorFieldKey, err)
if l.name != "" {
kvs = append(kvs, NameFieldKey, l.name)
}
kvs = defaultRender(kvs)
kitlevel.Error(*l.kl).Log(kvs...)
}
// defaultRender supports logr.Marshaler and fmt.Stringer.
// From: https://github.com/go-logr/zerologr/blob/33354eecabe37c0eacbba4df530534fed6d8a3f3/zerologr.go#L150-L162
func defaultRender(keysAndValues []interface{}) []interface{} {
for i, n := 1, len(keysAndValues); i < n; i += 2 {
value := keysAndValues[i]
switch v := value.(type) {
case logr.Marshaler:
keysAndValues[i] = v.MarshalLog()
case fmt.Stringer:
keysAndValues[i] = v.String()
}
}
return keysAndValues
}
// Init receives runtime info about the logr library.
func (l *kitlogger) Init(info logr.RuntimeInfo) {
l.depth = info.CallDepth + 4
}
// WithCallDepth returns a new LogSink that offsets the call
// stack by the specified number of frames when logging call
// site information.
//
// If depth is 0, the LogSink should skip exactly the number
// of call frames defined in RuntimeInfo.CallDepth when Info
// or Error are called, i.e. the attribution should be to the
// direct caller of Logger.Info or Logger.Error.
//
// If depth is 1 the attribution should skip 1 call frame, and so on.
// Successive calls to this are additive.
//
// WithCallDepth will be duplicated for LogfmtLogger if go-kit/log.Caller
// or go-kit/log.DefaultCaller is used, and present two key/value pairs,
// one of which will be incorrect.
func (l kitlogger) WithCallDepth(depth int) logr.LogSink {
newLogger := kitlog.With(*l.kl, CallerFieldKey, kitlog.Caller(l.depth+depth))
l.kl = &newLogger
return &l
}
// Underlier exposes access to the underlying logging implementation. Since
// callers only have a logr.Logger, they have to know which implementation is
// in use, so this interface is less of an abstraction and more of way to test
// type conversion.
type Underlier interface {
GetUnderlying() kitlog.Logger
}
// GetUnderlying returns the go-kit/log.Logger underneath this logSink.
func (l *kitlogger) GetUnderlying() kitlog.Logger {
return *l.kl
}