blob: c96dc5636bf0494761604f6db012c55bebe93027 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001package logrus
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
khenaidoo26721882021-08-11 17:42:52 -04007 "runtime"
khenaidoo59ce9dd2019-11-11 13:05:32 -05008)
9
10type fieldKey string
11
12// FieldMap allows customization of the key names for default fields.
13type FieldMap map[fieldKey]string
14
15func (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
24type JSONFormatter struct {
25 // TimestampFormat sets the format used for marshaling timestamps.
Abhay Kumar40252eb2025-10-13 13:25:53 +000026 // 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.
khenaidoo59ce9dd2019-11-11 13:05:32 -050029 TimestampFormat string
30
31 // DisableTimestamp allows disabling automatic timestamps in output
32 DisableTimestamp bool
33
khenaidoo26721882021-08-11 17:42:52 -040034 // DisableHTMLEscape allows disabling html escaping in output
35 DisableHTMLEscape bool
36
khenaidoo59ce9dd2019-11-11 13:05:32 -050037 // 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
khenaidoo26721882021-08-11 17:42:52 -040052 // 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
khenaidoo59ce9dd2019-11-11 13:05:32 -050058 // PrettyPrint will indent all json logs
59 PrettyPrint bool
60}
61
62// Format renders a single log entry
63func (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() {
khenaidoo26721882021-08-11 17:42:52 -040098 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 }
khenaidoo59ce9dd2019-11-11 13:05:32 -0500109 }
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)
khenaidoo26721882021-08-11 17:42:52 -0400119 encoder.SetEscapeHTML(!f.DisableHTMLEscape)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500120 if f.PrettyPrint {
121 encoder.SetIndent("", " ")
122 }
123 if err := encoder.Encode(data); err != nil {
Abhay Kumar40252eb2025-10-13 13:25:53 +0000124 return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500125 }
126
127 return b.Bytes(), nil
128}