| 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. | |
| // The format to use is the same than for time.Format or time.Parse from the standard | |
| // library. | |
| // The standard Library already provides a set of predefined format. | |
| 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, %w", err) | |
| } | |
| return b.Bytes(), nil | |
| } |