json_formatter.go raw

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