123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- 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
- }
|