| package logrus |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "runtime" |
| ) |
| |
| type fieldKey string |
| |
| // FieldMap allows customization of the key names for default fields. |
| type FieldMap map[fieldKey]string |
| |
| func (f FieldMap) resolve(key fieldKey) string { |
| if k, ok := f[key]; ok { |
| return k |
| } |
| |
| return string(key) |
| } |
| |
| // JSONFormatter formats logs into parsable json |
| type JSONFormatter struct { |
| // TimestampFormat sets the format used for marshaling timestamps. |
| TimestampFormat string |
| |
| // DisableTimestamp allows disabling automatic timestamps in output |
| DisableTimestamp bool |
| |
| // DisableHTMLEscape allows disabling html escaping in output |
| DisableHTMLEscape bool |
| |
| // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. |
| DataKey string |
| |
| // FieldMap allows users to customize the names of keys for default fields. |
| // As an example: |
| // formatter := &JSONFormatter{ |
| // FieldMap: FieldMap{ |
| // FieldKeyTime: "@timestamp", |
| // FieldKeyLevel: "@level", |
| // FieldKeyMsg: "@message", |
| // FieldKeyFunc: "@caller", |
| // }, |
| // } |
| FieldMap FieldMap |
| |
| // CallerPrettyfier can be set by the user to modify the content |
| // of the function and file keys in the json data when ReportCaller is |
| // activated. If any of the returned value is the empty string the |
| // corresponding key will be removed from json fields. |
| CallerPrettyfier func(*runtime.Frame) (function string, file string) |
| |
| // PrettyPrint will indent all json logs |
| PrettyPrint bool |
| } |
| |
| // Format renders a single log entry |
| func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { |
| data := make(Fields, len(entry.Data)+4) |
| for k, v := range entry.Data { |
| switch v := v.(type) { |
| case error: |
| // Otherwise errors are ignored by `encoding/json` |
| // https://github.com/sirupsen/logrus/issues/137 |
| data[k] = v.Error() |
| default: |
| data[k] = v |
| } |
| } |
| |
| if f.DataKey != "" { |
| newData := make(Fields, 4) |
| newData[f.DataKey] = data |
| data = newData |
| } |
| |
| prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) |
| |
| timestampFormat := f.TimestampFormat |
| if timestampFormat == "" { |
| timestampFormat = defaultTimestampFormat |
| } |
| |
| if entry.err != "" { |
| data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err |
| } |
| if !f.DisableTimestamp { |
| data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) |
| } |
| data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message |
| data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() |
| if entry.HasCaller() { |
| funcVal := entry.Caller.Function |
| fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) |
| if f.CallerPrettyfier != nil { |
| funcVal, fileVal = f.CallerPrettyfier(entry.Caller) |
| } |
| if funcVal != "" { |
| data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal |
| } |
| if fileVal != "" { |
| data[f.FieldMap.resolve(FieldKeyFile)] = fileVal |
| } |
| } |
| |
| var b *bytes.Buffer |
| if entry.Buffer != nil { |
| b = entry.Buffer |
| } else { |
| b = &bytes.Buffer{} |
| } |
| |
| encoder := json.NewEncoder(b) |
| encoder.SetEscapeHTML(!f.DisableHTMLEscape) |
| if f.PrettyPrint { |
| encoder.SetIndent("", " ") |
| } |
| if err := encoder.Encode(data); err != nil { |
| return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err) |
| } |
| |
| return b.Bytes(), nil |
| } |