diff options
Diffstat (limited to 'vendor/github.com/mattermost/logr/v2/formatters')
3 files changed, 571 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/logr/v2/formatters/gelf.go b/vendor/github.com/mattermost/logr/v2/formatters/gelf.go new file mode 100644 index 00000000..9dece13c --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatters/gelf.go @@ -0,0 +1,152 @@ +package formatters + +import ( + "bytes" + "fmt" + "net" + "os" + "strings" + + "github.com/francoispqt/gojay" + "github.com/mattermost/logr/v2" +) + +const ( + GelfVersion = "1.1" + GelfVersionKey = "version" + GelfHostKey = "host" + GelfShortKey = "short_message" + GelfFullKey = "full_message" + GelfTimestampKey = "timestamp" + GelfLevelKey = "level" +) + +// Gelf formats log records as GELF rcords (https://docs.graylog.org/en/4.0/pages/gelf.html). +type Gelf struct { + // Hostname allows a custom hostname, otherwise os.Hostname is used + Hostname string `json:"hostname"` + + // EnableCaller enables output of the file and line number that emitted a log record. + EnableCaller bool `json:"enable_caller"` + + // FieldSorter allows custom sorting for the context fields. + FieldSorter func(fields []logr.Field) []logr.Field `json:"-"` +} + +func (g *Gelf) CheckValid() error { + return nil +} + +// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field. +func (g *Gelf) IsStacktraceNeeded() bool { + return g.EnableCaller +} + +// Format converts a log record to bytes in GELF format. +func (g *Gelf) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) { + if buf == nil { + buf = &bytes.Buffer{} + } + enc := gojay.BorrowEncoder(buf) + defer func() { + enc.Release() + }() + + gr := gelfRecord{ + LogRec: rec, + Gelf: g, + level: level, + sorter: g.FieldSorter, + } + + err := enc.EncodeObject(gr) + if err != nil { + return nil, err + } + + buf.WriteByte(0) + return buf, nil +} + +type gelfRecord struct { + *logr.LogRec + *Gelf + level logr.Level + sorter func(fields []logr.Field) []logr.Field +} + +// MarshalJSONObject encodes the LogRec as JSON. +func (gr gelfRecord) MarshalJSONObject(enc *gojay.Encoder) { + enc.AddStringKey(GelfVersionKey, GelfVersion) + enc.AddStringKey(GelfHostKey, gr.getHostname()) + enc.AddStringKey(GelfShortKey, gr.Msg()) + + if gr.level.Stacktrace { + frames := gr.StackFrames() + if len(frames) != 0 { + var sbuf strings.Builder + for _, frame := range frames { + fmt.Fprintf(&sbuf, "%s\n %s:%d\n", frame.Function, frame.File, frame.Line) + } + enc.AddStringKey(GelfFullKey, sbuf.String()) + } + } + + secs := float64(gr.Time().UTC().Unix()) + millis := float64(gr.Time().Nanosecond() / 1000000) + ts := secs + (millis / 1000) + enc.AddFloat64Key(GelfTimestampKey, ts) + + enc.AddUint32Key(GelfLevelKey, uint32(gr.level.ID)) + + var fields []logr.Field + if gr.EnableCaller { + caller := logr.Field{ + Key: "_caller", + Type: logr.StringType, + String: gr.LogRec.Caller(), + } + fields = append(fields, caller) + } + + fields = append(fields, gr.Fields()...) + if gr.sorter != nil { + fields = gr.sorter(fields) + } + + if len(fields) > 0 { + for _, field := range fields { + if !strings.HasPrefix("_", field.Key) { + field.Key = "_" + field.Key + } + if err := encodeField(enc, field); err != nil { + enc.AddStringKey(field.Key, fmt.Sprintf("<error encoding field: %v>", err)) + } + } + } +} + +// IsNil returns true if the gelf record pointer is nil. +func (gr gelfRecord) IsNil() bool { + return gr.LogRec == nil +} + +func (g *Gelf) getHostname() string { + if g.Hostname != "" { + return g.Hostname + } + h, err := os.Hostname() + if err == nil { + return h + } + + // get the egress IP by fake dialing any address. UDP ensures no dial. + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return "unknown" + } + defer conn.Close() + + local := conn.LocalAddr().(*net.UDPAddr) + return local.IP.String() +} diff --git a/vendor/github.com/mattermost/logr/v2/formatters/json.go b/vendor/github.com/mattermost/logr/v2/formatters/json.go new file mode 100644 index 00000000..172b9612 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatters/json.go @@ -0,0 +1,273 @@ +package formatters + +import ( + "bytes" + "encoding/json" + "fmt" + "runtime" + "strings" + "sync" + + "github.com/francoispqt/gojay" + "github.com/mattermost/logr/v2" +) + +// JSON formats log records as JSON. +type JSON struct { + // DisableTimestamp disables output of timestamp field. + DisableTimestamp bool `json:"disable_timestamp"` + // DisableLevel disables output of level field. + DisableLevel bool `json:"disable_level"` + // DisableMsg disables output of msg field. + DisableMsg bool `json:"disable_msg"` + // DisableFields disables output of all fields. + DisableFields bool `json:"disable_fields"` + // DisableStacktrace disables output of stack trace. + DisableStacktrace bool `json:"disable_stacktrace"` + // EnableCaller enables output of the file and line number that emitted a log record. + EnableCaller bool `json:"enable_caller"` + + // TimestampFormat is an optional format for timestamps. If empty + // then DefTimestampFormat is used. + TimestampFormat string `json:"timestamp_format"` + + // KeyTimestamp overrides the timestamp field key name. + KeyTimestamp string `json:"key_timestamp"` + + // KeyLevel overrides the level field key name. + KeyLevel string `json:"key_level"` + + // KeyMsg overrides the msg field key name. + KeyMsg string `json:"key_msg"` + + // KeyGroupFields when not empty will group all context fields + // under this key. + KeyGroupFields string `json:"key_group_fields"` + + // KeyStacktrace overrides the stacktrace field key name. + KeyStacktrace string `json:"key_stacktrace"` + + // KeyCaller overrides the caller field key name. + KeyCaller string `json:"key_caller"` + + // FieldSorter allows custom sorting of the fields. If nil then + // no sorting is done. + FieldSorter func(fields []logr.Field) []logr.Field `json:"-"` + + once sync.Once +} + +func (j *JSON) CheckValid() error { + return nil +} + +// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field. +func (j *JSON) IsStacktraceNeeded() bool { + return j.EnableCaller +} + +// Format converts a log record to bytes in JSON format. +func (j *JSON) Format(rec *logr.LogRec, level logr.Level, 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() + }() + + jlr := JSONLogRec{ + LogRec: rec, + JSON: j, + level: level, + sorter: j.FieldSorter, + } + + 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" + } + if j.KeyCaller == "" { + j.KeyCaller = "caller" + } +} + +// JSONLogRec decorates a LogRec adding JSON encoding. +type JSONLogRec struct { + *logr.LogRec + *JSON + level logr.Level + sorter func(fields []logr.Field) []logr.Field +} + +// MarshalJSONObject encodes the LogRec as JSON. +func (jlr JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) { + if !jlr.DisableTimestamp { + timestampFmt := jlr.TimestampFormat + if timestampFmt == "" { + timestampFmt = logr.DefTimestampFormat + } + time := jlr.Time() + enc.AddTimeKey(jlr.KeyTimestamp, &time, timestampFmt) + } + if !jlr.DisableLevel { + enc.AddStringKey(jlr.KeyLevel, jlr.level.Name) + } + if !jlr.DisableMsg { + enc.AddStringKey(jlr.KeyMsg, jlr.Msg()) + } + if jlr.EnableCaller { + enc.AddStringKey(jlr.KeyCaller, jlr.Caller()) + } + if !jlr.DisableFields { + fields := jlr.Fields() + if jlr.sorter != nil { + fields = jlr.sorter(fields) + } + if jlr.KeyGroupFields != "" { + enc.AddObjectKey(jlr.KeyGroupFields, FieldArray(fields)) + } else { + if len(fields) > 0 { + for _, field := range fields { + field = jlr.prefixCollision(field) + if err := encodeField(enc, field); err != nil { + enc.AddStringKey(field.Key, "<error encoding field: "+err.Error()+">") + } + } + } + } + } + if jlr.level.Stacktrace && !jlr.DisableStacktrace { + frames := jlr.StackFrames() + if len(frames) > 0 { + enc.AddArrayKey(jlr.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(field logr.Field) logr.Field { + switch field.Key { + case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace: + f := field + f.Key = "_" + field.Key + return rec.prefixCollision(f) + } + return field +} + +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 FieldArray []logr.Field + +// MarshalJSONObject encodes Fields map to JSON. +func (fa FieldArray) MarshalJSONObject(enc *gojay.Encoder) { + for _, fld := range fa { + if err := encodeField(enc, fld); err != nil { + enc.AddStringKey(fld.Key, "<error encoding field: "+err.Error()+">") + } + } +} + +// IsNil returns true if map is nil. +func (fa FieldArray) IsNil() bool { + return fa == nil +} + +func encodeField(enc *gojay.Encoder, field logr.Field) error { + // first check if the value has a marshaller already. + switch vt := field.Interface.(type) { + case gojay.MarshalerJSONObject: + enc.AddObjectKey(field.Key, vt) + return nil + case gojay.MarshalerJSONArray: + enc.AddArrayKey(field.Key, vt) + return nil + } + + switch field.Type { + case logr.StringType: + enc.AddStringKey(field.Key, field.String) + + case logr.BoolType: + var b bool + if field.Integer != 0 { + b = true + } + enc.AddBoolKey(field.Key, b) + + case logr.StructType, logr.ArrayType, logr.MapType, logr.UnknownType: + b, err := json.Marshal(field.Interface) + if err != nil { + return err + } + embed := gojay.EmbeddedJSON(b) + enc.AddEmbeddedJSONKey(field.Key, &embed) + + case logr.StringerType, logr.ErrorType, logr.TimestampMillisType, logr.TimeType, logr.DurationType, logr.BinaryType: + var buf strings.Builder + _ = field.ValueString(&buf, nil) + enc.AddStringKey(field.Key, buf.String()) + + case logr.Int64Type, logr.Int32Type, logr.IntType: + enc.AddInt64Key(field.Key, field.Integer) + + case logr.Uint64Type, logr.Uint32Type, logr.UintType: + enc.AddUint64Key(field.Key, uint64(field.Integer)) + + case logr.Float64Type, logr.Float32Type: + enc.AddFloat64Key(field.Key, field.Float) + + default: + return fmt.Errorf("invalid field type: %d", field.Type) + } + return nil +} diff --git a/vendor/github.com/mattermost/logr/v2/formatters/plain.go b/vendor/github.com/mattermost/logr/v2/formatters/plain.go new file mode 100644 index 00000000..4d8af643 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatters/plain.go @@ -0,0 +1,146 @@ +package formatters + +import ( + "bytes" + "fmt" + "strings" + + "github.com/mattermost/logr/v2" +) + +// Plain is the simplest formatter, outputting only text with +// no colors. +type Plain struct { + // DisableTimestamp disables output of timestamp field. + DisableTimestamp bool `json:"disable_timestamp"` + // DisableLevel disables output of level field. + DisableLevel bool `json:"disable_level"` + // DisableMsg disables output of msg field. + DisableMsg bool `json:"disable_msg"` + // DisableFields disables output of all fields. + DisableFields bool `json:"disable_fields"` + // DisableStacktrace disables output of stack trace. + DisableStacktrace bool `json:"disable_stacktrace"` + // EnableCaller enables output of the file and line number that emitted a log record. + EnableCaller bool `json:"enable_caller"` + + // Delim is an optional delimiter output between each log field. + // Defaults to a single space. + Delim string `json:"delim"` + + // MinLevelLen sets the minimum level name length. If the level name is less + // than the minimum it will be padded with spaces. + MinLevelLen int `json:"min_level_len"` + + // MinMessageLen sets the minimum msg length. If the msg text is less + // than the minimum it will be padded with spaces. + MinMessageLen int `json:"min_msg_len"` + + // TimestampFormat is an optional format for timestamps. If empty + // then DefTimestampFormat is used. + TimestampFormat string `json:"timestamp_format"` + + // LineEnd sets the end of line character(s). Defaults to '\n'. + LineEnd string `json:"line_end"` + + // EnableColor sets whether output should include color. + EnableColor bool `json:"enable_color"` +} + +func (p *Plain) CheckValid() error { + if p.MinMessageLen < 0 || p.MinMessageLen > 1024 { + return fmt.Errorf("min_msg_len is invalid(%d)", p.MinMessageLen) + } + return nil +} + +// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field. +func (p *Plain) IsStacktraceNeeded() bool { + return p.EnableCaller +} + +// Format converts a log record to bytes. +func (p *Plain) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) { + delim := p.Delim + if delim == "" { + delim = " " + } + if buf == nil { + buf = &bytes.Buffer{} + } + + timestampFmt := p.TimestampFormat + if timestampFmt == "" { + timestampFmt = logr.DefTimestampFormat + } + + color := logr.NoColor + if p.EnableColor { + color = level.Color + } + + if !p.DisableLevel { + _ = logr.WriteWithColor(buf, level.Name, color) + count := len(level.Name) + if p.MinLevelLen > count { + _, _ = buf.WriteString(strings.Repeat(" ", p.MinLevelLen-count)) + } + buf.WriteString(delim) + } + + if !p.DisableTimestamp { + var arr [128]byte + tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt) + buf.WriteByte('[') + buf.Write(tbuf) + buf.WriteByte(']') + buf.WriteString(delim) + } + + if !p.DisableMsg { + count, _ := buf.WriteString(rec.Msg()) + if p.MinMessageLen > count { + _, _ = buf.WriteString(strings.Repeat(" ", p.MinMessageLen-count)) + } + _, _ = buf.WriteString(delim) + } + + var fields []logr.Field + + if p.EnableCaller { + fld := logr.Field{ + Key: "caller", + Type: logr.StringType, + String: rec.Caller(), + } + fields = append(fields, fld) + } + + if !p.DisableFields { + fields = append(fields, rec.Fields()...) + } + + if len(fields) > 0 { + if err := logr.WriteFields(buf, fields, logr.Space, color); err != nil { + return nil, err + } + } + + if level.Stacktrace && !p.DisableStacktrace { + frames := rec.StackFrames() + if len(frames) > 0 { + buf.WriteString("\n") + if err := logr.WriteStacktrace(buf, rec.StackFrames()); err != nil { + return nil, err + } + } + } + + if p.LineEnd == "" { + buf.WriteString("\n") + } else { + buf.WriteString(p.LineEnd) + } + + return buf, nil +} |