Browse Source
Reviewed-on: https://gitoa.ru/go-4devs/log/pulls/8 Co-authored-by: andrey <andrey@4devs.io> Co-committed-by: andrey <andrey@4devs.io>pull/9/head v0.5.0
andrey
11 months ago
30 changed files with 1778 additions and 1710 deletions
@ -1,32 +1,281 @@ |
|||||
|
//nolint:gomnd
|
||||
package field |
package field |
||||
|
|
||||
import "time" |
import ( |
||||
|
"fmt" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
"unicode" |
||||
|
"unicode/utf8" |
||||
|
) |
||||
|
|
||||
//nolint:interfacebloat
|
|
||||
type Encoder interface { |
type Encoder interface { |
||||
// Built-in types.
|
AppendField(dst []byte, field Field) []byte |
||||
AddArray(key string, value Value) |
AppendValue(dst []byte, val Value) []byte |
||||
AddAny(key string, value Value) |
} |
||||
AddNil(key string) |
|
||||
AddBool(key string, value bool) |
func WithAppendString(fn func(dst []byte, in string) []byte) func(*BaseEncoder) { |
||||
AddBinary(key string, value []byte) |
return func(be *BaseEncoder) { |
||||
AddInt(key string, value int) |
be.AppendString = fn |
||||
AddInt8(key string, value int8) |
} |
||||
AddInt16(key string, value int16) |
} |
||||
AddInt32(key string, value int32) |
|
||||
AddInt64(key string, value int64) |
func WithNullValue(in string) func(*BaseEncoder) { |
||||
AddUint(key string, value uint) |
return func(be *BaseEncoder) { |
||||
AddUint8(key string, value uint8) |
be.nullValue = []byte(in) |
||||
AddUint16(key string, value uint16) |
} |
||||
AddUint32(key string, value uint32) |
} |
||||
AddUint64(key string, value uint64) |
|
||||
AddUintptr(key string, value uintptr) |
func WithDelimeter(in byte) func(*BaseEncoder) { |
||||
AddTime(key string, value time.Time) |
return func(be *BaseEncoder) { |
||||
AddDuration(key string, value time.Duration) |
be.delimeter = in |
||||
AddFloat32(key string, value float32) |
} |
||||
AddFloat64(key string, value float64) |
} |
||||
AddComplex64(key string, value complex64) |
|
||||
AddComplex128(key string, value complex128) |
func WithGropuConfig(start, end, deli byte) func(*BaseEncoder) { |
||||
AddString(key, value string) |
return func(be *BaseEncoder) { |
||||
AddError(key string, value error) |
be.group = groupConfig{ |
||||
|
start: start, |
||||
|
end: end, |
||||
|
deli: deli, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func WithDefaultValue(fn func(dst []byte, e Encoder, val Value) []byte) func(*BaseEncoder) { |
||||
|
return func(be *BaseEncoder) { |
||||
|
be.DefaultValue = fn |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func NewEncoder(opts ...func(*BaseEncoder)) BaseEncoder { |
||||
|
be := BaseEncoder{ |
||||
|
nullValue: []byte("null"), |
||||
|
group: groupConfig{ |
||||
|
start: '{', |
||||
|
end: '}', |
||||
|
deli: ',', |
||||
|
}, |
||||
|
array: groupConfig{ |
||||
|
start: '[', |
||||
|
end: ']', |
||||
|
deli: ',', |
||||
|
}, |
||||
|
timeFormat: time.RFC3339, |
||||
|
AppendString: AppendString, |
||||
|
delimeter: '=', |
||||
|
DefaultValue: func(dst []byte, e Encoder, val Value) []byte { |
||||
|
return e.AppendValue(dst, StringValue(fmt.Sprintf("%+v", val.Any()))) |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
for _, opt := range opts { |
||||
|
opt(&be) |
||||
|
} |
||||
|
|
||||
|
return be |
||||
|
} |
||||
|
|
||||
|
type groupConfig struct { |
||||
|
start byte |
||||
|
end byte |
||||
|
deli byte |
||||
|
} |
||||
|
|
||||
|
type BaseEncoder struct { |
||||
|
nullValue []byte |
||||
|
group groupConfig |
||||
|
array groupConfig |
||||
|
timeFormat string |
||||
|
AppendString func(dst []byte, in string) []byte |
||||
|
delimeter byte |
||||
|
DefaultValue func(dst []byte, e Encoder, val Value) []byte |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendValue(dst []byte, val Value) []byte { |
||||
|
return b.appendValue(dst, val, "", 0) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendDelimiter(dst []byte, deli byte) []byte { |
||||
|
if deli == 0 { |
||||
|
return dst |
||||
|
} |
||||
|
|
||||
|
return append(dst, deli) |
||||
|
} |
||||
|
|
||||
|
//nolint:gocyclo,cyclop
|
||||
|
func (b BaseEncoder) appendValue(dst []byte, val Value, prefix string, deli byte) []byte { |
||||
|
switch val.Kind { |
||||
|
case KindGroup: |
||||
|
return b.appendGroup(dst, val.AsGroup(), prefix) |
||||
|
case KindClosure: |
||||
|
return b.appendValue(dst, AnyValue(val.Resolve()), prefix, deli) |
||||
|
case KindArray: |
||||
|
return b.AppendArray(b.AppendDelimiter(dst, deli), val.AsArray()) |
||||
|
case KindNil: |
||||
|
return b.AppendNull(b.AppendDelimiter(dst, deli)) |
||||
|
case KindBool: |
||||
|
return b.AppendBool(b.AppendDelimiter(dst, deli), val.AsBool()) |
||||
|
case KindBinary: |
||||
|
return b.AppendBytes(b.AppendDelimiter(dst, deli), val.AsBinary()) |
||||
|
case KindComplex128: |
||||
|
return b.AppendComplex(b.AppendDelimiter(dst, deli), val.AsComplex128()) |
||||
|
case KindInt64: |
||||
|
return b.AppendInt(b.AppendDelimiter(dst, deli), val.AsInt64()) |
||||
|
case KindFloat32: |
||||
|
return b.AppendFloat(b.AppendDelimiter(dst, deli), float64(val.AsFloat32()), 32) |
||||
|
case KindFloat64: |
||||
|
return b.AppendFloat(b.AppendDelimiter(dst, deli), val.AsFloat64(), 64) |
||||
|
case KindUint64: |
||||
|
return b.AppendUint(b.AppendDelimiter(dst, deli), val.AsUint64()) |
||||
|
case KindError: |
||||
|
return b.AppendString(b.AppendDelimiter(dst, deli), val.AsError().Error()) |
||||
|
case KindString: |
||||
|
return b.AppendString(b.AppendDelimiter(dst, deli), val.AsString()) |
||||
|
case KindDuration: |
||||
|
return b.AppendDuration(b.AppendDelimiter(dst, deli), val.AsDuration()) |
||||
|
case KindTime: |
||||
|
return b.AppendTime(b.AppendDelimiter(dst, deli), val.AsTime()) |
||||
|
case KindAny: |
||||
|
return b.DefaultValue(b.AppendDelimiter(dst, deli), b, val) |
||||
|
} |
||||
|
|
||||
|
return b.DefaultValue(b.AppendDelimiter(dst, deli), b, val) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendDuration(dst []byte, d time.Duration) []byte { |
||||
|
return b.AppendString(dst, d.String()) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendTime(dst []byte, t time.Time) []byte { |
||||
|
return b.AppendString(dst, t.Format(b.timeFormat)) |
||||
|
} |
||||
|
|
||||
|
func AppendString(dst []byte, in string) []byte { |
||||
|
if needsQuoting(in) { |
||||
|
return strconv.AppendQuote(dst, in) |
||||
|
} |
||||
|
|
||||
|
return append(dst, in...) |
||||
|
} |
||||
|
|
||||
|
//nolint:cyclop
|
||||
|
func needsQuoting(in string) bool { |
||||
|
if len(in) == 0 { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
for i := 0; i < len(in); { |
||||
|
char := in[i] |
||||
|
if char < utf8.RuneSelf { |
||||
|
// Quote anything except a backslash that would need quoting in a
|
||||
|
// JSON string, as well as space and '='
|
||||
|
if char != '\\' && (char == ' ' || char == '=' || !safeSet[char]) { |
||||
|
return true |
||||
|
} |
||||
|
i++ |
||||
|
|
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
decodeRune, size := utf8.DecodeRuneInString(in[i:]) |
||||
|
if decodeRune == utf8.RuneError || unicode.IsSpace(decodeRune) || !unicode.IsPrint(decodeRune) { |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
i += size |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendField(dst []byte, field Field) []byte { |
||||
|
prefix := "" |
||||
|
|
||||
|
if len(dst) != 0 { |
||||
|
prew := dst[len(dst)-1] |
||||
|
if prew != '{' && prew != '.' { |
||||
|
prefix = string(b.group.deli) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return b.appendField(dst, field, prefix, b.delimeter) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) appendField(dst []byte, field Field, prefix string, deli byte) []byte { |
||||
|
dst = b.AppendKey(dst, field.Key, prefix) |
||||
|
|
||||
|
return b.appendValue(dst, field.Value, field.Key+".", deli) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendKey(dst []byte, key string, prefix string) []byte { |
||||
|
if prefix != "" { |
||||
|
dst = append(dst, prefix...) |
||||
|
} |
||||
|
|
||||
|
return b.AppendString(dst, key) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendComplex(dst []byte, c complex128) []byte { |
||||
|
cmplx := strconv.FormatComplex(c, 'g', -1, 128) |
||||
|
|
||||
|
return b.AppendString(dst, cmplx) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendFloat(dst []byte, f float64, bitSize int) []byte { |
||||
|
return strconv.AppendFloat(dst, f, 'g', -1, bitSize) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendUint(dst []byte, u uint64) []byte { |
||||
|
return strconv.AppendUint(dst, u, 10) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendNull(dst []byte) []byte { |
||||
|
return append(dst, b.nullValue...) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendInt(dst []byte, val int64) []byte { |
||||
|
return strconv.AppendInt(dst, val, 10) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendBool(dst []byte, val bool) []byte { |
||||
|
return strconv.AppendBool(dst, val) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendGroup(dst []byte, fields []Field) []byte { |
||||
|
dst = append(dst, b.group.start) |
||||
|
dst = b.appendGroup(dst, fields, "") |
||||
|
|
||||
|
return append(dst, b.group.end) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) appendGroup(dst []byte, fields []Field, prefix string) []byte { |
||||
|
if len(fields) > 0 { |
||||
|
dst = b.appendField(dst, fields[0], ".", b.delimeter) |
||||
|
for _, field := range fields[1:] { |
||||
|
dst = b.appendField(append(dst, b.group.deli), field, prefix, b.delimeter) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return dst |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendArray(dst []byte, in []Value) []byte { |
||||
|
dst = append(dst, b.array.start) |
||||
|
if len(in) > 0 { |
||||
|
dst = b.appendValue(dst, in[0], "", 0) |
||||
|
for _, value := range in[1:] { |
||||
|
dst = b.appendValue(append(dst, b.array.deli), value, "", 0) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return append(dst, b.array.end) |
||||
|
} |
||||
|
|
||||
|
func (b BaseEncoder) AppendBytes(dst, in []byte) []byte { |
||||
|
dst = append(dst, '"') |
||||
|
dst = append(dst, in...) |
||||
|
|
||||
|
return append(dst, '"') |
||||
} |
} |
||||
|
@ -0,0 +1,23 @@ |
|||||
|
package field |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"strconv" |
||||
|
) |
||||
|
|
||||
|
func NewEncoderJSON(opts ...func(*BaseEncoder)) BaseEncoder { |
||||
|
opts = append([]func(*BaseEncoder){ |
||||
|
WithAppendString(strconv.AppendQuote), |
||||
|
WithDelimeter(':'), |
||||
|
WithDefaultValue(func(dst []byte, e Encoder, val Value) []byte { |
||||
|
js, err := json.Marshal(val.Any()) |
||||
|
if err != nil { |
||||
|
return e.AppendValue(dst, ErrorValue(err)) |
||||
|
} |
||||
|
|
||||
|
return append(dst, js...) |
||||
|
}), |
||||
|
}, opts...) |
||||
|
|
||||
|
return NewEncoder(opts...) |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package field_test |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
|
||||
|
"gitoa.ru/go-4devs/log/field" |
||||
|
"gitoa.ru/go-4devs/log/internal/buffer" |
||||
|
) |
||||
|
|
||||
|
func TestEncoderJSONAppendField_string(t *testing.T) { |
||||
|
t.Parallel() |
||||
|
|
||||
|
const expect = `"array":["value","other"],"str":"value","nullableStr":"value","nullstr":null` |
||||
|
|
||||
|
encode := field.NewEncoderJSON() |
||||
|
|
||||
|
buf := buffer.New() |
||||
|
defer func() { |
||||
|
buf.Free() |
||||
|
}() |
||||
|
|
||||
|
val := "value" |
||||
|
strs := field.Strings("array", val, "other") |
||||
|
*buf = encode.AppendField(*buf, strs) |
||||
|
|
||||
|
str := field.String("str", val) |
||||
|
*buf = encode.AppendField(*buf, str) |
||||
|
|
||||
|
strp := field.Stringp("nullableStr", &val) |
||||
|
*buf = encode.AppendField(*buf, strp) |
||||
|
|
||||
|
nullStr := field.Stringp("nullstr", nil) |
||||
|
*buf = encode.AppendField(*buf, nullStr) |
||||
|
|
||||
|
if buf.String() != expect { |
||||
|
t.Errorf("json string expect:%v got:%s", expect, buf) |
||||
|
} |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
package field |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
) |
||||
|
|
||||
|
func NewEncoderText(opts ...func(*BaseEncoder)) BaseEncoder { |
||||
|
opts = append([]func(*BaseEncoder){ |
||||
|
WithGropuConfig(0, 0, ' '), |
||||
|
WithNullValue("<nil>"), |
||||
|
WithDefaultValue(func(dst []byte, _ Encoder, val Value) []byte { |
||||
|
return fmt.Appendf(dst, "%+v", val.Any()) |
||||
|
}), |
||||
|
}, opts...) |
||||
|
|
||||
|
return NewEncoder(opts...) |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
package field |
||||
|
|
||||
|
import "errors" |
||||
|
|
||||
|
var ErrUndefined = errors.New("indefined") |
@ -1,570 +0,0 @@ |
|||||
package field |
|
||||
|
|
||||
import ( |
|
||||
"time" |
|
||||
) |
|
||||
|
|
||||
type Key string |
|
||||
|
|
||||
//nolint:funlen,cyclop,gocyclo
|
|
||||
func (k Key) Any(value interface{}) Field { |
|
||||
switch val := value.(type) { |
|
||||
case string: |
|
||||
return k.String(val) |
|
||||
case *string: |
|
||||
return k.Stringp(val) |
|
||||
case []string: |
|
||||
return k.Strings(val...) |
|
||||
case bool: |
|
||||
return k.Bool(val) |
|
||||
case *bool: |
|
||||
return k.Boolp(val) |
|
||||
case []bool: |
|
||||
return k.Bools(val...) |
|
||||
case int8: |
|
||||
return k.Int8(val) |
|
||||
case []int8: |
|
||||
return k.Int8s(val...) |
|
||||
case *int8: |
|
||||
return k.Int8p(val) |
|
||||
case int16: |
|
||||
return k.Int16(val) |
|
||||
case []int16: |
|
||||
return k.Int16s(val...) |
|
||||
case *int16: |
|
||||
return k.Int16p(val) |
|
||||
case int32: |
|
||||
return k.Int32(val) |
|
||||
case []int32: |
|
||||
return k.Int32s(val...) |
|
||||
case *int32: |
|
||||
return k.Int32p(val) |
|
||||
case int64: |
|
||||
return k.Int64(val) |
|
||||
case []int64: |
|
||||
return k.Int64s(val...) |
|
||||
case *int64: |
|
||||
return k.Int64p(val) |
|
||||
case uint: |
|
||||
return k.Uint(val) |
|
||||
case []uint: |
|
||||
return k.Uints(val...) |
|
||||
case *uint: |
|
||||
return k.Uintp(val) |
|
||||
case uint8: |
|
||||
return k.Uint8(val) |
|
||||
case *uint8: |
|
||||
return k.Uint8p(val) |
|
||||
case uint16: |
|
||||
return k.Uint16(val) |
|
||||
case []uint16: |
|
||||
return k.Uint16s(val...) |
|
||||
case *uint16: |
|
||||
return k.Uint16p(val) |
|
||||
case uint32: |
|
||||
return k.Uint32(val) |
|
||||
case []uint32: |
|
||||
return k.Uint32s(val...) |
|
||||
case *uint32: |
|
||||
return k.Uint32p(val) |
|
||||
case uint64: |
|
||||
return k.Uint64(val) |
|
||||
case []uint64: |
|
||||
return k.Uint64s(val...) |
|
||||
case *uint64: |
|
||||
return k.Uint64p(val) |
|
||||
case float32: |
|
||||
return k.Float32(val) |
|
||||
case []float32: |
|
||||
return k.Float32s(val...) |
|
||||
case *float32: |
|
||||
return k.Float32p(val) |
|
||||
case float64: |
|
||||
return k.Float64(val) |
|
||||
case []float64: |
|
||||
return k.Float64s(val...) |
|
||||
case *float64: |
|
||||
return k.Float64p(val) |
|
||||
case complex64: |
|
||||
return k.Complex64(val) |
|
||||
case []complex64: |
|
||||
return k.Complex64s(val...) |
|
||||
case *complex64: |
|
||||
return k.Complex64p(val) |
|
||||
case uintptr: |
|
||||
return k.Uintptr(val) |
|
||||
case []uintptr: |
|
||||
return k.Uintptrs(val...) |
|
||||
case *uintptr: |
|
||||
return k.Uintptrp(val) |
|
||||
case []byte: |
|
||||
return k.Bytes(val) |
|
||||
case time.Duration: |
|
||||
return k.Dureation(val) |
|
||||
case []time.Duration: |
|
||||
return k.Dureations(val) |
|
||||
case *time.Duration: |
|
||||
return k.Dureationp(val) |
|
||||
case time.Time: |
|
||||
return k.Time(val) |
|
||||
case []time.Time: |
|
||||
return k.Times(val...) |
|
||||
case *time.Time: |
|
||||
return k.Timep(val) |
|
||||
case error: |
|
||||
return k.Error(val) |
|
||||
case []error: |
|
||||
return k.Errors(val...) |
|
||||
} |
|
||||
|
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: Value{ |
|
||||
value: value, |
|
||||
vtype: TypeAny, |
|
||||
numeric: 0, |
|
||||
stringly: "", |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) String(value string) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: stringValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Strings(value ...string) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: stringsValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Stringp(value *string) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: stringpValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Bool(value bool) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: boolValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Bools(value ...bool) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: boolsValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Boolp(value *bool) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: boolpValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int(value int) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: intValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Ints(value ...int) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: intsValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Intp(value *int) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: intpValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int8(value int8) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int8Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int8s(value ...int8) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int8sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int8p(value *int8) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int8pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int16(value int16) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int16Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int16s(value ...int16) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int16sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int16p(value *int16) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int16pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int32(value int32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int32Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int32s(value ...int32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int32sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int32p(value *int32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int32pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int64(value int64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int64Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int64s(value ...int64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int64sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Int64p(value *int64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: int64pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint(value uint) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uintValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uints(value ...uint) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uintsValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uintp(value *uint) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uintpValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint8(value uint8) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint8Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint8s(value ...uint8) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint8sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint8p(value *uint8) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint8pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint16(value uint16) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint16Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint16s(value ...uint16) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint16sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint16p(value *uint16) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint16pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint32(value uint32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint32Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint32s(value ...uint32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint32sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint32p(value *uint32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint32pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint64(value uint64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint64Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint64s(value ...uint64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint64sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uint64p(value *uint64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uint64pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Float32(value float32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: float32Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Float32s(value ...float32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: float32sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Float32p(value *float32) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: float32pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Float64(value float64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: float64Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Float64s(value ...float64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: float64sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Float64p(value *float64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: float64pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Complex64(value complex64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: complex64Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Complex64s(value ...complex64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: complex64sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Complex64p(value *complex64) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: complex64pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Complex128(value complex128) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: complex128Value(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Complex128s(value []complex128) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: complex128sValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Complex128p(value *complex128) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: complex128pValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uintptr(value uintptr) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uintptrValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uintptrs(value ...uintptr) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uintptrsValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Uintptrp(value *uintptr) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: uintptrpValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Bytes(value []byte) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: bytesValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Dureation(value time.Duration) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: durationValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Dureations(value []time.Duration) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: durationsValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Dureationp(value *time.Duration) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: durationpValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Time(value time.Time) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: timeValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Times(value ...time.Time) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: timesValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Timep(value *time.Time) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: timepValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) FormatTime(format string, value time.Time) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: formatTimeValue(format, value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) FormatTimes(format string, value ...time.Time) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: formatTimesValue(format, value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) FormatTimep(format string, value *time.Time) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: formatTimepValue(format, value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Error(value error) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: errorValue(value), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (k Key) Errors(value ...error) Field { |
|
||||
return Field{ |
|
||||
key: k, |
|
||||
value: errorsValue(value), |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,86 @@ |
|||||
|
package field |
||||
|
|
||||
|
import "fmt" |
||||
|
|
||||
|
//go:generate stringer -type=Kind -linecomment -output=kind_string.go
|
||||
|
|
||||
|
type Kind int |
||||
|
|
||||
|
const ( |
||||
|
KindAny Kind = iota // any
|
||||
|
KindArray // array
|
||||
|
KindNil // nil
|
||||
|
KindString // string
|
||||
|
KindBool // bool
|
||||
|
KindInt64 // int64
|
||||
|
KindUint64 // uint64
|
||||
|
KindFloat32 // float32
|
||||
|
KindFloat64 // float64
|
||||
|
KindComplex128 // complex128
|
||||
|
KindBinary // bytes
|
||||
|
KindDuration // duration
|
||||
|
KindTime // time
|
||||
|
KindError // error
|
||||
|
KindGroup // group
|
||||
|
KindClosure // closure
|
||||
|
) |
||||
|
|
||||
|
func (l Kind) MarshalJSON() ([]byte, error) { |
||||
|
return []byte("\"" + l.String() + "\""), nil |
||||
|
} |
||||
|
|
||||
|
func (l *Kind) UnmarshalJSON(in []byte) error { |
||||
|
return l.UnmarshalText(in[1 : len(in)-1]) |
||||
|
} |
||||
|
|
||||
|
func (l Kind) MarshalText() ([]byte, error) { |
||||
|
return []byte(l.String()), nil |
||||
|
} |
||||
|
|
||||
|
//nolint:gocyclo,cyclop
|
||||
|
func (l *Kind) UnmarshalText(in []byte) error { |
||||
|
switch string(in) { |
||||
|
case KindAny.String(): |
||||
|
*l = KindAny |
||||
|
case KindArray.String(): |
||||
|
*l = KindArray |
||||
|
case KindNil.String(): |
||||
|
*l = KindNil |
||||
|
case KindString.String(): |
||||
|
*l = KindString |
||||
|
case KindBool.String(): |
||||
|
*l = KindBool |
||||
|
case KindInt64.String(): |
||||
|
*l = KindInt64 |
||||
|
case KindUint64.String(): |
||||
|
*l = KindUint64 |
||||
|
case KindFloat32.String(): |
||||
|
*l = KindFloat32 |
||||
|
case KindFloat64.String(): |
||||
|
*l = KindFloat64 |
||||
|
case KindComplex128.String(): |
||||
|
*l = KindComplex128 |
||||
|
case KindBinary.String(): |
||||
|
*l = KindBinary |
||||
|
case KindDuration.String(): |
||||
|
*l = KindDuration |
||||
|
case KindTime.String(): |
||||
|
*l = KindTime |
||||
|
case KindError.String(): |
||||
|
*l = KindError |
||||
|
case KindGroup.String(): |
||||
|
*l = KindGroup |
||||
|
case KindClosure.String(): |
||||
|
*l = KindClosure |
||||
|
} |
||||
|
|
||||
|
return fmt.Errorf("%w:filed(%v)", ErrUndefined, string(in)) |
||||
|
} |
||||
|
|
||||
|
func (l Kind) MarshalBinary() ([]byte, error) { |
||||
|
return []byte(l.String()), nil |
||||
|
} |
||||
|
|
||||
|
func (l *Kind) UnmarshalBinary(in []byte) error { |
||||
|
return l.UnmarshalText(in) |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
// Code generated by "stringer -type=Kind -linecomment -output=kind_string.go"; DO NOT EDIT.
|
||||
|
|
||||
|
package field |
||||
|
|
||||
|
import "strconv" |
||||
|
|
||||
|
func _() { |
||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
|
// Re-run the stringer command to generate them again.
|
||||
|
var x [1]struct{} |
||||
|
_ = x[KindAny-0] |
||||
|
_ = x[KindArray-1] |
||||
|
_ = x[KindNil-2] |
||||
|
_ = x[KindString-3] |
||||
|
_ = x[KindBool-4] |
||||
|
_ = x[KindInt64-5] |
||||
|
_ = x[KindUint64-6] |
||||
|
_ = x[KindFloat32-7] |
||||
|
_ = x[KindFloat64-8] |
||||
|
_ = x[KindComplex128-9] |
||||
|
_ = x[KindBinary-10] |
||||
|
_ = x[KindDuration-11] |
||||
|
_ = x[KindTime-12] |
||||
|
_ = x[KindError-13] |
||||
|
_ = x[KindGroup-14] |
||||
|
_ = x[KindClosure-15] |
||||
|
} |
||||
|
|
||||
|
const _Kind_name = "anyarraynilstringboolint64uint64float32float64complex128bytesdurationtimeerrorgroupclosure" |
||||
|
|
||||
|
var _Kind_index = [...]uint8{0, 3, 8, 11, 17, 21, 26, 32, 39, 46, 56, 61, 69, 73, 78, 83, 90} |
||||
|
|
||||
|
func (i Kind) String() string { |
||||
|
if i < 0 || i >= Kind(len(_Kind_index)-1) { |
||||
|
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" |
||||
|
} |
||||
|
return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
package field |
||||
|
|
||||
|
type LogValuer interface { |
||||
|
LogValue() any |
||||
|
} |
||||
|
|
||||
|
type ClosureFn func() any |
||||
|
|
||||
|
func (v ClosureFn) LogValue() any { |
||||
|
return v() |
||||
|
} |
@ -0,0 +1,112 @@ |
|||||
|
package field |
||||
|
|
||||
|
import "unicode/utf8" |
||||
|
|
||||
|
// Copied from encoding/json/tables.go.
|
||||
|
//
|
||||
|
// safeSet holds the value true if the ASCII character with the given array
|
||||
|
// position can be represented inside a JSON string without any further
|
||||
|
// escaping.
|
||||
|
//
|
||||
|
// All values are true except for the ASCII control characters (0-31), the
|
||||
|
// double quote ("), and the backslash character ("\").
|
||||
|
//
|
||||
|
//nolint:gochecknoglobals
|
||||
|
var safeSet = [utf8.RuneSelf]bool{ |
||||
|
' ': true, |
||||
|
'!': true, |
||||
|
'"': false, |
||||
|
'#': true, |
||||
|
'$': true, |
||||
|
'%': true, |
||||
|
'&': true, |
||||
|
'\'': true, |
||||
|
'(': true, |
||||
|
')': true, |
||||
|
'*': true, |
||||
|
'+': true, |
||||
|
',': true, |
||||
|
'-': true, |
||||
|
'.': true, |
||||
|
'/': true, |
||||
|
'0': true, |
||||
|
'1': true, |
||||
|
'2': true, |
||||
|
'3': true, |
||||
|
'4': true, |
||||
|
'5': true, |
||||
|
'6': true, |
||||
|
'7': true, |
||||
|
'8': true, |
||||
|
'9': true, |
||||
|
':': true, |
||||
|
';': true, |
||||
|
'<': true, |
||||
|
'=': true, |
||||
|
'>': true, |
||||
|
'?': true, |
||||
|
'@': true, |
||||
|
'A': true, |
||||
|
'B': true, |
||||
|
'C': true, |
||||
|
'D': true, |
||||
|
'E': true, |
||||
|
'F': true, |
||||
|
'G': true, |
||||
|
'H': true, |
||||
|
'I': true, |
||||
|
'J': true, |
||||
|
'K': true, |
||||
|
'L': true, |
||||
|
'M': true, |
||||
|
'N': true, |
||||
|
'O': true, |
||||
|
'P': true, |
||||
|
'Q': true, |
||||
|
'R': true, |
||||
|
'S': true, |
||||
|
'T': true, |
||||
|
'U': true, |
||||
|
'V': true, |
||||
|
'W': true, |
||||
|
'X': true, |
||||
|
'Y': true, |
||||
|
'Z': true, |
||||
|
'[': true, |
||||
|
'\\': false, |
||||
|
']': true, |
||||
|
'^': true, |
||||
|
'_': true, |
||||
|
'`': true, |
||||
|
'a': true, |
||||
|
'b': true, |
||||
|
'c': true, |
||||
|
'd': true, |
||||
|
'e': true, |
||||
|
'f': true, |
||||
|
'g': true, |
||||
|
'h': true, |
||||
|
'i': true, |
||||
|
'j': true, |
||||
|
'k': true, |
||||
|
'l': true, |
||||
|
'm': true, |
||||
|
'n': true, |
||||
|
'o': true, |
||||
|
'p': true, |
||||
|
'q': true, |
||||
|
'r': true, |
||||
|
's': true, |
||||
|
't': true, |
||||
|
'u': true, |
||||
|
'v': true, |
||||
|
'w': true, |
||||
|
'x': true, |
||||
|
'y': true, |
||||
|
'z': true, |
||||
|
'{': true, |
||||
|
'|': true, |
||||
|
'}': true, |
||||
|
'~': true, |
||||
|
'\u007f': true, |
||||
|
} |
@ -1,126 +0,0 @@ |
|||||
package field |
|
||||
|
|
||||
type Type uint32 |
|
||||
|
|
||||
const ( |
|
||||
TypeAny Type = 1 << iota // any
|
|
||||
TypeArray // array
|
|
||||
TypeNil // nil
|
|
||||
TypeString // string
|
|
||||
TypeBool // bool
|
|
||||
TypeInt // int
|
|
||||
TypeInt8 // int8
|
|
||||
TypeInt16 // int16
|
|
||||
TypeInt32 // int32
|
|
||||
TypeInt64 // int64
|
|
||||
TypeUint // uint
|
|
||||
TypeUint8 // uint8
|
|
||||
TypeUint16 // uint16
|
|
||||
TypeUint32 // uint32
|
|
||||
TypeUint64 // uint64
|
|
||||
TypeFloat32 // float32
|
|
||||
TypeFloat64 // float64
|
|
||||
TypeComplex64 // complex64
|
|
||||
TypeComplex128 // complex128
|
|
||||
TypeUintptr // uintptr
|
|
||||
TypeBinary // bytes
|
|
||||
TypeDuration // duration
|
|
||||
TypeTime // time
|
|
||||
TypeError // error
|
|
||||
) |
|
||||
|
|
||||
func (t Type) IsAny() bool { |
|
||||
return t&TypeAny > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsArray() bool { |
|
||||
return t&TypeArray > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsNil() bool { |
|
||||
return t&TypeNil > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsBool() bool { |
|
||||
return t&TypeBool > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsString() bool { |
|
||||
return t&TypeString > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsInt() bool { |
|
||||
return t&TypeInt > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsInt8() bool { |
|
||||
return t&TypeInt8 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsInt16() bool { |
|
||||
return t&TypeInt16 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsInt32() bool { |
|
||||
return t&TypeInt32 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsInt64() bool { |
|
||||
return t&TypeInt64 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsUint() bool { |
|
||||
return t&TypeUint > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsUint8() bool { |
|
||||
return t&TypeUint8 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsUint16() bool { |
|
||||
return t&TypeUint16 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsUint32() bool { |
|
||||
return t&TypeUint32 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsUint64() bool { |
|
||||
return t&TypeUint64 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsFloat32() bool { |
|
||||
return t&TypeFloat32 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsFloat64() bool { |
|
||||
return t&TypeFloat64 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsComplex64() bool { |
|
||||
return t&TypeComplex64 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsComplex128() bool { |
|
||||
return t&TypeComplex128 > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsUintptr() bool { |
|
||||
return t&TypeUintptr > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsBinary() bool { |
|
||||
return t&TypeBinary > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsDuration() bool { |
|
||||
return t&TypeDuration > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsTime() bool { |
|
||||
return t&TypeTime > 0 |
|
||||
} |
|
||||
|
|
||||
func (t Type) IsError() bool { |
|
||||
return t&TypeError > 0 |
|
||||
} |
|
File diff suppressed because it is too large
@ -0,0 +1,42 @@ |
|||||
|
package buffer |
||||
|
|
||||
|
import "sync" |
||||
|
|
||||
|
const bufferSize = 1024 |
||||
|
|
||||
|
type Buffer []byte |
||||
|
|
||||
|
// Having an initial size gives a dramatic speedup.
|
||||
|
//
|
||||
|
//nolint:gochecknoglobals
|
||||
|
var bufPool = sync.Pool{ |
||||
|
New: func() any { |
||||
|
b := make([]byte, 0, bufferSize) |
||||
|
|
||||
|
return (*Buffer)(&b) |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
//nolint:forcetypeassert
|
||||
|
func New() *Buffer { |
||||
|
return bufPool.Get().(*Buffer) |
||||
|
} |
||||
|
|
||||
|
func (b *Buffer) Free() { |
||||
|
// To reduce peak allocation, return only smaller buffers to the pool.
|
||||
|
const maxBufferSize = 16 << 10 |
||||
|
if cap(*b) <= maxBufferSize { |
||||
|
*b = (*b)[:0] |
||||
|
bufPool.Put(b) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (b *Buffer) WriteString(s string) (int, error) { |
||||
|
*b = append(*b, s...) |
||||
|
|
||||
|
return len(s), nil |
||||
|
} |
||||
|
|
||||
|
func (b *Buffer) String() string { |
||||
|
return string(*b) |
||||
|
} |
@ -1,119 +1,129 @@ |
|||||
package log |
package log |
||||
|
|
||||
import ( |
import ( |
||||
"bytes" |
|
||||
"context" |
"context" |
||||
"encoding/json" |
|
||||
"fmt" |
"fmt" |
||||
"io" |
"io" |
||||
"os" |
"os" |
||||
"strings" |
|
||||
"sync" |
|
||||
|
|
||||
"gitoa.ru/go-4devs/log/entry" |
"gitoa.ru/go-4devs/log/entry" |
||||
|
"gitoa.ru/go-4devs/log/field" |
||||
|
"gitoa.ru/go-4devs/log/internal/buffer" |
||||
) |
) |
||||
|
|
||||
// New creates standart logger.
|
// Keys for "built-in" attributes.
|
||||
func New(opts ...Option) Logger { |
const ( |
||||
logger := log{e: stringFormat(), w: os.Stderr} |
// TimeKey is the key used by the built-in handlers for the time
|
||||
|
// when the log method is called. The associated Value is a [time.Time].
|
||||
|
KeyTime = "time" |
||||
|
// LevelKey is the key used by the built-in handlers for the level
|
||||
|
// of the log call. The associated value is a [Level].
|
||||
|
KeyLevel = "level" |
||||
|
// MessageKey is the key used by the built-in handlers for the
|
||||
|
// message of the log call. The associated value is a string.
|
||||
|
KeyMessage = "msg" |
||||
|
// SourceKey is the key used by the built-in handlers for the source file
|
||||
|
// and line of the log call. The associated value is a string.
|
||||
|
KeySource = "source" |
||||
|
) |
||||
|
|
||||
for _, opt := range opts { |
func WithWriter(w io.Writer) func(*option) { |
||||
opt(&logger) |
return func(o *option) { |
||||
|
o.out = w |
||||
} |
} |
||||
|
} |
||||
|
|
||||
return func(_ context.Context, entry *entry.Entry) (int, error) { |
func WithStdout() func(*option) { |
||||
b, err := logger.e(entry) |
return func(o *option) { |
||||
if err != nil { |
o.out = os.Stdout |
||||
return 0, fmt.Errorf("enode err: %w", err) |
|
||||
} |
|
||||
|
|
||||
n, err := logger.w.Write(b) |
|
||||
if err != nil { |
|
||||
return 0, fmt.Errorf("failed write: %w", err) |
|
||||
} |
|
||||
|
|
||||
return n, nil |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// Option configure log.
|
// WithStringFormat sets format as simple string.
|
||||
type Option func(*log) |
func WithStringFormat() func(*option) { |
||||
|
return func(o *option) { |
||||
// Encode sets formats and encode output message.
|
o.format = formatText() |
||||
type Encode func(*entry.Entry) ([]byte, error) |
} |
||||
|
|
||||
type log struct { |
|
||||
w io.Writer |
|
||||
e Encode |
|
||||
} |
} |
||||
|
|
||||
// WithWriter sets writer logger.
|
// WithJSONFormat sets json output format.
|
||||
func WithWriter(writer io.Writer) Option { |
func WithJSONFormat() func(*option) { |
||||
return func(l *log) { |
return func(o *option) { |
||||
l.w = writer |
o.format = formatJSON() |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
// WithStdout sets logged to os.Stdout.
|
type option struct { |
||||
func WithStdout() Option { |
format func(io.Writer, *entry.Entry) (int, error) |
||||
return WithWriter(os.Stdout) |
out io.Writer |
||||
} |
} |
||||
|
|
||||
// WithEncode sets format log.
|
// New creates standart logger.
|
||||
func WithEncode(e Encode) Option { |
func New(opts ...func(*option)) Logger { |
||||
return func(l *log) { |
log := option{ |
||||
l.e = e |
format: formatText(), |
||||
|
out: os.Stderr, |
||||
} |
} |
||||
} |
|
||||
|
|
||||
// WithStringFormat sets format as simple string.
|
for _, opt := range opts { |
||||
func WithStringFormat() Option { |
opt(&log) |
||||
return WithEncode(stringFormat()) |
} |
||||
} |
|
||||
|
|
||||
// WithJSONFormat sets json output format.
|
|
||||
func WithJSONFormat() Option { |
|
||||
return WithEncode(jsonFormat) |
|
||||
} |
|
||||
|
|
||||
//nolint:forcetypeassert
|
return func(_ context.Context, entry *entry.Entry) (int, error) { |
||||
func stringFormat() func(entry *entry.Entry) ([]byte, error) { |
return log.format(log.out, entry) |
||||
pool := sync.Pool{ |
|
||||
New: func() interface{} { |
|
||||
return &bytes.Buffer{} |
|
||||
}, |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
return func(entry *entry.Entry) ([]byte, error) { |
func formatText() func(io.Writer, *entry.Entry) (int, error) { |
||||
buf := pool.Get().(*bytes.Buffer) |
enc := field.NewEncoderText() |
||||
buf.Reset() |
|
||||
|
|
||||
|
return func(w io.Writer, entry *entry.Entry) (int, error) { |
||||
|
buf := buffer.New() |
||||
defer func() { |
defer func() { |
||||
pool.Put(buf) |
buf.Free() |
||||
}() |
}() |
||||
|
|
||||
buf.WriteString("msg=\"") |
*buf = enc.AppendField(*buf, field.String(KeyMessage, entry.Message())) |
||||
buf.WriteString(strings.TrimSpace(entry.Message())) |
|
||||
buf.WriteString("\"") |
|
||||
|
|
||||
for _, field := range entry.Fields() { |
for _, field := range entry.Fields() { |
||||
buf.WriteString(" ") |
*buf = enc.AppendField(*buf, field) |
||||
buf.WriteString(string(field.Key())) |
|
||||
buf.WriteString("=") |
|
||||
buf.WriteString(field.Value().String()) |
|
||||
} |
} |
||||
|
|
||||
buf.WriteString("\n") |
_, _ = buf.WriteString("\n") |
||||
|
|
||||
|
n, err := w.Write(*buf) |
||||
|
if err != nil { |
||||
|
return 0, fmt.Errorf("format text:%w", err) |
||||
|
} |
||||
|
|
||||
return buf.Bytes(), nil |
return n, nil |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
func jsonFormat(entry *entry.Entry) ([]byte, error) { |
func formatJSON() func(w io.Writer, entry *entry.Entry) (int, error) { |
||||
res, err := json.Marshal(entry.AddString("msg", entry.Message()).Fields().AsMap()) |
enc := field.NewEncoderJSON() |
||||
if err != nil { |
|
||||
return nil, fmt.Errorf("marshal err: %w", err) |
|
||||
} |
|
||||
|
|
||||
return append(res, []byte("\n")...), nil |
return func(w io.Writer, entry *entry.Entry) (int, error) { |
||||
|
buf := buffer.New() |
||||
|
defer func() { |
||||
|
buf.Free() |
||||
|
}() |
||||
|
|
||||
|
_, _ = buf.WriteString("{") |
||||
|
*buf = enc.AppendField(*buf, field.String(KeyMessage, entry.Message())) |
||||
|
|
||||
|
for _, field := range entry.Fields() { |
||||
|
*buf = enc.AppendField(*buf, field) |
||||
|
} |
||||
|
|
||||
|
_, _ = buf.WriteString("}") |
||||
|
_, _ = buf.WriteString("\n") |
||||
|
|
||||
|
n, err := w.Write(*buf) |
||||
|
if err != nil { |
||||
|
return 0, fmt.Errorf("format json:%w", err) |
||||
|
} |
||||
|
|
||||
|
return n, nil |
||||
|
} |
||||
} |
} |
||||
|
Loading…
Reference in new issue