1package 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} 106