1 | package logrus
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bytes"
|
---|
5 | "encoding/json"
|
---|
6 | "fmt"
|
---|
7 | "runtime"
|
---|
8 | )
|
---|
9 |
|
---|
10 | type fieldKey string
|
---|
11 |
|
---|
12 | // FieldMap allows customization of the key names for default fields.
|
---|
13 | type FieldMap map[fieldKey]string
|
---|
14 |
|
---|
15 | func (f FieldMap) resolve(key fieldKey) string {
|
---|
16 | if k, ok := f[key]; ok {
|
---|
17 | return k
|
---|
18 | }
|
---|
19 |
|
---|
20 | return string(key)
|
---|
21 | }
|
---|
22 |
|
---|
23 | // JSONFormatter formats logs into parsable json
|
---|
24 | type JSONFormatter struct {
|
---|
25 | // TimestampFormat sets the format used for marshaling timestamps.
|
---|
26 | // The format to use is the same than for time.Format or time.Parse from the standard
|
---|
27 | // library.
|
---|
28 | // The standard Library already provides a set of predefined format.
|
---|
29 | TimestampFormat string
|
---|
30 |
|
---|
31 | // DisableTimestamp allows disabling automatic timestamps in output
|
---|
32 | DisableTimestamp bool
|
---|
33 |
|
---|
34 | // DisableHTMLEscape allows disabling html escaping in output
|
---|
35 | DisableHTMLEscape bool
|
---|
36 |
|
---|
37 | // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
---|
38 | DataKey string
|
---|
39 |
|
---|
40 | // FieldMap allows users to customize the names of keys for default fields.
|
---|
41 | // As an example:
|
---|
42 | // formatter := &JSONFormatter{
|
---|
43 | // FieldMap: FieldMap{
|
---|
44 | // FieldKeyTime: "@timestamp",
|
---|
45 | // FieldKeyLevel: "@level",
|
---|
46 | // FieldKeyMsg: "@message",
|
---|
47 | // FieldKeyFunc: "@caller",
|
---|
48 | // },
|
---|
49 | // }
|
---|
50 | FieldMap FieldMap
|
---|
51 |
|
---|
52 | // CallerPrettyfier can be set by the user to modify the content
|
---|
53 | // of the function and file keys in the json data when ReportCaller is
|
---|
54 | // activated. If any of the returned value is the empty string the
|
---|
55 | // corresponding key will be removed from json fields.
|
---|
56 | CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
---|
57 |
|
---|
58 | // PrettyPrint will indent all json logs
|
---|
59 | PrettyPrint bool
|
---|
60 | }
|
---|
61 |
|
---|
62 | // Format renders a single log entry
|
---|
63 | func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
---|
64 | data := make(Fields, len(entry.Data)+4)
|
---|
65 | for k, v := range entry.Data {
|
---|
66 | switch v := v.(type) {
|
---|
67 | case error:
|
---|
68 | // Otherwise errors are ignored by `encoding/json`
|
---|
69 | // https://github.com/sirupsen/logrus/issues/137
|
---|
70 | data[k] = v.Error()
|
---|
71 | default:
|
---|
72 | data[k] = v
|
---|
73 | }
|
---|
74 | }
|
---|
75 |
|
---|
76 | if f.DataKey != "" {
|
---|
77 | newData := make(Fields, 4)
|
---|
78 | newData[f.DataKey] = data
|
---|
79 | data = newData
|
---|
80 | }
|
---|
81 |
|
---|
82 | prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
---|
83 |
|
---|
84 | timestampFormat := f.TimestampFormat
|
---|
85 | if timestampFormat == "" {
|
---|
86 | timestampFormat = defaultTimestampFormat
|
---|
87 | }
|
---|
88 |
|
---|
89 | if entry.err != "" {
|
---|
90 | data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
|
---|
91 | }
|
---|
92 | if !f.DisableTimestamp {
|
---|
93 | data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
---|
94 | }
|
---|
95 | data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
---|
96 | data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
---|
97 | if entry.HasCaller() {
|
---|
98 | funcVal := entry.Caller.Function
|
---|
99 | fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
---|
100 | if f.CallerPrettyfier != nil {
|
---|
101 | funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
---|
102 | }
|
---|
103 | if funcVal != "" {
|
---|
104 | data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
|
---|
105 | }
|
---|
106 | if fileVal != "" {
|
---|
107 | data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
|
---|
108 | }
|
---|
109 | }
|
---|
110 |
|
---|
111 | var b *bytes.Buffer
|
---|
112 | if entry.Buffer != nil {
|
---|
113 | b = entry.Buffer
|
---|
114 | } else {
|
---|
115 | b = &bytes.Buffer{}
|
---|
116 | }
|
---|
117 |
|
---|
118 | encoder := json.NewEncoder(b)
|
---|
119 | encoder.SetEscapeHTML(!f.DisableHTMLEscape)
|
---|
120 | if f.PrettyPrint {
|
---|
121 | encoder.SetIndent("", " ")
|
---|
122 | }
|
---|
123 | if err := encoder.Encode(data); err != nil {
|
---|
124 | return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
---|
125 | }
|
---|
126 |
|
---|
127 | return b.Bytes(), nil
|
---|
128 | }
|
---|