134 | 134 |
var _ logr.CallDepthLogSink = &fnlogger{}
|
135 | 135 |
var _ Underlier = &fnlogger{}
|
136 | 136 |
|
137 | |
func flatten(kvList ...interface{}) string {
|
|
137 |
// NewFormatter constructs a Formatter.
|
|
138 |
func NewFormatter(opts Options) Formatter {
|
|
139 |
f := Formatter{
|
|
140 |
prefix: "",
|
|
141 |
values: nil,
|
|
142 |
depth: 0,
|
|
143 |
logCaller: opts.LogCaller,
|
|
144 |
logTimestamp: opts.LogTimestamp,
|
|
145 |
verbosity: opts.Verbosity,
|
|
146 |
}
|
|
147 |
return f
|
|
148 |
}
|
|
149 |
|
|
150 |
// Formatter is an opaque struct which can be embedded in a LogSink
|
|
151 |
// implementation. It should be constructed with NewFormatter. Some of
|
|
152 |
// its methods directly implement logr.LogSink.
|
|
153 |
type Formatter struct {
|
|
154 |
prefix string
|
|
155 |
values []interface{}
|
|
156 |
depth int
|
|
157 |
logCaller MessageClass
|
|
158 |
logTimestamp bool
|
|
159 |
verbosity int
|
|
160 |
}
|
|
161 |
|
|
162 |
func (f Formatter) flatten(kvList ...interface{}) string {
|
138 | 163 |
if len(kvList)%2 != 0 {
|
139 | 164 |
kvList = append(kvList, "<no-value>")
|
140 | 165 |
}
|
|
154 | 179 |
buf.WriteString(k)
|
155 | 180 |
buf.WriteRune('"')
|
156 | 181 |
buf.WriteRune('=')
|
157 | |
buf.WriteString(pretty(v))
|
|
182 |
buf.WriteString(f.pretty(v))
|
158 | 183 |
}
|
159 | 184 |
return buf.String()
|
160 | 185 |
}
|
161 | 186 |
|
162 | |
func pretty(value interface{}) string {
|
163 | |
return prettyWithFlags(value, 0)
|
|
187 |
func (f Formatter) pretty(value interface{}) string {
|
|
188 |
return f.prettyWithFlags(value, 0)
|
164 | 189 |
}
|
165 | 190 |
|
166 | 191 |
const (
|
|
168 | 193 |
)
|
169 | 194 |
|
170 | 195 |
// TODO: This is not fast. Most of the overhead goes here.
|
171 | |
func prettyWithFlags(value interface{}, flags uint32) string {
|
|
196 |
func (f Formatter) prettyWithFlags(value interface{}, flags uint32) string {
|
172 | 197 |
// Handle types that take full control of logging.
|
173 | 198 |
if v, ok := value.(logr.Marshaler); ok {
|
174 | 199 |
// Replace the value with what the type wants to get logged.
|
|
256 | 281 |
case reflect.Struct:
|
257 | 282 |
buf.WriteRune('{')
|
258 | 283 |
for i := 0; i < t.NumField(); i++ {
|
259 | |
f := t.Field(i)
|
260 | |
if f.PkgPath != "" {
|
|
284 |
fld := t.Field(i)
|
|
285 |
if fld.PkgPath != "" {
|
261 | 286 |
// reflect says this field is only defined for non-exported fields.
|
262 | 287 |
continue
|
263 | 288 |
}
|
|
265 | 290 |
buf.WriteRune(',')
|
266 | 291 |
}
|
267 | 292 |
buf.WriteRune('"')
|
268 | |
name := f.Name
|
269 | |
if tag, found := f.Tag.Lookup("json"); found {
|
|
293 |
name := fld.Name
|
|
294 |
if tag, found := fld.Tag.Lookup("json"); found {
|
270 | 295 |
if comma := strings.Index(tag, ","); comma != -1 {
|
271 | 296 |
name = tag[:comma]
|
272 | 297 |
} else {
|
|
276 | 301 |
buf.WriteString(name)
|
277 | 302 |
buf.WriteRune('"')
|
278 | 303 |
buf.WriteRune(':')
|
279 | |
buf.WriteString(pretty(v.Field(i).Interface()))
|
|
304 |
buf.WriteString(f.pretty(v.Field(i).Interface()))
|
280 | 305 |
}
|
281 | 306 |
buf.WriteRune('}')
|
282 | 307 |
return buf.String()
|
|
287 | 312 |
buf.WriteRune(',')
|
288 | 313 |
}
|
289 | 314 |
e := v.Index(i)
|
290 | |
buf.WriteString(pretty(e.Interface()))
|
|
315 |
buf.WriteString(f.pretty(e.Interface()))
|
291 | 316 |
}
|
292 | 317 |
buf.WriteRune(']')
|
293 | 318 |
return buf.String()
|
|
302 | 327 |
}
|
303 | 328 |
// JSON only does string keys.
|
304 | 329 |
buf.WriteRune('"')
|
305 | |
buf.WriteString(prettyWithFlags(it.Key().Interface(), flagRawString))
|
|
330 |
buf.WriteString(f.prettyWithFlags(it.Key().Interface(), flagRawString))
|
306 | 331 |
buf.WriteRune('"')
|
307 | 332 |
buf.WriteRune(':')
|
308 | |
buf.WriteString(pretty(it.Value().Interface()))
|
|
333 |
buf.WriteString(f.pretty(it.Value().Interface()))
|
309 | 334 |
i++
|
310 | 335 |
}
|
311 | 336 |
buf.WriteRune('}')
|
|
314 | 339 |
if v.IsNil() {
|
315 | 340 |
return "null"
|
316 | 341 |
}
|
317 | |
return pretty(v.Elem().Interface())
|
|
342 |
return f.pretty(v.Elem().Interface())
|
318 | 343 |
}
|
319 | 344 |
return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
|
320 | 345 |
}
|
|
322 | 347 |
type callerID struct {
|
323 | 348 |
File string `json:"file"`
|
324 | 349 |
Line int `json:"line"`
|
325 | |
}
|
326 | |
|
327 | |
// NewFormatter constructs a Formatter.
|
328 | |
func NewFormatter(opts Options) Formatter {
|
329 | |
f := Formatter{
|
330 | |
prefix: "",
|
331 | |
values: nil,
|
332 | |
depth: 0,
|
333 | |
logCaller: opts.LogCaller,
|
334 | |
logTimestamp: opts.LogTimestamp,
|
335 | |
verbosity: opts.Verbosity,
|
336 | |
}
|
337 | |
return f
|
338 | |
}
|
339 | |
|
340 | |
// Formatter is an opaque struct which can be embedded in a LogSink
|
341 | |
// implementation. It should be constructed with NewFormatter. Some of
|
342 | |
// its methods directly implement logr.LogSink.
|
343 | |
type Formatter struct {
|
344 | |
prefix string
|
345 | |
values []interface{}
|
346 | |
depth int
|
347 | |
logCaller MessageClass
|
348 | |
logTimestamp bool
|
349 | |
verbosity int
|
350 | 350 |
}
|
351 | 351 |
|
352 | 352 |
func (f Formatter) caller() callerID {
|
|
389 | 389 |
args = append(args, "level", level, "msg", msg)
|
390 | 390 |
args = append(args, f.values...)
|
391 | 391 |
args = append(args, kvList...)
|
392 | |
return f.prefix, flatten(args...)
|
|
392 |
return f.prefix, f.flatten(args...)
|
393 | 393 |
}
|
394 | 394 |
|
395 | 395 |
// FormatError flattens an Error log message into strings.
|
|
410 | 410 |
args = append(args, "error", loggableErr)
|
411 | 411 |
args = append(args, f.values...)
|
412 | 412 |
args = append(args, kvList...)
|
413 | |
return f.prefix, flatten(args...)
|
|
413 |
return f.prefix, f.flatten(args...)
|
414 | 414 |
}
|
415 | 415 |
|
416 | 416 |
// AddName appends the specified name. funcr uses '/' characters to separate
|