summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-logger/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/codeberg.org/gruf/go-logger/format.go')
-rw-r--r--vendor/codeberg.org/gruf/go-logger/format.go640
1 files changed, 640 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-logger/format.go b/vendor/codeberg.org/gruf/go-logger/format.go
new file mode 100644
index 000000000..214773ff3
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-logger/format.go
@@ -0,0 +1,640 @@
+package logger
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "time"
+
+ "codeberg.org/gruf/go-bytes"
+)
+
+// Check our types impl LogFormat
+var _ LogFormat = &TextFormat{}
+
+// LogFormat defines a method of formatting log entries
+type LogFormat interface {
+ AppendLevel(buf *bytes.Buffer, lvl LEVEL)
+ AppendTimestamp(buf *bytes.Buffer, fmtNow string)
+ AppendField(buf *bytes.Buffer, key string, value interface{})
+ AppendFields(buf *bytes.Buffer, fields map[string]interface{})
+ AppendByteField(buf *bytes.Buffer, key string, value byte)
+ AppendBytesField(buf *bytes.Buffer, key string, value []byte)
+ AppendStringField(buf *bytes.Buffer, key string, value string)
+ AppendStringsField(buf *bytes.Buffer, key string, value []string)
+ AppendBoolField(buf *bytes.Buffer, key string, value bool)
+ AppendBoolsField(buf *bytes.Buffer, key string, value []bool)
+ AppendIntField(buf *bytes.Buffer, key string, value int)
+ AppendIntsField(buf *bytes.Buffer, key string, value []int)
+ AppendUintField(buf *bytes.Buffer, key string, value uint)
+ AppendUintsField(buf *bytes.Buffer, key string, value []uint)
+ AppendFloatField(buf *bytes.Buffer, key string, value float64)
+ AppendFloatsField(buf *bytes.Buffer, key string, value []float64)
+ AppendTimeField(buf *bytes.Buffer, key string, value time.Time)
+ AppendTimesField(buf *bytes.Buffer, key string, value []time.Time)
+ AppendDurationField(buf *bytes.Buffer, key string, value time.Duration)
+ AppendDurationsField(buf *bytes.Buffer, key string, value []time.Duration)
+ AppendMsg(buf *bytes.Buffer, a ...interface{})
+ AppendMsgf(buf *bytes.Buffer, s string, a ...interface{})
+}
+
+// TextFormat is the default LogFormat implementation, with very similar formatting to logfmt
+type TextFormat struct {
+ // Strict defines whether to use strict key-value pair formatting,
+ // i.e. should the level, timestamp and msg be formatted as key-value pairs
+ // or simply be printed as-is
+ Strict bool
+
+ // Levels defines the map of log LEVELs to level strings this LogFormat will use
+ Levels Levels
+}
+
+// NewLogFmt returns a newly set LogFmt object, with DefaultLevels() set
+func NewLogFmt(strict bool) *TextFormat {
+ return &TextFormat{
+ Strict: strict,
+ Levels: DefaultLevels(),
+ }
+}
+
+// appendReflectValue will safely append a reflected value
+func appendReflectValue(buf *bytes.Buffer, v reflect.Value, isKey bool) {
+ switch v.Kind() {
+ case reflect.Slice:
+ appendSliceType(buf, v)
+ case reflect.Map:
+ appendMapType(buf, v)
+ case reflect.Struct:
+ appendStructType(buf, v)
+ case reflect.Ptr:
+ if v.IsNil() {
+ appendNil(buf)
+ } else {
+ appendIface(buf, v.Elem().Interface(), isKey)
+ }
+ default:
+ // Just print reflect string
+ appendString(buf, v.String())
+ }
+}
+
+// appendKey should only be used in the case of directly setting key-value pairs,
+// not in the case of appendMapType, appendStructType
+func appendKey(buf *bytes.Buffer, key string) {
+ if len(key) > 0 {
+ // Only write key if here
+ appendStringUnquoted(buf, key)
+ buf.WriteByte('=')
+ }
+}
+
+// appendSlice performs provided fn and writes square brackets [] around it
+func appendSlice(buf *bytes.Buffer, fn func()) {
+ buf.WriteByte('[')
+ fn()
+ buf.WriteByte(']')
+}
+
+// appendMap performs provided fn and writes curly braces {} around it
+func appendMap(buf *bytes.Buffer, fn func()) {
+ buf.WriteByte('{')
+ fn()
+ buf.WriteByte('}')
+}
+
+// appendStruct performs provided fn and writes curly braces {} around it
+func appendStruct(buf *bytes.Buffer, fn func()) {
+ buf.WriteByte('{')
+ fn()
+ buf.WriteByte('}')
+}
+
+// appendNil writes a nil value placeholder to buf
+func appendNil(buf *bytes.Buffer) {
+ buf.WriteString(`<nil>`)
+}
+
+// appendByte writes a single byte to buf
+func appendByte(buf *bytes.Buffer, b byte) {
+ buf.WriteByte(b)
+}
+
+// appendBytes writes a quoted byte slice to buf
+func appendBytes(buf *bytes.Buffer, b []byte) {
+ buf.WriteByte('"')
+ buf.Write(b)
+ buf.WriteByte('"')
+}
+
+// appendBytesUnquoted writes a byte slice to buf as-is
+func appendBytesUnquoted(buf *bytes.Buffer, b []byte) {
+ buf.Write(b)
+}
+
+// appendString writes a quoted string to buf
+func appendString(buf *bytes.Buffer, s string) {
+ buf.WriteByte('"')
+ buf.WriteString(s)
+ buf.WriteByte('"')
+}
+
+// appendStringUnquoted writes a string as-is to buf
+func appendStringUnquoted(buf *bytes.Buffer, s string) {
+ buf.WriteString(s)
+}
+
+// appendStringSlice writes a slice of strings to buf
+func appendStringSlice(buf *bytes.Buffer, s []string) {
+ appendSlice(buf, func() {
+ for _, s := range s {
+ appendString(buf, s)
+ buf.WriteByte(',')
+ }
+ if len(s) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendBool writes a formatted bool to buf
+func appendBool(buf *bytes.Buffer, b bool) {
+ buf.B = strconv.AppendBool(buf.B, b)
+}
+
+// appendBool writes a slice of formatted bools to buf
+func appendBoolSlice(buf *bytes.Buffer, b []bool) {
+ appendSlice(buf, func() {
+ // Write elements
+ for _, b := range b {
+ appendBool(buf, b)
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if len(b) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendInt writes a formatted int to buf
+func appendInt(buf *bytes.Buffer, i int64) {
+ buf.B = strconv.AppendInt(buf.B, i, 10)
+}
+
+// appendIntSlice writes a slice of formatted int to buf
+func appendIntSlice(buf *bytes.Buffer, i []int) {
+ appendSlice(buf, func() {
+ // Write elements
+ for _, i := range i {
+ appendInt(buf, int64(i))
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if len(i) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendUint writes a formatted uint to buf
+func appendUint(buf *bytes.Buffer, u uint64) {
+ buf.B = strconv.AppendUint(buf.B, u, 10)
+}
+
+// appendUintSlice writes a slice of formatted uint to buf
+func appendUintSlice(buf *bytes.Buffer, u []uint) {
+ appendSlice(buf, func() {
+ // Write elements
+ for _, u := range u {
+ appendUint(buf, uint64(u))
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if len(u) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendFloat writes a formatted float to buf
+func appendFloat(buf *bytes.Buffer, f float64) {
+ buf.B = strconv.AppendFloat(buf.B, f, 'G', -1, 64)
+}
+
+// appendFloatSlice writes a slice formatted floats to buf
+func appendFloatSlice(buf *bytes.Buffer, f []float64) {
+ appendSlice(buf, func() {
+ // Write elements
+ for _, f := range f {
+ appendFloat(buf, f)
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if len(f) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendTime writes a formatted, quoted time string to buf
+func appendTime(buf *bytes.Buffer, t time.Time) {
+ buf.WriteByte('"')
+ buf.B = t.AppendFormat(buf.B, time.RFC1123)
+ buf.WriteByte('"')
+}
+
+// appendTimeUnquoted writes a formatted time string to buf as-is
+func appendTimeUnquoted(buf *bytes.Buffer, t time.Time) {
+ buf.B = t.AppendFormat(buf.B, time.RFC1123)
+}
+
+// appendTimeSlice writes a slice of formatted time strings to buf
+func appendTimeSlice(buf *bytes.Buffer, t []time.Time) {
+ appendSlice(buf, func() {
+ // Write elements
+ for _, t := range t {
+ appendTime(buf, t)
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if len(t) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendDuration writes a formatted, quoted duration string to buf
+func appendDuration(buf *bytes.Buffer, d time.Duration) {
+ appendString(buf, d.String())
+}
+
+// appendDurationUnquoted writes a formatted duration string to buf as-is
+func appendDurationUnquoted(buf *bytes.Buffer, d time.Duration) {
+ appendStringUnquoted(buf, d.String())
+}
+
+// appendDurationSlice writes a slice of formatted, quoted duration strings to buf
+func appendDurationSlice(buf *bytes.Buffer, d []time.Duration) {
+ appendSlice(buf, func() {
+ // Write elements
+ for _, d := range d {
+ appendString(buf, d.String())
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if len(d) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendIface parses and writes a formatted interface value to buf
+func appendIface(buf *bytes.Buffer, i interface{}, isKey bool) {
+ switch i := i.(type) {
+ case nil:
+ appendNil(buf)
+ case byte:
+ appendByte(buf, i)
+ case []byte:
+ if isKey {
+ // Keys don't get quoted
+ appendBytesUnquoted(buf, i)
+ } else {
+ appendBytes(buf, i)
+ }
+ case string:
+ if isKey {
+ // Keys don't get quoted
+ appendStringUnquoted(buf, i)
+ } else {
+ appendString(buf, i)
+ }
+ case []string:
+ appendStringSlice(buf, i)
+ case int:
+ appendInt(buf, int64(i))
+ case int8:
+ appendInt(buf, int64(i))
+ case int16:
+ appendInt(buf, int64(i))
+ case int32:
+ appendInt(buf, int64(i))
+ case int64:
+ appendInt(buf, i)
+ case []int:
+ appendIntSlice(buf, i)
+ case uint:
+ appendUint(buf, uint64(i))
+ case uint16:
+ appendUint(buf, uint64(i))
+ case uint32:
+ appendUint(buf, uint64(i))
+ case uint64:
+ appendUint(buf, i)
+ case []uint:
+ appendUintSlice(buf, i)
+ case float32:
+ appendFloat(buf, float64(i))
+ case float64:
+ appendFloat(buf, i)
+ case []float64:
+ appendFloatSlice(buf, i)
+ case bool:
+ appendBool(buf, i)
+ case []bool:
+ appendBoolSlice(buf, i)
+ case time.Time:
+ if isKey {
+ // Keys don't get quoted
+ appendTimeUnquoted(buf, i)
+ } else {
+ appendTime(buf, i)
+ }
+ case *time.Time:
+ if isKey {
+ // Keys don't get quoted
+ appendTimeUnquoted(buf, *i)
+ } else {
+ appendTime(buf, *i)
+ }
+ case []time.Time:
+ appendTimeSlice(buf, i)
+ case time.Duration:
+ if isKey {
+ // Keys dont get quoted
+ appendDurationUnquoted(buf, i)
+ } else {
+ appendDuration(buf, i)
+ }
+ case []time.Duration:
+ appendDurationSlice(buf, i)
+ case map[string]interface{}:
+ appendIfaceMap(buf, i)
+ case error:
+ if isKey {
+ // Keys don't get quoted
+ appendStringUnquoted(buf, i.Error())
+ } else {
+ appendString(buf, i.Error())
+ }
+ case fmt.Stringer:
+ if isKey {
+ // Keys don't get quoted
+ appendStringUnquoted(buf, i.String())
+ } else {
+ appendString(buf, i.String())
+ }
+ default:
+ // If we reached here, we need reflection.
+ appendReflectValue(buf, reflect.ValueOf(i), isKey)
+ }
+}
+
+// appendIfaceMap writes a map of key-value pairs (as a set of fields) to buf
+func appendIfaceMap(buf *bytes.Buffer, v map[string]interface{}) {
+ appendMap(buf, func() {
+ // Write map pairs!
+ for key, value := range v {
+ appendStringUnquoted(buf, key)
+ buf.WriteByte('=')
+ appendIface(buf, value, false)
+ buf.WriteByte(' ')
+ }
+
+ // Drop last space
+ if len(v) > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendSliceType writes a slice of unknown type (parsed by reflection) to buf
+func appendSliceType(buf *bytes.Buffer, v reflect.Value) {
+ n := v.Len()
+ appendSlice(buf, func() {
+ for i := 0; i < n; i++ {
+ appendIface(buf, v.Index(i).Interface(), false)
+ buf.WriteByte(',')
+ }
+
+ // Drop last comma
+ if n > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendMapType writes a map of unknown types (parsed by reflection) to buf
+func appendMapType(buf *bytes.Buffer, v reflect.Value) {
+ // Get a map iterator
+ r := v.MapRange()
+ n := v.Len()
+
+ // Now begin creating the map!
+ appendMap(buf, func() {
+ // Iterate pairs
+ for r.Next() {
+ appendIface(buf, r.Key().Interface(), true)
+ buf.WriteByte('=')
+ appendIface(buf, r.Value().Interface(), false)
+ buf.WriteByte(' ')
+ }
+
+ // Drop last space
+ if n > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+// appendStructType writes a struct (as a set of key-value fields) to buf
+func appendStructType(buf *bytes.Buffer, v reflect.Value) {
+ // Get value type & no. fields
+ t := v.Type()
+ n := v.NumField()
+ w := 0
+
+ // Iter and write struct fields
+ appendStruct(buf, func() {
+ for i := 0; i < n; i++ {
+ vfield := v.Field(i)
+
+ // Check for unexported fields
+ if !vfield.CanInterface() {
+ continue
+ }
+
+ // Append the struct member as field key-value
+ appendStringUnquoted(buf, t.Field(i).Name)
+ buf.WriteByte('=')
+ appendIface(buf, vfield.Interface(), false)
+ buf.WriteByte(' ')
+
+ // Iter written count
+ w++
+ }
+
+ // Drop last space
+ if w > 0 {
+ buf.Truncate(1)
+ }
+ })
+}
+
+func (f *TextFormat) AppendLevel(buf *bytes.Buffer, lvl LEVEL) {
+ if f.Strict {
+ // Strict format, append level key
+ buf.WriteString(`level=`)
+ buf.WriteString(f.Levels.LevelString(lvl))
+ return
+ }
+
+ // Write level string
+ buf.WriteByte('[')
+ buf.WriteString(f.Levels.LevelString(lvl))
+ buf.WriteByte(']')
+}
+
+func (f *TextFormat) AppendTimestamp(buf *bytes.Buffer, now string) {
+ if f.Strict {
+ // Strict format, use key and quote
+ appendStringUnquoted(buf, `time`)
+ buf.WriteByte('=')
+ appendString(buf, now)
+ return
+ }
+
+ // Write time as-is
+ appendStringUnquoted(buf, now)
+}
+
+func (f *TextFormat) AppendField(buf *bytes.Buffer, key string, value interface{}) {
+ appendKey(buf, key)
+ appendIface(buf, value, false)
+}
+
+func (f *TextFormat) AppendFields(buf *bytes.Buffer, fields map[string]interface{}) {
+ // Append individual fields
+ for key, value := range fields {
+ appendKey(buf, key)
+ appendIface(buf, value, false)
+ buf.WriteByte(' ')
+ }
+
+ // Drop last space
+ if len(fields) > 0 {
+ buf.Truncate(1)
+ }
+}
+
+func (f *TextFormat) AppendByteField(buf *bytes.Buffer, key string, value byte) {
+ appendKey(buf, key)
+ appendByte(buf, value)
+}
+
+func (f *TextFormat) AppendBytesField(buf *bytes.Buffer, key string, value []byte) {
+ appendKey(buf, key)
+ appendBytes(buf, value)
+}
+
+func (f *TextFormat) AppendStringField(buf *bytes.Buffer, key string, value string) {
+ appendKey(buf, key)
+ appendString(buf, value)
+}
+
+func (f *TextFormat) AppendStringsField(buf *bytes.Buffer, key string, value []string) {
+ appendKey(buf, key)
+ appendStringSlice(buf, value)
+}
+
+func (f *TextFormat) AppendBoolField(buf *bytes.Buffer, key string, value bool) {
+ appendKey(buf, key)
+ appendBool(buf, value)
+}
+
+func (f *TextFormat) AppendBoolsField(buf *bytes.Buffer, key string, value []bool) {
+ appendKey(buf, key)
+ appendBoolSlice(buf, value)
+}
+
+func (f *TextFormat) AppendIntField(buf *bytes.Buffer, key string, value int) {
+ appendKey(buf, key)
+ appendInt(buf, int64(value))
+}
+
+func (f *TextFormat) AppendIntsField(buf *bytes.Buffer, key string, value []int) {
+ appendKey(buf, key)
+ appendIntSlice(buf, value)
+}
+
+func (f *TextFormat) AppendUintField(buf *bytes.Buffer, key string, value uint) {
+ appendKey(buf, key)
+ appendUint(buf, uint64(value))
+}
+
+func (f *TextFormat) AppendUintsField(buf *bytes.Buffer, key string, value []uint) {
+ appendKey(buf, key)
+ appendUintSlice(buf, value)
+}
+
+func (f *TextFormat) AppendFloatField(buf *bytes.Buffer, key string, value float64) {
+ appendKey(buf, key)
+ appendFloat(buf, value)
+}
+
+func (f *TextFormat) AppendFloatsField(buf *bytes.Buffer, key string, value []float64) {
+ appendKey(buf, key)
+ appendFloatSlice(buf, value)
+}
+
+func (f *TextFormat) AppendTimeField(buf *bytes.Buffer, key string, value time.Time) {
+ appendKey(buf, key)
+ appendTime(buf, value)
+}
+
+func (f *TextFormat) AppendTimesField(buf *bytes.Buffer, key string, value []time.Time) {
+ appendKey(buf, key)
+ appendTimeSlice(buf, value)
+}
+
+func (f *TextFormat) AppendDurationField(buf *bytes.Buffer, key string, value time.Duration) {
+ appendKey(buf, key)
+ appendDuration(buf, value)
+}
+
+func (f *TextFormat) AppendDurationsField(buf *bytes.Buffer, key string, value []time.Duration) {
+ appendKey(buf, key)
+ appendDurationSlice(buf, value)
+}
+
+func (f *TextFormat) AppendMsg(buf *bytes.Buffer, a ...interface{}) {
+ if f.Strict {
+ // Strict format, use key and quote
+ buf.WriteString(`msg="`)
+ fmt.Fprint(buf, a...)
+ buf.WriteByte('"')
+ return
+ }
+
+ // Write message as-is
+ fmt.Fprint(buf, a...)
+}
+
+func (f *TextFormat) AppendMsgf(buf *bytes.Buffer, s string, a ...interface{}) {
+ if f.Strict {
+ // Strict format, use key and quote
+ buf.WriteString(`msg="`)
+ fmt.Fprintf(buf, s, a...)
+ buf.WriteByte('"')
+ return
+ }
+
+ // Write message as-is
+ fmt.Fprintf(buf, s, a...)
+}