json_formatter.go (3389B)
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 }