summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/logr/format/json.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/logr/format/json.go')
-rw-r--r--vendor/github.com/mattermost/logr/format/json.go273
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)
+ }
+}