66
|
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 }
|