blob: 2605753599faf140f7a91f3fc462da4c0971ef64 [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001package logrus
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7)
8
9type fieldKey string
10
11// FieldMap allows customization of the key names for default fields.
12type FieldMap map[fieldKey]string
13
14func (f FieldMap) resolve(key fieldKey) string {
15 if k, ok := f[key]; ok {
16 return k
17 }
18
19 return string(key)
20}
21
22// JSONFormatter formats logs into parsable json
23type JSONFormatter struct {
24 // TimestampFormat sets the format used for marshaling timestamps.
25 TimestampFormat string
26
27 // DisableTimestamp allows disabling automatic timestamps in output
28 DisableTimestamp bool
29
30 // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
31 DataKey string
32
33 // FieldMap allows users to customize the names of keys for default fields.
34 // As an example:
35 // formatter := &JSONFormatter{
36 // FieldMap: FieldMap{
37 // FieldKeyTime: "@timestamp",
38 // FieldKeyLevel: "@level",
39 // FieldKeyMsg: "@message",
40 // FieldKeyFunc: "@caller",
41 // },
42 // }
43 FieldMap FieldMap
44
45 // PrettyPrint will indent all json logs
46 PrettyPrint bool
47}
48
49// Format renders a single log entry
50func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
51 data := make(Fields, len(entry.Data)+4)
52 for k, v := range entry.Data {
53 switch v := v.(type) {
54 case error:
55 // Otherwise errors are ignored by `encoding/json`
56 // https://github.com/sirupsen/logrus/issues/137
57 data[k] = v.Error()
58 default:
59 data[k] = v
60 }
61 }
62
63 if f.DataKey != "" {
64 newData := make(Fields, 4)
65 newData[f.DataKey] = data
66 data = newData
67 }
68
69 prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
70
71 timestampFormat := f.TimestampFormat
72 if timestampFormat == "" {
73 timestampFormat = defaultTimestampFormat
74 }
75
76 if entry.err != "" {
77 data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
78 }
79 if !f.DisableTimestamp {
80 data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
81 }
82 data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
83 data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
84 if entry.HasCaller() {
85 data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function
86 data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
87 }
88
89 var b *bytes.Buffer
90 if entry.Buffer != nil {
91 b = entry.Buffer
92 } else {
93 b = &bytes.Buffer{}
94 }
95
96 encoder := json.NewEncoder(b)
97 if f.PrettyPrint {
98 encoder.SetIndent("", " ")
99 }
100 if err := encoder.Encode(data); err != nil {
101 return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
102 }
103
104 return b.Bytes(), nil
105}