1 package fog
2
3 import (
4 "fmt"
5 "strings"
6
7 "github.com/go-logr/logr"
8 "go.uber.org/zap"
9 "go.uber.org/zap/zapcore"
10 )
11
12
13
14
15
16
17
18
19
20
21
22
23 type fogr struct {
24 l *zap.Logger
25 }
26
27 func New(opts ...Option) logr.Logger {
28 o := makeOptions(opts...)
29
30 c := zapcore.NewCore(
31 newEncoder(zapcore.EncoderConfig{
32 LevelKey: LevelKey,
33 TimeKey: TimeKey,
34 MessageKey: MessageKey,
35 StacktraceKey: StacktraceKey,
36 NameKey: "logger",
37 EncodeTime: zapcore.RFC3339TimeEncoder,
38 EncodeDuration: zapcore.MillisDurationEncoder,
39 EncodeLevel: encodeLevel(),
40 }),
41 zapcore.Lock(zapcore.AddSync(o.dest)),
42 zap.NewAtomicLevelAt(zapcore.Level(o.lvl)),
43 )
44 zopts := []zap.Option{
45 zap.AddCaller(),
46 zap.AddCallerSkip(1),
47 }
48
49 if o.development {
50 zopts = append(zopts, zap.Development())
51 }
52
53 return logr.New(&fogr{zap.New(c, zopts...)})
54 }
55
56
57 var _ logr.LogSink = (*fogr)(nil)
58 var _ logr.CallDepthLogSink = (*fogr)(nil)
59
60
61
62 func (f *fogr) Init(ri logr.RuntimeInfo) {
63 f.l = f.l.WithOptions(zap.AddCallerSkip(ri.CallDepth))
64 }
65
66
67 func (f *fogr) Enabled(lvl int) bool {
68 return f.l.Core().Enabled(toZapLevel(lvl))
69 }
70
71
72
73
74 func (f *fogr) Info(lvl int, msg string, keysAndVals ...interface{}) {
75 if checkedEntry := f.l.Check(toZapLevel(lvl), msg); checkedEntry != nil {
76 checkedEntry.Write(f.handleFields(lvl, keysAndVals)...)
77 }
78 }
79
80
81
82 func (f *fogr) Error(err error, msg string, keysAndVals ...interface{}) {
83 f.l.Error(msg, f.handleFields(int(zap.ErrorLevel), keysAndVals, zap.NamedError(errorKey, err))...)
84 }
85
86
87 func (f *fogr) WithValues(keysAndValues ...interface{}) logr.LogSink {
88 newLogger := *f
89 newLogger.l = f.l.With(f.handleFields(-1, keysAndValues)...)
90 return &newLogger
91 }
92
93
94 func (f *fogr) WithName(name string) logr.LogSink {
95 newLogger := *f
96 newLogger.l = f.l.Named(name)
97 return &newLogger
98 }
99
100
101 func (f *fogr) WithCallDepth(depth int) logr.LogSink {
102 newLogger := *f
103 newLogger.l = f.l.WithOptions(zap.AddCallerSkip(depth))
104 return &newLogger
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 func WithLabels(l logr.Logger, keysAndValues ...string) logr.Logger {
122 if len(keysAndValues)%2 != 0 {
123
124
125 if f, ok := l.GetSink().(*fogr); ok {
126 f.l.WithOptions(zap.AddCallerSkip(1)).DPanic(
127 "odd number of arguments passed as key-value pairs for logging labels",
128 zapIt("ignored", keysAndValues),
129 )
130 }
131
132 return l
133 }
134
135
136
137 for i := 0; i < len(keysAndValues); {
138 keysAndValues[i] = fmt.Sprintf("%s%s", gcpLabelKey, keysAndValues[i])
139 i += 2
140 }
141
142
143 modifiedKeysAndValues := make([]interface{}, len(keysAndValues))
144 for i := range keysAndValues {
145 modifiedKeysAndValues[i] = keysAndValues[i]
146 }
147
148
149 return l.WithValues(modifiedKeysAndValues...)
150 }
151
152
153
154 func toZapLevel(lvl int) zapcore.Level {
155 if lvl > 127 {
156 lvl = 127
157 }
158
159 return 0 - zapcore.Level(lvl)
160 }
161
162
163
164
165 func (f *fogr) handleFields(lvl int, args []interface{}, additional ...zap.Field) []zap.Field {
166 injectNumericLevel := lvl < 0
167
168 if len(args) == 0 {
169
170 if injectNumericLevel {
171 return additional
172 }
173
174 return append(additional, zap.Int(lvlKey, lvl))
175 }
176
177
178
179
180
181 numFields := len(args)/2 + len(additional)
182 if injectNumericLevel {
183 numFields++
184 }
185 fields := make([]zap.Field, 0, numFields)
186 if injectNumericLevel {
187 fields = append(fields, zap.Int(lvlKey, lvl))
188 }
189
190
191
192 var googleLabels map[string]string
193
194
195 for i := 0; i < len(args); {
196
197
198 if i == len(args)-1 {
199 f.l.WithOptions(zap.AddCallerSkip(1)).DPanic(
200 "odd number of arguments passed as key-value pairs for logging",
201 zapIt("ignored key", args[i]),
202 )
203 break
204 }
205
206
207
208
209 key, val := args[i], args[i+1]
210 keyStr, isString := key.(string)
211 if !isString {
212
213
214 f.l.WithOptions(zap.AddCallerSkip(1)).DPanic(
215 "non-string key argument passed to logging, ignoring all later arguments",
216 zapIt("invalid key", key),
217 )
218 break
219 }
220 if strings.HasPrefix(keyStr, gcpLabelKey) {
221 if googleLabels == nil {
222 googleLabels = make(map[string]string)
223 }
224
225 googleLabels[strings.TrimPrefix(keyStr, gcpLabelKey)] = val.(string)
226 } else if strings.HasPrefix(keyStr, gcpOperationKey) {
227 keyStr = strings.TrimPrefix(keyStr, gcpOperationKey)
228 fields = append(fields, zapIt(operationKey, map[string]string{keyStr: val.(string)}))
229 } else {
230 fields = append(fields, zapIt(keyStr, val))
231 }
232 i += 2
233 }
234
235
236
237 if googleLabels != nil {
238 fields = append(fields, zapIt(labelsKey, googleLabels))
239 }
240
241 return append(fields, additional...)
242 }
243
244
245 func zapIt(field string, val interface{}) zap.Field {
246
247
248 if marshaler, ok := val.(logr.Marshaler); ok {
249 field, val = invokeMarshaler(field, marshaler)
250 }
251 return zap.Any(field, val)
252 }
253
254 func invokeMarshaler(field string, m logr.Marshaler) (f string, ret interface{}) {
255 defer func() {
256 if r := recover(); r != nil {
257 ret = fmt.Sprintf("PANIC=%s", r)
258 f = field + "Error"
259 }
260 }()
261 return field, m.MarshalLog()
262 }
263
View as plain text