diff options
Diffstat (limited to 'vendor/github.com/mattermost/logr/format/json.go')
-rw-r--r-- | vendor/github.com/mattermost/logr/format/json.go | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/logr/format/json.go b/vendor/github.com/mattermost/logr/format/json.go new file mode 100644 index 00000000..8f56c6cb --- /dev/null +++ b/vendor/github.com/mattermost/logr/format/json.go @@ -0,0 +1,273 @@ +package format + +import ( + "bytes" + "fmt" + "runtime" + "sort" + "sync" + "time" + + "github.com/francoispqt/gojay" + "github.com/mattermost/logr" +) + +// ContextField is a name/value pair within the context fields. +type ContextField struct { + Key string + Val interface{} +} + +// JSON formats log records as JSON. +type JSON struct { + // DisableTimestamp disables output of timestamp field. + DisableTimestamp bool + // DisableLevel disables output of level field. + DisableLevel bool + // DisableMsg disables output of msg field. + DisableMsg bool + // DisableContext disables output of all context fields. + DisableContext bool + // DisableStacktrace disables output of stack trace. + DisableStacktrace bool + + // TimestampFormat is an optional format for timestamps. If empty + // then DefTimestampFormat is used. + TimestampFormat string + + // Deprecated: this has no effect. + Indent string + + // EscapeHTML determines if certain characters (e.g. `<`, `>`, `&`) + // are escaped. + EscapeHTML bool + + // KeyTimestamp overrides the timestamp field key name. + KeyTimestamp string + + // KeyLevel overrides the level field key name. + KeyLevel string + + // KeyMsg overrides the msg field key name. + KeyMsg string + + // KeyContextFields when not empty will group all context fields + // under this key. + KeyContextFields string + + // KeyStacktrace overrides the stacktrace field key name. + KeyStacktrace string + + // ContextSorter allows custom sorting for the context fields. + ContextSorter func(fields logr.Fields) []ContextField + + once sync.Once +} + +// Format converts a log record to bytes in JSON format. +func (j *JSON) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { + j.once.Do(j.applyDefaultKeyNames) + + if buf == nil { + buf = &bytes.Buffer{} + } + enc := gojay.BorrowEncoder(buf) + defer func() { + enc.Release() + }() + + sorter := j.ContextSorter + if sorter == nil { + sorter = j.defaultContextSorter + } + + jlr := JSONLogRec{ + LogRec: rec, + JSON: j, + stacktrace: stacktrace, + sorter: sorter, + } + + err := enc.EncodeObject(jlr) + if err != nil { + return nil, err + } + buf.WriteByte('\n') + return buf, nil +} + +func (j *JSON) applyDefaultKeyNames() { + if j.KeyTimestamp == "" { + j.KeyTimestamp = "timestamp" + } + if j.KeyLevel == "" { + j.KeyLevel = "level" + } + if j.KeyMsg == "" { + j.KeyMsg = "msg" + } + if j.KeyStacktrace == "" { + j.KeyStacktrace = "stacktrace" + } +} + +// defaultContextSorter sorts the context fields alphabetically by key. +func (j *JSON) defaultContextSorter(fields logr.Fields) []ContextField { + keys := make([]string, 0, len(fields)) + for k := range fields { + keys = append(keys, k) + } + sort.Strings(keys) + + cf := make([]ContextField, 0, len(keys)) + for _, k := range keys { + cf = append(cf, ContextField{Key: k, Val: fields[k]}) + } + return cf +} + +// JSONLogRec decorates a LogRec adding JSON encoding. +type JSONLogRec struct { + *logr.LogRec + *JSON + stacktrace bool + sorter func(fields logr.Fields) []ContextField +} + +// MarshalJSONObject encodes the LogRec as JSON. +func (rec JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) { + if !rec.DisableTimestamp { + timestampFmt := rec.TimestampFormat + if timestampFmt == "" { + timestampFmt = logr.DefTimestampFormat + } + time := rec.Time() + enc.AddTimeKey(rec.KeyTimestamp, &time, timestampFmt) + } + if !rec.DisableLevel { + enc.AddStringKey(rec.KeyLevel, rec.Level().Name) + } + if !rec.DisableMsg { + enc.AddStringKey(rec.KeyMsg, rec.Msg()) + } + if !rec.DisableContext { + ctxFields := rec.sorter(rec.Fields()) + if rec.KeyContextFields != "" { + enc.AddObjectKey(rec.KeyContextFields, jsonFields(ctxFields)) + } else { + if len(ctxFields) > 0 { + for _, cf := range ctxFields { + key := rec.prefixCollision(cf.Key) + encodeField(enc, key, cf.Val) + } + } + } + } + if rec.stacktrace && !rec.DisableStacktrace { + frames := rec.StackFrames() + if len(frames) > 0 { + enc.AddArrayKey(rec.KeyStacktrace, stackFrames(frames)) + } + } + +} + +// IsNil returns true if the LogRec pointer is nil. +func (rec JSONLogRec) IsNil() bool { + return rec.LogRec == nil +} + +func (rec JSONLogRec) prefixCollision(key string) string { + switch key { + case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace: + return rec.prefixCollision("_" + key) + } + return key +} + +type stackFrames []runtime.Frame + +// MarshalJSONArray encodes stackFrames slice as JSON. +func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) { + for _, frame := range s { + enc.AddObject(stackFrame(frame)) + } +} + +// IsNil returns true if stackFrames is empty slice. +func (s stackFrames) IsNil() bool { + return len(s) == 0 +} + +type stackFrame runtime.Frame + +// MarshalJSONArray encodes stackFrame as JSON. +func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) { + enc.AddStringKey("Function", f.Function) + enc.AddStringKey("File", f.File) + enc.AddIntKey("Line", f.Line) +} + +func (f stackFrame) IsNil() bool { + return false +} + +type jsonFields []ContextField + +// MarshalJSONObject encodes Fields map to JSON. +func (f jsonFields) MarshalJSONObject(enc *gojay.Encoder) { + for _, ctxField := range f { + encodeField(enc, ctxField.Key, ctxField.Val) + } +} + +// IsNil returns true if map is nil. +func (f jsonFields) IsNil() bool { + return f == nil +} + +func encodeField(enc *gojay.Encoder, key string, val interface{}) { + switch vt := val.(type) { + case gojay.MarshalerJSONObject: + enc.AddObjectKey(key, vt) + case gojay.MarshalerJSONArray: + enc.AddArrayKey(key, vt) + case string: + enc.AddStringKey(key, vt) + case error: + enc.AddStringKey(key, vt.Error()) + case bool: + enc.AddBoolKey(key, vt) + case int: + enc.AddIntKey(key, vt) + case int64: + enc.AddInt64Key(key, vt) + case int32: + enc.AddIntKey(key, int(vt)) + case int16: + enc.AddIntKey(key, int(vt)) + case int8: + enc.AddIntKey(key, int(vt)) + case uint64: + enc.AddIntKey(key, int(vt)) + case uint32: + enc.AddIntKey(key, int(vt)) + case uint16: + enc.AddIntKey(key, int(vt)) + case uint8: + enc.AddIntKey(key, int(vt)) + case float64: + enc.AddFloatKey(key, vt) + case float32: + enc.AddFloat32Key(key, vt) + case *gojay.EmbeddedJSON: + enc.AddEmbeddedJSONKey(key, vt) + case time.Time: + enc.AddTimeKey(key, &vt, logr.DefTimestampFormat) + case *time.Time: + enc.AddTimeKey(key, vt, logr.DefTimestampFormat) + default: + s := fmt.Sprintf("%v", vt) + enc.AddStringKey(key, s) + } +} |