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
|
package logr
import (
"bytes"
"fmt"
"io"
"runtime"
"sort"
)
// Formatter turns a LogRec into a formatted string.
type Formatter interface {
// Format converts a log record to bytes. If buf is not nil then it will be
// be filled with the formatted results, otherwise a new buffer will be allocated.
Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error)
}
const (
// DefTimestampFormat is the default time stamp format used by
// Plain formatter and others.
DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00"
)
// DefaultFormatter is the default formatter, outputting only text with
// no colors and a space delimiter. Use `format.Plain` instead.
type DefaultFormatter struct {
}
// Format converts a log record to bytes.
func (p *DefaultFormatter) Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
if buf == nil {
buf = &bytes.Buffer{}
}
delim := " "
timestampFmt := DefTimestampFormat
fmt.Fprintf(buf, "%s%s", rec.Time().Format(timestampFmt), delim)
fmt.Fprintf(buf, "%v%s", rec.Level(), delim)
fmt.Fprint(buf, rec.Msg(), delim)
ctx := rec.Fields()
if len(ctx) > 0 {
WriteFields(buf, ctx, " ")
}
if stacktrace {
frames := rec.StackFrames()
if len(frames) > 0 {
buf.WriteString("\n")
WriteStacktrace(buf, rec.StackFrames())
}
}
buf.WriteString("\n")
return buf, nil
}
// WriteFields writes zero or more name value pairs to the io.Writer.
// The pairs are sorted by key name and output in key=value format
// with optional separator between fields.
func WriteFields(w io.Writer, flds Fields, separator string) {
keys := make([]string, 0, len(flds))
for k := range flds {
keys = append(keys, k)
}
sort.Strings(keys)
sep := ""
for _, key := range keys {
writeField(w, key, flds[key], sep)
sep = separator
}
}
func writeField(w io.Writer, key string, val interface{}, sep string) {
var template string
switch v := val.(type) {
case error:
val := v.Error()
if shouldQuote(val) {
template = "%s%s=%q"
} else {
template = "%s%s=%s"
}
case string:
if shouldQuote(v) {
template = "%s%s=%q"
} else {
template = "%s%s=%s"
}
default:
template = "%s%s=%v"
}
fmt.Fprintf(w, template, sep, key, val)
}
// shouldQuote returns true if val contains any characters that might be unsafe
// when injecting log output into an aggregator, viewer or report.
func shouldQuote(val string) bool {
for _, c := range val {
if !((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z')) {
return true
}
}
return false
}
// WriteStacktrace formats and outputs a stack trace to an io.Writer.
func WriteStacktrace(w io.Writer, frames []runtime.Frame) {
for _, frame := range frames {
if frame.Function != "" {
fmt.Fprintf(w, " %s\n", frame.Function)
}
if frame.File != "" {
fmt.Fprintf(w, " %s:%d\n", frame.File, frame.Line)
}
}
}
|