Browse Source

restore v0.3.0

pull/2/head v0.3.1
andrey1s 3 years ago
parent
commit
deb67b0008
  1. BIN
      .DS_Store
  2. 177
      bench_test.go
  3. 22
      entry/caller.go
  4. 132
      entry/entry.go
  5. 21
      entry/pool.go
  6. 37
      field.go
  7. 31
      field/encoder.go
  8. 333
      field/field.go
  9. 29
      field/fields.go
  10. 568
      field/key.go
  11. 126
      field/type.go
  12. 755
      field/value.go
  13. 75
      global.go
  14. 9
      go.mod
  15. 76
      go.sum
  16. 90
      handler/logrus/logger.go
  17. 36
      handler/logrus/logger_test.go
  18. 56
      handler/otel/helpers.go
  19. 16
      handler/otel/level.go
  20. 51
      handler/otel/level_string.go
  21. 16
      handler/otel/logger.go
  22. 16
      handler/otel/middleware.go
  23. 95
      handler/zap/logger.go
  24. 41
      handler/zap/logger_test.go
  25. 22
      level.go
  26. 73
      level/level.go
  27. 18
      level/level_string.go
  28. 221
      logger.go
  29. 20
      logger_example_caller_test.go
  30. 21
      logger_example_logrus_test.go
  31. 81
      logger_example_test.go
  32. 62
      logger_example_trace_test.go
  33. 28
      logger_example_zap_test.go
  34. 85
      logger_test.go
  35. 122
      middleware.go
  36. 121
      std.go
  37. 118
      writter.go

BIN
.DS_Store

Binary file not shown.

177
bench_test.go

@ -0,0 +1,177 @@
package log_test
import (
"context"
"errors"
"fmt"
"io/ioutil"
"testing"
"time"
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/field"
)
// nolint: gochecknoglobals
var (
errExample = errors.New("fail")
_messages = fakeMessages(1000)
_tenInts = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
_tenStrings = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
_tenTimes = []time.Time{
time.Unix(0, 0),
time.Unix(1, 0),
time.Unix(2, 0),
time.Unix(3, 0),
time.Unix(4, 0),
time.Unix(5, 0),
time.Unix(6, 0),
time.Unix(7, 0),
time.Unix(8, 0),
time.Unix(9, 0),
}
_oneUser = &user{
Name: "Jane Doe",
Email: "jane@test.com",
CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),
}
_tenUsers = users{
_oneUser,
_oneUser,
_oneUser,
_oneUser,
_oneUser,
_oneUser,
_oneUser,
_oneUser,
_oneUser,
_oneUser,
}
)
type user struct {
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
type users []*user
func fakeMessages(n int) []string {
messages := make([]string, n)
for i := range messages {
messages[i] = fmt.Sprintf("Test logging, but use a somewhat realistic message length. (#%v)", i)
}
return messages
}
func getMessage(iter int) string {
return _messages[iter%1000]
}
func fakeFmtArgs() []interface{} {
// Need to keep this a function instead of a package-global var so that we
// pay the cast-to-interface{} penalty on each call.
return []interface{}{
_tenInts[0],
_tenInts,
_tenStrings[0],
_tenStrings,
_tenTimes[0],
_tenTimes,
_oneUser,
_oneUser,
_tenUsers,
errExample,
}
}
func fakeFields() []field.Field {
return []field.Field{
field.Int("int", _tenInts[0]),
field.Ints("ints", _tenInts...),
field.String("string", _tenStrings[0]),
field.Strings("strings", _tenStrings...),
field.Time("time", _tenTimes[0]),
field.Times("times", _tenTimes...),
field.Any("user1", _oneUser),
field.Any("user2", _oneUser),
field.Any("users", _tenUsers),
field.Error("err", errExample),
}
}
func fakeSugarFields() []interface{} {
return []interface{}{
"int", _tenInts[0],
"ints", _tenInts,
"string", _tenStrings[0],
"strings", _tenStrings,
"time", _tenTimes[0],
"times", _tenTimes,
"user1", _oneUser,
"user2", _oneUser,
"users", _tenUsers,
"error", errExample,
}
}
func NewLogger() log.Logger {
return log.New(log.WithWriter(ioutil.Discard))
}
func BenchmarkDisabledWithoutFields(b *testing.B) {
ctx := context.Background()
logger := NewLogger()
b.Run("4devs/log", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.Info(ctx, getMessage(0))
}
})
})
b.Run("4devs/log.Formatting", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.Infof(ctx, "%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...)
}
})
})
}
func BenchmarkDisabledAccumulatedContext(b *testing.B) {
ctx := context.Background()
logger := NewLogger()
b.Run("4devs/log", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.InfoKV(ctx, getMessage(0), fakeFields()...)
}
})
})
b.Run("4devs/log.Sugar", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.InfoKVs(ctx, getMessage(1), fakeSugarFields()...)
}
})
})
b.Run("4devs/log.Context", func(b *testing.B) {
b.ResetTimer()
logger := NewLogger().With(log.GoVersion("goversion"))
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
logger.InfoKV(ctx, getMessage(0), fakeFields()...)
}
})
})
}

22
entry/caller.go

@ -0,0 +1,22 @@
package entry
import (
"path/filepath"
"runtime"
"strconv"
)
func Caller(depth int, full bool) string {
const offset = 4
_, file, line, ok := runtime.Caller(depth + offset)
if !ok {
file, line = "???", 0
}
if !full && ok {
file = filepath.Base(file)
}
return file + ":" + strconv.Itoa(line)
}

132
entry/entry.go

@ -0,0 +1,132 @@
package entry
import (
"strings"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
)
const (
defaultCap = 5
)
type Option func(*Entry)
func WithCapacity(cap int) Option {
return func(e *Entry) {
e.fields = make(field.Fields, 0, cap+1)
}
}
func WithFields(fields ...field.Field) Option {
return func(e *Entry) {
e.fields = fields
}
}
func WithMessage(msg string) Option {
return func(e *Entry) {
e.msg = msg
}
}
func WithLevel(lvl level.Level) Option {
return func(e *Entry) {
e.level = lvl
}
}
func New(opts ...Option) *Entry {
e := &Entry{
fields: make(field.Fields, 0, defaultCap+1),
level: level.Debug,
}
for _, opt := range opts {
opt(e)
}
return e
}
// Entry slice field.
type Entry struct {
msg string
level level.Level
fields field.Fields
}
func (e *Entry) Reset() {
e.fields = e.fields[:0]
}
func (e *Entry) Fields() field.Fields {
return e.fields
}
// String implement stringer.
func (e *Entry) String() string {
if e == nil {
return ""
}
str := make([]string, len(e.fields)+1)
str[0] = e.msg
for i, field := range e.fields {
str[i+1] = field.String()
}
return strings.Join(str, " ")
}
func (e *Entry) Message() string {
return e.msg
}
func (e *Entry) Level() level.Level {
if e == nil {
return level.Debug
}
return e.level
}
func (e *Entry) SetLevel(level level.Level) *Entry {
if e == nil {
return New().SetLevel(level)
}
e.level = level
return e
}
func (e *Entry) SetMessage(msg string) *Entry {
if e == nil {
return New().SetMessage(msg)
}
e.msg = msg
return e
}
func (e *Entry) Add(fields ...field.Field) *Entry {
if e == nil {
return New(WithFields(fields...))
}
e.fields = e.fields.Append(fields...)
return e
}
func (e *Entry) AddAny(key string, value interface{}) *Entry {
return e.Add(field.Any(key, value))
}
func (e *Entry) AddString(key, value string) *Entry {
return e.Add(field.String(key, value))
}

21
entry/pool.go

@ -0,0 +1,21 @@
package entry
import "sync"
// nolint: gochecknoglobals
var pool = sync.Pool{
New: func() interface{} {
return New()
},
}
func Get() *Entry {
e := pool.Get().(*Entry)
e.Reset()
return e
}
func Put(e *Entry) {
pool.Put(e)
}

37
field.go

@ -1,40 +1,15 @@
package log package log
import ( import (
"fmt" "gitoa.ru/go-4devs/log/field"
"strings"
) )
// Fields slice field. // Field create field.
type Fields []Field func Field(key string, value interface{}) field.Field {
return field.Any(key, value)
// String implement stringer.
func (f Fields) String() string {
str := make([]string, len(f))
for i, field := range f {
str[i] = field.String()
}
return strings.Join(str, " ")
}
// NewField create field.
func NewField(key string, value interface{}) Field {
return Field{Key: key, Value: value}
}
// Field struct.
type Field struct {
Key string
Value interface{}
}
// String implent stringer.
func (f Field) String() string {
return fmt.Sprintf("%s=%+v", f.Key, f.Value)
} }
// FieldError new errors field with key error. // FieldError new errors field with key error.
func FieldError(err error) Field { func FieldError(err error) field.Field {
return NewField("error", err) return field.Error("error", err)
} }

31
field/encoder.go

@ -0,0 +1,31 @@
package field
import "time"
type Encoder interface {
// Built-in types.
AddArray(key string, value Value)
AddAny(key string, value Value)
AddNil(key string)
AddBool(key string, value bool)
AddBinary(key string, value []byte)
AddInt(key string, value int)
AddInt8(key string, value int8)
AddInt16(key string, value int16)
AddInt32(key string, value int32)
AddInt64(key string, value int64)
AddUint(key string, value uint)
AddUint8(key string, value uint8)
AddUint16(key string, value uint16)
AddUint32(key string, value uint32)
AddUint64(key string, value uint64)
AddUintptr(key string, value uintptr)
AddTime(key string, value time.Time)
AddDuration(key string, value time.Duration)
AddFloat32(key string, value float32)
AddFloat64(key string, value float64)
AddComplex64(key string, value complex64)
AddComplex128(key string, value complex128)
AddString(key, value string)
AddError(key string, value error)
}

333
field/field.go

@ -0,0 +1,333 @@
package field
import (
"fmt"
"time"
)
func Any(key string, value interface{}) Field {
return Key(key).Any(value)
}
func String(key, value string) Field {
return Key(key).String(value)
}
func Stringp(key string, value *string) Field {
return Key(key).Stringp(value)
}
func Strings(key string, value ...string) Field {
return Key(key).Strings(value...)
}
func Bool(key string, value bool) Field {
return Key(key).Bool(value)
}
func Bools(key string, value ...bool) Field {
return Key(key).Bools(value...)
}
func Boolp(key string, value *bool) Field {
return Key(key).Boolp(value)
}
func Uint(key string, value uint) Field {
return Key(key).Uint(value)
}
func Uints(key string, value ...uint) Field {
return Key(key).Uints(value...)
}
func Uintp(key string, value *uint) Field {
return Key(key).Uintp(value)
}
func Uint8(key string, value uint8) Field {
return Key(key).Uint8(value)
}
func Uint8s(key string, value ...uint8) Field {
return Key(key).Uint8s(value...)
}
func Uint8p(key string, value *uint8) Field {
return Key(key).Uint8p(value)
}
func Uint16(key string, value uint16) Field {
return Key(key).Uint16(value)
}
func Uint16s(key string, value ...uint16) Field {
return Key(key).Uint16s(value...)
}
func Uint16p(key string, value *uint16) Field {
return Key(key).Uint16p(value)
}
func Uint32(key string, value uint32) Field {
return Key(key).Uint32(value)
}
func Uint32s(key string, value ...uint32) Field {
return Key(key).Uint32s(value...)
}
func Uint32p(key string, value *uint32) Field {
return Key(key).Uint32p(value)
}
func Uint64(key string, value uint64) Field {
return Key(key).Uint64(value)
}
func Uint64s(key string, value ...uint64) Field {
return Key(key).Uint64s(value...)
}
func Uint64p(key string, value *uint64) Field {
return Key(key).Uint64p(value)
}
func Int(key string, value int) Field {
return Key(key).Int(value)
}
func Ints(key string, value ...int) Field {
return Key(key).Ints(value...)
}
func Intp(key string, value *int) Field {
return Key(key).Intp(value)
}
func Int8(key string, value int8) Field {
return Key(key).Int8(value)
}
func Int8s(key string, value ...int8) Field {
return Key(key).Int8s(value...)
}
func Int8p(key string, value *int8) Field {
return Key(key).Int8p(value)
}
func Int16(key string, value int16) Field {
return Key(key).Int16(value)
}
func Int16s(key string, value ...int16) Field {
return Key(key).Int16s(value...)
}
func Int16p(key string, value *int16) Field {
return Key(key).Int16p(value)
}
func Int32(key string, value int32) Field {
return Key(key).Int32(value)
}
func Int32s(key string, value ...int32) Field {
return Key(key).Int32s(value...)
}
func Int32p(key string, value *int32) Field {
return Key(key).Int32p(value)
}
func Int64(key string, value int64) Field {
return Key(key).Int64(value)
}
func Int64s(key string, value ...int64) Field {
return Key(key).Int64s(value...)
}
func Int64p(key string, value *int64) Field {
return Key(key).Int64p(value)
}
func Float32(key string, value float32) Field {
return Key(key).Float32(value)
}
func Float32s(key string, value ...float32) Field {
return Key(key).Float32s(value...)
}
func Float32p(key string, value *float32) Field {
return Key(key).Float32p(value)
}
func Float64(key string, value float64) Field {
return Key(key).Float64(value)
}
func Float64s(key string, value ...float64) Field {
return Key(key).Float64s(value...)
}
func Float64p(key string, value *float64) Field {
return Key(key).Float64p(value)
}
func Complex64(key string, value complex64) Field {
return Key(key).Complex64(value)
}
func Complex64s(key string, value ...complex64) Field {
return Key(key).Complex64s(value...)
}
func Complex64p(key string, value *complex64) Field {
return Key(key).Complex64p(value)
}
func Uintptr(key string, value uintptr) Field {
return Key(key).Uintptr(value)
}
func Uintptrs(key string, value ...uintptr) Field {
return Key(key).Uintptrs(value...)
}
func Uintptrp(key string, value *uintptr) Field {
return Key(key).Uintptrp(value)
}
func Bytes(key string, value []byte) Field {
return Key(key).Bytes(value)
}
func Duration(key string, value time.Duration) Field {
return Key(key).Dureation(value)
}
func Durations(key string, value ...time.Duration) Field {
return Key(key).Dureations(value)
}
func Durationp(key string, value *time.Duration) Field {
return Key(key).Dureationp(value)
}
func Time(key string, value time.Time) Field {
return Key(key).Time(value)
}
func Times(key string, value ...time.Time) Field {
return Key(key).Times(value...)
}
func Timep(key string, value *time.Time) Field {
return Key(key).Timep(value)
}
func FormatTime(key, format string, value time.Time) Field {
return Key(key).FormatTime(format, value)
}
func FormatTimes(key, foramt string, value ...time.Time) Field {
return Key(key).FormatTimes(foramt, value...)
}
func FormatTimep(key, foramt string, value *time.Time) Field {
return Key(key).FormatTimep(foramt, value)
}
func Error(key string, value error) Field {
return Key(key).Error(value)
}
func Errors(key string, value ...error) Field {
return Key(key).Errors(value...)
}
// Field struct.
type Field struct {
key Key
value Value
}
//nolint: gocyclo
func (f Field) AddTo(enc Encoder) {
key := string(f.key)
switch {
case f.value.IsArray():
enc.AddAny(key, f.value)
case f.value.IsNil():
enc.AddNil(key)
case f.value.IsBool():
enc.AddBool(key, f.value.asBool())
case f.value.IsBinary():
enc.AddBinary(key, f.value.asBinary())
case f.value.IsInt():
enc.AddInt(key, f.value.asInt())
case f.value.IsInt8():
enc.AddInt8(key, f.value.asInt8())
case f.value.IsInt16():
enc.AddInt16(key, f.value.asInt16())
case f.value.IsInt32():
enc.AddInt32(key, f.value.asInt32())
case f.value.IsInt64():
enc.AddInt64(key, f.value.asInt64())
case f.value.IsUint():
enc.AddUint(key, f.value.asUint())
case f.value.IsUint8():
enc.AddUint8(key, f.value.asUint8())
case f.value.IsUint16():
enc.AddUint16(key, f.value.asUint16())
case f.value.IsUint32():
enc.AddUint32(key, f.value.asUint32())
case f.value.IsUint64():
enc.AddUint64(key, f.value.asUint64())
case f.value.IsUintptr():
enc.AddUintptr(key, f.value.asUintptr())
case f.value.IsTime():
enc.AddTime(key, f.value.asTime())
case f.value.IsDuration():
enc.AddDuration(key, f.value.asDuration())
case f.value.IsFloat32():
enc.AddFloat32(key, f.value.asFloat32())
case f.value.IsFloat64():
enc.AddFloat64(key, f.value.asFloat64())
case f.value.IsComplex64():
enc.AddComplex64(key, f.value.asComplex64())
case f.value.IsComplex128():
enc.AddComplex128(key, f.value.asComplex128())
case f.value.IsString():
enc.AddString(key, f.value.asString())
case f.value.IsError():
enc.AddError(key, f.value.asError())
default:
enc.AddAny(key, f.value)
}
}
func (f Field) Type() Type {
return f.value.vtype
}
func (f Field) Key() Key {
return f.key
}
func (f Field) Value() Value {
return f.value
}
func (f Field) AsInterface() interface{} {
return f.value.AsInterface()
}
// String implent stringer.
func (f Field) String() string {
return fmt.Sprintf("%s=%+v", f.key, f.value.AsInterface())
}

29
field/fields.go

@ -0,0 +1,29 @@
package field
type Fields []Field
type MapField map[Key]Value
func (f Fields) Append(fields ...Field) Fields {
f = append(f, fields...)
return f
}
func (f Fields) Set(idx int, field Field) {
f[idx] = field
}
func (f Fields) Len() int {
return len(f)
}
func (f Fields) AsMap() MapField {
m := make(MapField, len(f))
for _, field := range f {
m[field.Key()] = field.Value()
}
return m
}

568
field/key.go

@ -0,0 +1,568 @@
package field
import (
"time"
)
type Key string
//nolint: gocyclo, funlen
func (k Key) Any(value interface{}) Field {
switch v := value.(type) {
case string:
return k.String(v)
case *string:
return k.Stringp(v)
case []string:
return k.Strings(v...)
case bool:
return k.Bool(v)
case *bool:
return k.Boolp(v)
case []bool:
return k.Bools(v...)
case int8:
return k.Int8(v)
case []int8:
return k.Int8s(v...)
case *int8:
return k.Int8p(v)
case int16:
return k.Int16(v)
case []int16:
return k.Int16s(v...)
case *int16:
return k.Int16p(v)
case int32:
return k.Int32(v)
case []int32:
return k.Int32s(v...)
case *int32:
return k.Int32p(v)
case int64:
return k.Int64(v)
case []int64:
return k.Int64s(v...)
case *int64:
return k.Int64p(v)
case uint:
return k.Uint(v)
case []uint:
return k.Uints(v...)
case *uint:
return k.Uintp(v)
case uint8:
return k.Uint8(v)
case *uint8:
return k.Uint8p(v)
case uint16:
return k.Uint16(v)
case []uint16:
return k.Uint16s(v...)
case *uint16:
return k.Uint16p(v)
case uint32:
return k.Uint32(v)
case []uint32:
return k.Uint32s(v...)
case *uint32:
return k.Uint32p(v)
case uint64:
return k.Uint64(v)
case []uint64:
return k.Uint64s(v...)
case *uint64:
return k.Uint64p(v)
case float32:
return k.Float32(v)
case []float32:
return k.Float32s(v...)
case *float32:
return k.Float32p(v)
case float64:
return k.Float64(v)
case []float64:
return k.Float64s(v...)
case *float64:
return k.Float64p(v)
case complex64:
return k.Complex64(v)
case []complex64:
return k.Complex64s(v...)
case *complex64:
return k.Complex64p(v)
case uintptr:
return k.Uintptr(v)
case []uintptr:
return k.Uintptrs(v...)
case *uintptr:
return k.Uintptrp(v)
case []byte:
return k.Bytes(v)
case time.Duration:
return k.Dureation(v)
case []time.Duration:
return k.Dureations(v)
case *time.Duration:
return k.Dureationp(v)
case time.Time:
return k.Time(v)
case []time.Time:
return k.Times(v...)
case *time.Time:
return k.Timep(v)
case error:
return k.Error(v)
case []error:
return k.Errors(v...)
}
return Field{
key: k,
value: Value{
value: value,
vtype: TypeAny,
},
}
}
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),
}
}

126
field/type.go

@ -0,0 +1,126 @@
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
}

755
field/value.go

@ -0,0 +1,755 @@
package field
import (
"encoding/json"
"fmt"
"math"
"strconv"
"time"
)
type Value struct {
vtype Type
numeric uint64
stringly string
value interface{}
}
func (v Value) MarshalJSON() ([]byte, error) {
return json.Marshal(v.AsInterface())
}
//nolint: gocyclo
func (v Value) String() string {
switch {
case v.vtype.IsArray(), v.vtype.IsAny():
return fmt.Sprintf("%+v", v.AsInterface())
case v.vtype.IsNil():
return "<nil>"
case v.vtype.IsString():
return v.asString()
case v.vtype.IsBool():
return strconv.FormatBool(v.asBool())
case v.vtype.IsInt(), v.vtype.IsInt8(), v.vtype.IsInt16(), v.vtype.IsInt32():
return strconv.Itoa(v.asInt())
case v.vtype.IsInt64():
return strconv.FormatInt(v.asInt64(), 10)
case v.vtype.IsUint(), v.vtype.IsUint8(), v.vtype.IsUint16(), v.vtype.IsUint32(), v.vtype.IsUint64():
return strconv.FormatUint(v.asUint64(), 10)
case v.vtype.IsFloat64():
return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64)
case v.vtype.IsFloat32():
return strconv.FormatFloat(float64(v.asFloat32()), 'g', -1, 32)
case v.vtype.IsComplex128():
return strconv.FormatComplex(v.asComplex128(), 'g', -1, 128)
case v.vtype.IsComplex64():
return strconv.FormatComplex(complex128(v.asComplex64()), 'g', -1, 64)
case v.vtype.IsBinary():
return string(v.asBinary())
case v.vtype.IsDuration():
return v.asDuration().String()
case v.vtype.IsTime():
return v.asTime().Format(v.asString())
case v.vtype.IsError():
return v.asError().Error()
}
return fmt.Sprintf("%+v", v.AsInterface())
}
//nolint: gocyclo
func (v Value) AsInterface() interface{} {
switch {
case v.vtype.IsArray():
return v.value
case v.vtype.IsNil():
return nil
case v.vtype.IsString():
return v.asString()
case v.vtype.IsBool():
return v.asBool()
case v.vtype.IsInt():
return v.asInt()
case v.vtype.IsInt8():
return v.asInt8()
case v.vtype.IsInt16():
return v.asInt16()
case v.vtype.IsInt32():
return v.asInt32()
case v.vtype.IsInt64():
return v.asInt64()
case v.vtype.IsUint():
return v.asUint()
case v.vtype.IsUint8():
return v.asUint8()
case v.vtype.IsUint16():
return v.asUint16()
case v.vtype.IsUint32():
return v.asUint32()
case v.vtype.IsUint64():
return v.asUint64()
case v.vtype.IsFloat32():
return v.asFloat32()
case v.vtype.IsFloat64():
return v.asFloat64()
case v.vtype.IsComplex64():
return v.asComplex64()
case v.vtype.IsComplex128():
return v.asComplex128()
case v.vtype.IsUintptr():
return v.asUintptr()
case v.vtype.IsBinary():
return v.asBinary()
case v.vtype.IsDuration():
return v.asDuration()
case v.vtype.IsTime():
return v.asTime()
case v.vtype.IsError():
return v.asError()
}
return v.value
}
func (v Value) IsArray() bool {
return v.vtype.IsArray()
}
func (v Value) IsNil() bool {
return v.vtype.IsNil()
}
func (v Value) IsString() bool {
return v.vtype.IsString()
}
func (v Value) IsBool() bool {
return v.vtype.IsBool()
}
func (v Value) IsInt() bool {
return v.vtype.IsInt()
}
func (v Value) IsInt8() bool {
return v.vtype.IsInt8()
}
func (v Value) IsInt16() bool {
return v.vtype.IsInt16()
}
func (v Value) IsInt32() bool {
return v.vtype.IsInt32()
}
func (v Value) IsInt64() bool {
return v.vtype.IsInt64()
}
func (v Value) IsUint() bool {
return v.vtype.IsUint()
}
func (v Value) IsUint8() bool {
return v.vtype.IsUint8()
}
func (v Value) IsUint16() bool {
return v.vtype.IsUint16()
}
func (v Value) IsUint32() bool {
return v.vtype.IsUint32()
}
func (v Value) IsUint64() bool {
return v.vtype.IsUint64()
}
func (v Value) IsFloat32() bool {
return v.vtype.IsFloat32()
}
func (v Value) IsFloat64() bool {
return v.vtype.IsFloat64()
}
func (v Value) IsComplex64() bool {
return v.vtype.IsComplex64()
}
func (v Value) IsComplex128() bool {
return v.vtype.IsComplex128()
}
func (v Value) IsUintptr() bool {
return v.vtype.IsUintptr()
}
func (v Value) IsBinary() bool {
return v.vtype.IsBinary()
}
func (v Value) IsDuration() bool {
return v.vtype.IsDuration()
}
func (v Value) IsTime() bool {
return v.vtype.IsTime()
}
func (v Value) IsError() bool {
return v.vtype.IsError()
}
func (v Value) asString() string {
return v.stringly
}
func (v Value) asBool() bool {
return v.numeric == 1
}
func (v Value) asInt() int {
return int(v.numeric)
}
func (v Value) asInt8() int8 {
return int8(v.numeric)
}
func (v Value) asInt16() int16 {
return int16(v.numeric)
}
func (v Value) asInt32() int32 {
return int32(v.numeric)
}
func (v Value) asInt64() int64 {
return int64(v.numeric)
}
func (v Value) asUint() uint {
return uint(v.numeric)
}
func (v Value) asUint8() uint8 {
return uint8(v.numeric)
}
func (v Value) asUint16() uint16 {
return uint16(v.numeric)
}
func (v Value) asUint32() uint32 {
return uint32(v.numeric)
}
func (v Value) asUint64() uint64 {
return v.numeric
}
func (v Value) asFloat32() float32 {
return math.Float32frombits(uint32(v.numeric))
}
func (v Value) asFloat64() float64 {
return math.Float64frombits(v.numeric)
}
func (v Value) asComplex64() complex64 {
return v.value.(complex64)
}
func (v Value) asComplex128() complex128 {
return v.value.(complex128)
}
func (v Value) asUintptr() uintptr {
return v.value.(uintptr)
}
func (v Value) asBinary() []byte {
return v.value.([]byte)
}
func (v Value) asDuration() time.Duration {
return v.value.(time.Duration)
}
func (v Value) asTime() time.Time {
return v.value.(time.Time)
}
func (v Value) asError() error {
return v.value.(error)
}
func nilValue(t Type) Value {
return Value{vtype: t | TypeNil}
}
func stringValue(v string) Value {
return Value{
stringly: v,
vtype: TypeString,
}
}
func stringsValue(v []string) Value {
return Value{
value: v,
vtype: TypeString | TypeArray,
}
}
func stringpValue(v *string) Value {
if v != nil {
return stringValue(*v)
}
return nilValue(TypeString)
}
func boolValue(b bool) Value {
if b {
return Value{
numeric: 1,
vtype: TypeBool,
}
}
return Value{
vtype: TypeBool,
}
}
func boolsValue(b []bool) Value {
return Value{
value: b,
vtype: TypeBool | TypeArray,
}
}
func boolpValue(b *bool) Value {
if b != nil {
return boolValue(*b)
}
return nilValue(TypeBool)
}
func intValue(i int) Value {
return Value{
vtype: TypeInt,
numeric: uint64(i),
}
}
func intsValue(i []int) Value {
return Value{
value: i,
vtype: TypeInt | TypeArray,
}
}
func intpValue(in *int) Value {
if in != nil {
return intValue(*in)
}
return nilValue(TypeInt)
}
func int8Value(i int8) Value {
return Value{
vtype: TypeInt8,
numeric: uint64(i),
}
}
func int8sValue(i []int8) Value {
return Value{
value: i,
vtype: TypeInt8 | TypeArray,
}
}
func int8pValue(in *int8) Value {
if in != nil {
return int8Value(*in)
}
return nilValue(TypeInt8)
}
func int16Value(i int16) Value {
return Value{
vtype: TypeInt16,
numeric: uint64(i),
}
}
func int16sValue(i []int16) Value {
return Value{
value: i,
vtype: TypeInt16 | TypeArray,
}
}
func int16pValue(in *int16) Value {
if in != nil {
return int16Value(*in)
}
return nilValue(TypeInt16)
}
func int32Value(i int32) Value {
return Value{
vtype: TypeInt32,
numeric: uint64(i),
}
}
func int32sValue(i []int32) Value {
return Value{
value: i,
vtype: TypeInt32 | TypeArray,
}
}
func int32pValue(in *int32) Value {
if in != nil {
return int32Value(*in)
}
return nilValue(TypeInt32)
}
func int64Value(i int64) Value {
return Value{
vtype: TypeInt64,
numeric: uint64(i),
}
}
func int64sValue(i []int64) Value {
return Value{
value: i,
vtype: TypeInt64 | TypeArray,
}
}
func int64pValue(in *int64) Value {
if in != nil {
return int64Value(*in)
}
return nilValue(TypeInt64)
}
func uintValue(i uint) Value {
return Value{
vtype: TypeUint,
numeric: uint64(i),
}
}
func uintsValue(i []uint) Value {
return Value{
value: i,
vtype: TypeUint | TypeArray,
}
}
func uintpValue(in *uint) Value {
if in != nil {
return uintValue(*in)
}
return nilValue(TypeUint)
}
func uint8Value(i uint8) Value {
return Value{
vtype: TypeUint8,
numeric: uint64(i),
}
}
func uint8sValue(i []uint8) Value {
return Value{
value: i,
vtype: TypeUint8 | TypeArray,
}
}
func uint8pValue(in *uint8) Value {
if in != nil {
return uint8Value(*in)
}
return nilValue(TypeUint8)
}
func uint16Value(i uint16) Value {
return Value{
vtype: TypeUint16,
numeric: uint64(i),
}
}
func uint16sValue(i []uint16) Value {
return Value{
value: i,
vtype: TypeUint16 | TypeArray,
}
}
func uint16pValue(in *uint16) Value {
if in != nil {
return uint16Value(*in)
}
return nilValue(TypeUint16)
}
func uint32Value(i uint32) Value {
return Value{
vtype: TypeUint32,
numeric: uint64(i),
}
}
func uint32sValue(i []uint32) Value {
return Value{
value: i,
vtype: TypeUint32 | TypeArray,
}
}
func uint32pValue(in *uint32) Value {
if in != nil {
return uint32Value(*in)
}
return nilValue(TypeUint32)
}
func uint64Value(i uint64) Value {
return Value{
vtype: TypeUint64,
numeric: i,
}
}
func uint64sValue(i []uint64) Value {
return Value{
value: i,
vtype: TypeUint64 | TypeArray,
}
}
func uint64pValue(in *uint64) Value {
if in != nil {
return uint64Value(*in)
}
return nilValue(TypeUint64)
}
func float32Value(i float32) Value {
return Value{
vtype: TypeFloat32,
numeric: uint64(math.Float32bits(i)),
}
}
func float32sValue(i []float32) Value {
return Value{
value: i,
vtype: TypeFloat32 | TypeArray,
}
}
func float32pValue(in *float32) Value {
if in != nil {
return float32Value(*in)
}
return nilValue(TypeFloat32)
}
func float64Value(i float64) Value {
return Value{
vtype: TypeFloat64,
numeric: math.Float64bits(i),
}
}
func float64sValue(i []float64) Value {
return Value{
value: i,
vtype: TypeFloat64 | TypeArray,
}
}
func float64pValue(in *float64) Value {
if in != nil {
return float64Value(*in)
}
return nilValue(TypeFloat64)
}
func complex64Value(in complex64) Value {
return Value{
vtype: TypeComplex64,
value: in,
}
}
func complex64sValue(in []complex64) Value {
return Value{
vtype: TypeComplex64 | TypeArray,
value: in,
}
}
func complex64pValue(in *complex64) Value {
if in != nil {
return complex64Value(*in)
}
return nilValue(TypeComplex64)
}
func complex128Value(in complex128) Value {
return Value{
vtype: TypeComplex64,
value: in,
}
}
func complex128sValue(in []complex128) Value {
return Value{
vtype: TypeComplex128 | TypeArray,
value: in,
}
}
func complex128pValue(in *complex128) Value {
if in != nil {
return complex128Value(*in)
}
return nilValue(TypeComplex128)
}
func uintptrValue(in uintptr) Value {
return Value{
vtype: TypeUintptr,
value: in,
}
}
func uintptrsValue(in []uintptr) Value {
return Value{
vtype: TypeUintptr | TypeArray,
value: in,
}
}
func uintptrpValue(in *uintptr) Value {
if in != nil {
return uintptrValue(*in)
}
return nilValue(TypeUintptr)
}
func bytesValue(in []byte) Value {
return Value{
vtype: TypeBinary,
value: in,
}
}
func durationValue(in time.Duration) Value {
return Value{
vtype: TypeDuration,
value: in,
}
}
func durationsValue(in []time.Duration) Value {
return Value{
vtype: TypeDuration | TypeArray,
value: in,
}
}
func durationpValue(in *time.Duration) Value {
if in != nil {
return durationValue(*in)
}
return nilValue(TypeDuration)
}
func timeValue(in time.Time) Value {
return formatTimeValue(time.RFC3339, in)
}
func timesValue(in []time.Time) Value {
return formatTimesValue(time.RFC3339, in)
}
func timepValue(in *time.Time) Value {
return formatTimepValue(time.RFC3339, in)
}
func formatTimeValue(format string, in time.Time) Value {
return Value{
vtype: TypeTime,
value: in,
stringly: format,
}
}
func formatTimesValue(format string, in []time.Time) Value {
return Value{
vtype: TypeTime | TypeArray,
value: in,
stringly: format,
}
}
func formatTimepValue(format string, in *time.Time) Value {
if in != nil {
return formatTimeValue(format, *in)
}
return nilValue(TypeTime)
}
func errorValue(in error) Value {
if in != nil {
return Value{
vtype: TypeError,
value: in,
}
}
return nilValue(TypeError)
}
func errorsValue(in []error) Value {
return Value{
vtype: TypeError | TypeArray,
value: in,
}
}

75
global.go

@ -2,18 +2,27 @@ package log
import ( import (
"context" "context"
"io"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
) )
//nolint:gochecknoglobals //nolint:gochecknoglobals
var global = With(New(), WithLevel(LevelDebug)) var global = With(New(),
WithCaller("caller", 1, false),
WithLevel("level", level.Debug),
WithExit(level.Alert),
WithPanic(level.Emergency),
)
// SetLogger sets global used logger. This function is not thread-safe. // SetLogger sets global used logger. This function is not thread-safe.
func SetLogger(l Logger) { func SetLogger(l Logger) {
global = l global = l
} }
// GetLogger return global logger. // Log return global logger.
func GetLogger() Logger { func Log() Logger {
return global return global
} }
@ -87,43 +96,83 @@ func Panicln(args ...interface{}) {
global.Panicln(args...) global.Panicln(args...)
} }
// EmergKVs sugared log by emergency level and key-values.
func EmergKVs(ctx context.Context, msg string, args ...interface{}) {
global.EmergKVs(ctx, msg, args...)
}
// AlertKVs sugared log by alert level and key-values.
func AlertKVs(ctx context.Context, msg string, args ...interface{}) {
global.AlertKVs(ctx, msg, args...)
}
// CritKVs sugared log by critcal level and key-values.
func CritKVs(ctx context.Context, msg string, args ...interface{}) {
global.CritKVs(ctx, msg, args...)
}
// ErrKVs sugared log by error level and key-values.
func ErrKVs(ctx context.Context, msg string, args ...interface{}) {
global.ErrKVs(ctx, msg, args...)
}
// WarnKVs sugared log by warning level and key-values.
func WarnKVs(ctx context.Context, msg string, args ...interface{}) {
global.WarnKVs(ctx, msg, args...)
}
// NoticeKVs sugared log by notice level and key-values.
func NoticeKVs(ctx context.Context, msg string, args ...interface{}) {
global.NoticeKVs(ctx, msg, args...)
}
// InfoKVs sugared log by info level and key-values.
func InfoKVs(ctx context.Context, msg string, args ...interface{}) {
global.InfoKVs(ctx, msg, args...)
}
// DebugKVs sugared log by debug level and key-values.
func DebugKVs(ctx context.Context, msg string, args ...interface{}) {
global.DebugKVs(ctx, msg, args...)
}
// EmergKV log by emergency level and key-values. // EmergKV log by emergency level and key-values.
func EmergKV(ctx context.Context, msg string, args ...interface{}) { func EmergKV(ctx context.Context, msg string, args ...field.Field) {
global.EmergKV(ctx, msg, args...) global.EmergKV(ctx, msg, args...)
} }
// AlertKV log by alert level and key-values. // AlertKV log by alert level and key-values.
func AlertKV(ctx context.Context, msg string, args ...interface{}) { func AlertKV(ctx context.Context, msg string, args ...field.Field) {
global.AlertKV(ctx, msg, args...) global.AlertKV(ctx, msg, args...)
} }
// CritKV log by critcal level and key-values. // CritKV log by critcal level and key-values.
func CritKV(ctx context.Context, msg string, args ...interface{}) { func CritKV(ctx context.Context, msg string, args ...field.Field) {
global.CritKV(ctx, msg, args...) global.CritKV(ctx, msg, args...)
} }
// ErrKV log by error level and key-values. // ErrKV log by error level and key-values.
func ErrKV(ctx context.Context, msg string, args ...interface{}) { func ErrKV(ctx context.Context, msg string, args ...field.Field) {
global.ErrKV(ctx, msg, args...) global.ErrKV(ctx, msg, args...)
} }
// WarnKV log by warning level and key-values. // WarnKV log by warning level and key-values.
func WarnKV(ctx context.Context, msg string, args ...interface{}) { func WarnKV(ctx context.Context, msg string, args ...field.Field) {
global.WarnKV(ctx, msg, args...) global.WarnKV(ctx, msg, args...)
} }
// NoticeKV log by notice level and key-values. // NoticeKV log by notice level and key-values.
func NoticeKV(ctx context.Context, msg string, args ...interface{}) { func NoticeKV(ctx context.Context, msg string, args ...field.Field) {
global.NoticeKV(ctx, msg, args...) global.NoticeKV(ctx, msg, args...)
} }
// InfoKV log by info level and key-values. // InfoKV log by info level and key-values.
func InfoKV(ctx context.Context, msg string, args ...interface{}) { func InfoKV(ctx context.Context, msg string, args ...field.Field) {
global.InfoKV(ctx, msg, args...) global.InfoKV(ctx, msg, args...)
} }
// DebugKV log by debug level and key-values. // DebugKV log by debug level and key-values.
func DebugKV(ctx context.Context, msg string, args ...interface{}) { func DebugKV(ctx context.Context, msg string, args ...field.Field) {
global.DebugKV(ctx, msg, args...) global.DebugKV(ctx, msg, args...)
} }
@ -181,3 +230,7 @@ func Fatalf(format string, args ...interface{}) {
func Panicf(format string, args ...interface{}) { func Panicf(format string, args ...interface{}) {
global.Panicf(format, args...) global.Panicf(format, args...)
} }
func Writer(ctx context.Context, level level.Level) io.Writer {
return global.Writer(ctx, level)
}

9
go.mod

@ -1,3 +1,10 @@
module gitoa.ru/go-4devs/log module gitoa.ru/go-4devs/log
go 1.14 go 1.15
require (
github.com/sirupsen/logrus v1.7.0
go.opentelemetry.io/otel v0.13.0
go.opentelemetry.io/otel/sdk v0.13.0
go.uber.org/zap v1.16.0
)

76
go.sum

@ -0,0 +1,76 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/sketches-go v0.0.1/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA=
go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
go.opentelemetry.io/otel/sdk v0.13.0 h1:4VCfpKamZ8GtnepXxMRurSpHpMKkcxhtO33z1S4rGDQ=
go.opentelemetry.io/otel/sdk v0.13.0/go.mod h1:dKvLH8Uu8LcEPlSAUsfW7kMGaJBhk/1NYvpPZ6wIMbU=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

90
handler/logrus/logger.go

@ -0,0 +1,90 @@
package logrus
import (
"context"
"github.com/sirupsen/logrus"
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/level"
)
// Option configure logger.
type Option func(*logger)
// WithLevel sets callback level to log level.
func WithLevel(level level.Level, c func(*logrus.Entry, string)) Option {
return func(l *logger) {
l.levels[level] = c
}
}
// WithLogrus sets logrus logger.
func WithLogrus(logrus *logrus.Logger) Option {
return func(l *logger) {
l.logrus = logrus
}
}
// New create new logrus handler.
func New(opts ...Option) log.Logger {
log := logger{
logrus: logrus.StandardLogger(),
levels: map[level.Level]func(*logrus.Entry, string){
level.Emergency: panicLog,
level.Alert: fatalLog,
level.Critical: errorLog,
level.Error: errorLog,
level.Warning: warnLog,
level.Notice: infoLog,
level.Info: infoLog,
level.Debug: debugLog,
},
}
for _, o := range opts {
o(&log)
}
return log.log
}
type logger struct {
levels map[level.Level]func(l *logrus.Entry, msg string)
logrus *logrus.Logger
}
func (l *logger) log(ctx context.Context, e *entry.Entry) (int, error) {
lrgFields := make(logrus.Fields, e.Fields().Len())
for _, field := range e.Fields() {
lrgFields[string(field.Key())] = field.AsInterface()
}
l.levels[e.Level()](l.logrus.WithFields(lrgFields), e.Message())
return 0, nil
}
func panicLog(e *logrus.Entry, msg string) {
e.Panic(msg)
}
func fatalLog(e *logrus.Entry, msg string) {
e.Fatal(msg)
}
func errorLog(e *logrus.Entry, msg string) {
e.Error(msg)
}
func warnLog(e *logrus.Entry, msg string) {
e.Warn(msg)
}
func infoLog(e *logrus.Entry, msg string) {
e.Info(msg)
}
func debugLog(e *logrus.Entry, msg string) {
e.Debug(msg)
}

36
handler/logrus/logger_test.go

@ -0,0 +1,36 @@
package logrus_test
import (
"bytes"
"context"
"strings"
"testing"
lgr "github.com/sirupsen/logrus"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/handler/logrus"
"gitoa.ru/go-4devs/log/level"
)
func TestNew(t *testing.T) {
ctx := context.Background()
buf := &bytes.Buffer{}
lgrus := lgr.New()
lgrus.SetLevel(lgr.DebugLevel)
lgrus.SetOutput(buf)
lgrus.SetFormatter(&lgr.TextFormatter{
DisableTimestamp: true,
})
handler := logrus.New(logrus.WithLogrus(lgrus))
expect := "level=info msg=\"handle logrus message\"\n"
if _, err := handler(ctx, entry.New(entry.WithLevel(level.Info), entry.WithMessage("handle logrus message"))); err != nil {
t.Error(err)
}
if !strings.HasSuffix(buf.String(), expect) {
t.Errorf("invalid suffix\n got: %s\nexpect:%s\n", buf.String(), expect)
}
}

56
handler/otel/helpers.go

@ -0,0 +1,56 @@
package otel
import (
"context"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/level"
"go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/label"
)
const (
fieldSeverityNumber = "SeverityNumber"
fieldSeverityText = "SeverityText"
levelFields = 2
)
func levels(lvl level.Level) Level {
switch lvl {
case level.Emergency:
return levelError3
case level.Alert:
return levelFatal
case level.Critical:
return levelError2
case level.Error:
return levelError
case level.Warning:
return levelWarn
case level.Notice:
return levelInfo2
case level.Info:
return levelInfo
case level.Debug:
return levelDebug
}
return 0
}
func addEvent(ctx context.Context, e *entry.Entry) {
span := trace.SpanFromContext(ctx)
attrs := make([]label.KeyValue, 0, e.Fields().Len()+levelFields)
lvl := levels(e.Level())
attrs = append(attrs,
label.String(fieldSeverityText, lvl.String()),
label.Int(fieldSeverityNumber, int(lvl)),
)
for _, field := range e.Fields() {
attrs = append(attrs, label.String(string(field.Key()), field.Value().String()))
}
span.AddEvent(ctx, e.Message(), attrs...)
}

16
handler/otel/level.go

@ -0,0 +1,16 @@
package otel
//go:generate stringer -type=Level -linecomment -output=level_string.go
type Level int
const (
levelDebug Level = 5 // DEBUG
levelInfo Level = 9 // INFO
levelInfo2 Level = 10 // INFO2
levelWarn Level = 13 // WARN
levelError Level = 17 // ERROR
levelError2 Level = 18 // ERROR2
levelError3 Level = 19 // ERROR3
levelFatal Level = 21 // FATAL
)

51
handler/otel/level_string.go

@ -0,0 +1,51 @@
// Code generated by "stringer -type=Level -linecomment -output=level_string.go"; DO NOT EDIT.
package otel
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[levelDebug-5]
_ = x[levelInfo-9]
_ = x[levelInfo2-10]
_ = x[levelWarn-13]
_ = x[levelError-17]
_ = x[levelError2-18]
_ = x[levelError3-19]
_ = x[levelFatal-21]
}
const (
_Level_name_0 = "DEBUG"
_Level_name_1 = "INFOINFO2"
_Level_name_2 = "WARN"
_Level_name_3 = "ERRORERROR2ERROR3"
_Level_name_4 = "FATAL"
)
var (
_Level_index_1 = [...]uint8{0, 4, 9}
_Level_index_3 = [...]uint8{0, 5, 11, 17}
)
func (i Level) String() string {
switch {
case i == 5:
return _Level_name_0
case 9 <= i && i <= 10:
i -= 9
return _Level_name_1[_Level_index_1[i]:_Level_index_1[i+1]]
case i == 13:
return _Level_name_2
case 17 <= i && i <= 19:
i -= 17
return _Level_name_3[_Level_index_3[i]:_Level_index_3[i+1]]
case i == 21:
return _Level_name_4
default:
return "Level(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

16
handler/otel/logger.go

@ -0,0 +1,16 @@
package otel
import (
"context"
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/entry"
)
func New() log.Logger {
return func(ctx context.Context, e *entry.Entry) (int, error) {
addEvent(ctx, e)
return 0, nil
}
}

16
handler/otel/middleware.go

@ -0,0 +1,16 @@
package otel
import (
"context"
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/entry"
)
func Middleware() log.Middleware {
return func(ctx context.Context, e *entry.Entry, handler log.Logger) (int, error) {
addEvent(ctx, e)
return handler(ctx, e)
}
}

95
handler/zap/logger.go

@ -0,0 +1,95 @@
package zap
import (
"context"
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/level"
"go.uber.org/zap"
)
// Option configure logger.
type Option func(*logger)
// WithLevel sets level logged message.
func WithLevel(level level.Level, f func(z *zap.Logger, msg string, fields ...zap.Field)) Option {
return func(l *logger) {
l.levels[level] = f
}
}
// WithZap sets zap logger.
func WithZap(z *zap.Logger) Option {
return func(l *logger) {
l.zap = z
}
}
// New create handler by zap logger.
func New(opts ...Option) log.Logger {
z, err := zap.NewDevelopment()
if err != nil {
panic(err)
}
log := logger{
zap: z,
levels: map[level.Level]func(z *zap.Logger, msg string, fields ...zap.Field){
level.Emergency: fatalLog,
level.Alert: panicLog,
level.Critical: errorLog,
level.Error: errorLog,
level.Warning: warnLog,
level.Notice: infoLog,
level.Info: infoLog,
level.Debug: debugLog,
},
}
for _, opt := range opts {
opt(&log)
}
return log.log
}
type logger struct {
zap *zap.Logger
levels map[level.Level]func(z *zap.Logger, msg string, fields ...zap.Field)
}
func (l *logger) log(ctx context.Context, e *entry.Entry) (int, error) {
zf := make([]zap.Field, e.Fields().Len())
for i, field := range e.Fields() {
zf[i] = zap.Any(string(field.Key()), field.AsInterface())
}
l.levels[e.Level()](l.zap, e.Message(), zf...)
return 0, nil
}
func panicLog(z *zap.Logger, msg string, fields ...zap.Field) {
z.Panic(msg, fields...)
}
func fatalLog(z *zap.Logger, msg string, fields ...zap.Field) {
z.Fatal(msg, fields...)
}
func errorLog(z *zap.Logger, msg string, fields ...zap.Field) {
z.Error(msg, fields...)
}
func warnLog(z *zap.Logger, msg string, fields ...zap.Field) {
z.Warn(msg, fields...)
}
func infoLog(z *zap.Logger, msg string, fields ...zap.Field) {
z.Info(msg, fields...)
}
func debugLog(z *zap.Logger, msg string, fields ...zap.Field) {
z.Debug(msg, fields...)
}

41
handler/zap/logger_test.go

@ -0,0 +1,41 @@
package zap_test
import (
"bytes"
"context"
"testing"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/field"
zlog "gitoa.ru/go-4devs/log/handler/zap"
"gitoa.ru/go-4devs/log/level"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func TestNew(t *testing.T) {
ctx := context.Background()
buf := &bytes.Buffer{}
core := zapcore.NewCore(zapcore.NewJSONEncoder(zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
NameKey: "logger",
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
}), zapcore.AddSync(buf), zapcore.DebugLevel)
logger := zlog.New(zlog.WithZap(zap.New(core)))
expect := `{"level":"info","msg":"handle zap message","env":"test"}` + "\n"
if _, err := logger(ctx, entry.New(
entry.WithFields(field.String("env", "test")),
entry.WithLevel(level.Notice),
entry.WithMessage("handle zap message"),
)); err != nil {
t.Error(err)
}
if buf.String() != expect {
t.Errorf("invalid message\n got: %s\nexpect:%s\n", buf.String(), expect)
}
}

22
level.go

@ -1,22 +0,0 @@
package log
//go:generate stringer -type=Level -linecomment
// Level log.
type Level uint8
// awailable log levels.
const (
LevelEmergency Level = iota // emergency
LevelAlert // alert
LevelCritical // critical
LevelError // error
LevelWarning // warning
LevelNotice // notice
LevelInfo // info
LevelDebug // debug
)
func (l Level) MarshalJSON() ([]byte, error) {
return []byte("\"" + l.String() + "\""), nil
}

73
level/level.go

@ -0,0 +1,73 @@
package level
import (
"encoding/json"
"strings"
)
//go:generate stringer -type=Level -linecomment
var (
_ json.Marshaler = Level(0)
_ json.Unmarshaler = (*Level)(nil)
)
// Level log.
type Level uint32
// available log levels.
const (
Emergency Level = iota // emergency
Alert // alert
Critical // critical
Error // error
Warning // warning
Notice // notice
Info // info
Debug // debug
)
func (l Level) MarshalJSON() ([]byte, error) {
return json.Marshal(l.String())
}
func (l Level) Is(level Level) bool {
return level == l
}
func (l Level) Enabled(level Level) bool {
return l <= level
}
func (l *Level) UnmarshalJSON(in []byte) error {
var v string
if err := json.Unmarshal(in, &v); err != nil {
return err
}
lvl := Parse(v)
*l = lvl
return nil
}
func Parse(lvl string) Level {
switch strings.ToLower(lvl) {
case "debug", "Debug", "DEBUG":
return Debug
case "info", "Info", "INFO":
return Info
case "notice", "Notice", "NOTICE":
return Notice
case "warning", "Warning", "WARNING":
return Warning
case "error", "Error", "ERROR":
return Error
case "critical", "Critical", "CRITICAL":
return Critical
case "alert", "Alert", "ALERT":
return Alert
default:
return Emergency
}
}

18
level_string.go → level/level_string.go

@ -1,6 +1,6 @@
// Code generated by "stringer -type=Level -linecomment"; DO NOT EDIT. // Code generated by "stringer -type=Level -linecomment"; DO NOT EDIT.
package log package level
import "strconv" import "strconv"
@ -8,14 +8,14 @@ func _() {
// An "invalid array index" compiler error signifies that the constant values have changed. // An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again. // Re-run the stringer command to generate them again.
var x [1]struct{} var x [1]struct{}
_ = x[LevelEmergency-0] _ = x[Emergency-0]
_ = x[LevelAlert-1] _ = x[Alert-1]
_ = x[LevelCritical-2] _ = x[Critical-2]
_ = x[LevelError-3] _ = x[Error-3]
_ = x[LevelWarning-4] _ = x[Warning-4]
_ = x[LevelNotice-5] _ = x[Notice-5]
_ = x[LevelInfo-6] _ = x[Info-6]
_ = x[LevelDebug-7] _ = x[Debug-7]
} }
const _Level_name = "emergencyalertcriticalerrorwarningnoticeinfodebug" const _Level_name = "emergencyalertcriticalerrorwarningnoticeinfodebug"

221
logger.go

@ -2,39 +2,83 @@ package log
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io"
"os"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
)
var _ io.Writer = (Logger)(nil)
var (
ErrIgnoredKey = errors.New("ignored key without a value")
ErrNonStringKeys = errors.New("ignored key-value pairs with non-string keys")
) )
func writeOutput(_ int, err error) {
if err != nil {
fmt.Fprint(os.Stderr, err)
}
}
// Logger logged message. // Logger logged message.
type Logger func(ctx context.Context, level Level, msg string, fields Fields) type Logger func(ctx context.Context, entry *entry.Entry) (int, error)
func (l Logger) log(ctx context.Context, level Level, args ...interface{}) { func (l Logger) log(ctx context.Context, level level.Level, args ...interface{}) {
l(ctx, level, fmt.Sprint(args...), nil) writeOutput(l.write(ctx, level, fmt.Sprint(args...)))
} }
func (l Logger) logKV(ctx context.Context, level Level, msg string, args ...interface{}) { func (l Logger) Write(in []byte) (int, error) {
l(ctx, level, msg, l.kv(ctx, args...)) return l.write(context.Background(), level.Info, string(in))
} }
func (l Logger) logf(ctx context.Context, level Level, format string, args ...interface{}) { func (l Logger) write(ctx context.Context, level level.Level, msg string, fields ...field.Field) (int, error) {
l(ctx, level, fmt.Sprintf(format, args...), nil) e := entry.Get()
defer func() {
entry.Put(e)
}()
return l(ctx, e.SetLevel(level).SetMessage(msg).Add(fields...))
} }
func (l Logger) logln(ctx context.Context, level Level, args ...interface{}) { func (l Logger) logKVs(ctx context.Context, level level.Level, msg string, args ...interface{}) {
l(ctx, level, fmt.Sprintln(args...), nil) writeOutput(l.write(ctx, level, msg, l.kv(ctx, args...)...))
} }
func (l Logger) kv(ctx context.Context, args ...interface{}) []Field { func (l Logger) logKV(ctx context.Context, level level.Level, msg string, fields ...field.Field) {
fields := make([]Field, 0, len(args)) writeOutput(l.write(ctx, level, msg, fields...))
}
func (l Logger) logf(ctx context.Context, level level.Level, format string, args ...interface{}) {
writeOutput(l.write(ctx, level, fmt.Sprintf(format, args...)))
}
func (l Logger) logln(ctx context.Context, level level.Level, args ...interface{}) {
writeOutput(l.write(ctx, level, fmt.Sprintln(args...)))
}
func (l Logger) kv(ctx context.Context, args ...interface{}) field.Fields {
e := entry.Get()
defer func() {
entry.Put(e)
}()
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
if f, ok := args[i].(Field); ok { if f, ok := args[i].(field.Field); ok {
fields = append(fields, f) e = e.Add(f)
continue continue
} }
if i == len(args)-1 { if i == len(args)-1 {
l(ctx, LevelCritical, fmt.Sprint("Ignored key without a value.", args[i]), fields) l.logKV(ctx, level.Critical, fmt.Sprint("Ignored key without a value.", args[i]), e.Fields()...)
break break
} }
@ -42,14 +86,15 @@ func (l Logger) kv(ctx context.Context, args ...interface{}) []Field {
key, val := args[i-1], args[i] key, val := args[i-1], args[i]
if keyStr, ok := key.(string); ok { if keyStr, ok := key.(string); ok {
fields = append(fields, Field{Key: keyStr, Value: val}) e = e.AddAny(keyStr, val)
continue continue
} }
l(ctx, LevelCritical, fmt.Sprint("Ignored key-value pairs with non-string keys.", key, val), fields) l.logKV(ctx, level.Critical, fmt.Sprint("Ignored key-value pairs with non-string keys.", key, val), e.Fields()...)
} }
return fields return e.Fields()
} }
// With adds middlewares to logger. // With adds middlewares to logger.
@ -59,165 +104,225 @@ func (l Logger) With(mw ...Middleware) Logger {
// Emerg log by emergency level. // Emerg log by emergency level.
func (l Logger) Emerg(ctx context.Context, args ...interface{}) { func (l Logger) Emerg(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelEmergency, args...) l.log(ctx, level.Emergency, args...)
} }
// Alert log by alert level. // Alert log by alert level.
func (l Logger) Alert(ctx context.Context, args ...interface{}) { func (l Logger) Alert(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelAlert, args...) l.log(ctx, level.Alert, args...)
} }
// Crit log by critical level. // Crit log by critical level.
func (l Logger) Crit(ctx context.Context, args ...interface{}) { func (l Logger) Crit(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelCritical, args...) l.log(ctx, level.Critical, args...)
} }
// Err log by error level. // Err log by error level.
func (l Logger) Err(ctx context.Context, args ...interface{}) { func (l Logger) Err(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelError, args...) l.log(ctx, level.Error, args...)
} }
// Warn log by warning level. // Warn log by warning level.
func (l Logger) Warn(ctx context.Context, args ...interface{}) { func (l Logger) Warn(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelWarning, args...) l.log(ctx, level.Warning, args...)
} }
// Notice log by notice level. // Notice log by notice level.
func (l Logger) Notice(ctx context.Context, args ...interface{}) { func (l Logger) Notice(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelNotice, args...) l.log(ctx, level.Notice, args...)
} }
// Info log by info level. // Info log by info level.
func (l Logger) Info(ctx context.Context, args ...interface{}) { func (l Logger) Info(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelInfo, args...) l.log(ctx, level.Info, args...)
} }
// Debug log by debug level. // Debug log by debug level.
func (l Logger) Debug(ctx context.Context, args ...interface{}) { func (l Logger) Debug(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelDebug, args...) l.log(ctx, level.Debug, args...)
} }
// Print log by info level and arguments. // Print log by info level and arguments.
func (l Logger) Print(args ...interface{}) { func (l Logger) Print(args ...interface{}) {
l.log(context.Background(), LevelInfo, args...) l.log(context.Background(), level.Info, args...)
} }
// Fatal log by alert level and arguments. // Fatal log by alert level and arguments.
func (l Logger) Fatal(args ...interface{}) { func (l Logger) Fatal(args ...interface{}) {
l.log(context.Background(), LevelAlert, args...) l.log(context.Background(), level.Alert, args...)
} }
// Panic log by emergency level and arguments. // Panic log by emergency level and arguments.
func (l Logger) Panic(args ...interface{}) { func (l Logger) Panic(args ...interface{}) {
l.log(context.Background(), LevelEmergency, args...) l.log(context.Background(), level.Emergency, args...)
} }
// Println log by info level and arguments. // Println log by info level and arguments.
func (l Logger) Println(args ...interface{}) { func (l Logger) Println(args ...interface{}) {
l.logln(context.Background(), LevelInfo, args...) l.logln(context.Background(), level.Info, args...)
} }
// Fatalln log by alert level and arguments. // Fatalln log by alert level and arguments.
func (l Logger) Fatalln(args ...interface{}) { func (l Logger) Fatalln(args ...interface{}) {
l.logln(context.Background(), LevelAlert, args...) l.logln(context.Background(), level.Alert, args...)
} }
// Panicln log by emergency level and arguments. // Panicln log by emergency level and arguments.
func (l Logger) Panicln(args ...interface{}) { func (l Logger) Panicln(args ...interface{}) {
l.logln(context.Background(), LevelEmergency, args...) l.logln(context.Background(), level.Emergency, args...)
}
// EmergKVs sugared log by emergency level and key-values.
func (l Logger) EmergKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Emergency, msg, args...)
}
// AlertKVs sugared log by alert level and key-values.
func (l Logger) AlertKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Alert, msg, args...)
}
// CritKVs sugared log by critcal level and key-values.
func (l Logger) CritKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Critical, msg, args...)
}
// ErrKVs sugared log by error level and key-values.
func (l Logger) ErrKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Error, msg, args...)
}
// WarnKVs sugared log by warning level and key-values.
func (l Logger) WarnKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Warning, msg, args...)
}
// NoticeKVs sugared log by notice level and key-values.
func (l Logger) NoticeKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Notice, msg, args...)
}
// InfoKVs sugared log by info level and key-values.
func (l Logger) InfoKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Info, msg, args...)
}
// DebugKVs sugared log by debug level and key-values.
func (l Logger) DebugKVs(ctx context.Context, msg string, args ...interface{}) {
l.logKVs(ctx, level.Debug, msg, args...)
} }
// EmergKV log by emergency level and key-values. // EmergKV log by emergency level and key-values.
func (l Logger) EmergKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) EmergKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelEmergency, msg, args...) l.logKV(ctx, level.Emergency, msg, args...)
} }
// AlertKV log by alert level and key-values. // AlertKV log by alert level and key-values.
func (l Logger) AlertKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) AlertKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelAlert, msg, args...) l.logKV(ctx, level.Alert, msg, args...)
} }
// CritKV log by critcal level and key-values. // CritKV log by critcal level and key-values.
func (l Logger) CritKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) CritKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelCritical, msg, args...) l.logKV(ctx, level.Critical, msg, args...)
} }
// ErrKV log by error level and key-values. // ErrKV log by error level and key-values.
func (l Logger) ErrKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) ErrKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelError, msg, args...) l.logKV(ctx, level.Error, msg, args...)
} }
// WarnKV log by warning level and key-values. // WarnKV log by warning level and key-values.
func (l Logger) WarnKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) WarnKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelWarning, msg, args...) l.logKV(ctx, level.Warning, msg, args...)
} }
// NoticeKV log by notice level and key-values. // NoticeKV log by notice level and key-values.
func (l Logger) NoticeKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) NoticeKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelNotice, msg, args...) l.logKV(ctx, level.Notice, msg, args...)
} }
// InfoKV log by info level and key-values. // InfoKV log by info level and key-values.
func (l Logger) InfoKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) InfoKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelInfo, msg, args...) l.logKV(ctx, level.Info, msg, args...)
} }
// DebugKV log by debug level and key-values. // DebugKV log by debug level and key-values.
func (l Logger) DebugKV(ctx context.Context, msg string, args ...interface{}) { func (l Logger) DebugKV(ctx context.Context, msg string, args ...field.Field) {
l.logKV(ctx, LevelDebug, msg, args...) l.logKV(ctx, level.Debug, msg, args...)
} }
// Emergf log by emergency level by format and arguments. // Emergf log by emergency level by format and arguments.
func (l Logger) Emergf(ctx context.Context, format string, args ...interface{}) { func (l Logger) Emergf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelEmergency, format, args...) l.logf(ctx, level.Emergency, format, args...)
} }
// Alertf log by alert level by format and arguments. // Alertf log by alert level by format and arguments.
func (l Logger) Alertf(ctx context.Context, format string, args ...interface{}) { func (l Logger) Alertf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelAlert, format, args...) l.logf(ctx, level.Alert, format, args...)
} }
// Critf log by critical level by format and arguments. // Critf log by critical level by format and arguments.
func (l Logger) Critf(ctx context.Context, format string, args ...interface{}) { func (l Logger) Critf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelCritical, format, args...) l.logf(ctx, level.Critical, format, args...)
} }
// Errf log by error level by format and arguments. // Errf log by error level by format and arguments.
func (l Logger) Errf(ctx context.Context, format string, args ...interface{}) { func (l Logger) Errf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelError, format, args...) l.logf(ctx, level.Error, format, args...)
} }
// Warnf log by warning level by format and arguments. // Warnf log by warning level by format and arguments.
func (l Logger) Warnf(ctx context.Context, format string, args ...interface{}) { func (l Logger) Warnf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelWarning, format, args...) l.logf(ctx, level.Warning, format, args...)
} }
// Noticef log by notice level by format and arguments. // Noticef log by notice level by format and arguments.
func (l Logger) Noticef(ctx context.Context, format string, args ...interface{}) { func (l Logger) Noticef(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelNotice, format, args...) l.logf(ctx, level.Notice, format, args...)
} }
// Infof log by info level by format and arguments. // Infof log by info level by format and arguments.
func (l Logger) Infof(ctx context.Context, format string, args ...interface{}) { func (l Logger) Infof(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelInfo, format, args...) l.logf(ctx, level.Info, format, args...)
} }
// Debugf log by debug level by format and arguments. // Debugf log by debug level by format and arguments.
func (l Logger) Debugf(ctx context.Context, format string, args ...interface{}) { func (l Logger) Debugf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelDebug, format, args...) l.logf(ctx, level.Debug, format, args...)
} }
// Printf log by info level by format and arguments without context. // Printf log by info level by format and arguments without context.
func (l Logger) Printf(format string, args ...interface{}) { func (l Logger) Printf(format string, args ...interface{}) {
l.logf(context.Background(), LevelInfo, format, args...) l.logf(context.Background(), level.Info, format, args...)
} }
// Fatalf log by alert level by format and arguments without context. // Fatalf log by alert level by format and arguments without context.
func (l Logger) Fatalf(format string, args ...interface{}) { func (l Logger) Fatalf(format string, args ...interface{}) {
l.logf(context.Background(), LevelAlert, format, args...) l.logf(context.Background(), level.Alert, format, args...)
} }
// Panicf log by emergency level and arguments without context. // Panicf log by emergency level and arguments without context.
func (l Logger) Panicf(format string, args ...interface{}) { func (l Logger) Panicf(format string, args ...interface{}) {
l.logf(context.Background(), LevelEmergency, format, args...) l.logf(context.Background(), level.Emergency, format, args...)
}
func (l Logger) Writer(ctx context.Context, level level.Level, fields ...field.Field) io.Writer {
return writer{
ctx: ctx,
level: level,
Logger: l,
fields: fields,
}
}
type writer struct {
ctx context.Context
level level.Level
fields []field.Field
Logger
}
func (w writer) Write(in []byte) (int, error) {
return w.write(w.ctx, w.level, string(in), w.fields...)
} }

20
logger_example_caller_test.go

@ -0,0 +1,20 @@
package log_test
import (
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/level"
)
func ExampleNew_withCaller() {
logger := log.With(
log.New(log.WithStdout()),
log.WithLevel("level", level.Debug),
log.WithCaller("caller", 2, false),
)
logger.Err(ctx, "same error message")
logger.InfoKVs(ctx, "same info message", "api-version", 0.1)
// Output:
// msg="same error message" level=error caller=logger_example_caller_test.go:14
// msg="same info message" api-version=0.1 level=info caller=logger_example_caller_test.go:15
}

21
logger_example_logrus_test.go

@ -0,0 +1,21 @@
package log_test
import (
"io"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/handler/zap"
uzap "go.uber.org/zap"
)
func ExampleNew_zapHandler() {
log := zap.New(zap.WithZap(uzap.NewExample()))
log.Err(ctx, "log zap")
log.ErrKV(ctx, "log zap kv", field.Int("int", 42))
log.ErrKVs(ctx, "log zap kv sugar", "err", io.EOF)
// Output:
// {"level":"error","msg":"log zap"}
// {"level":"error","msg":"log zap kv","int":42}
// {"level":"error","msg":"log zap kv sugar","err":"EOF"}
}

81
logger_example_test.go

@ -3,15 +3,23 @@ package log_test
import ( import (
"context" "context"
"fmt" "fmt"
std "log"
"os" "os"
"sync/atomic"
"gitoa.ru/go-4devs/log" "gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
) )
//nolint:gochecknoglobals //nolint:gochecknoglobals
var ctx = context.Background() var ctx = context.Background()
func setStdout() {
// set stout for example by default stderror
log.SetLogger(log.New(log.WithStdout()).With(log.WithLevel("level", level.Debug)))
}
func ExampleNew() { func ExampleNew() {
logger := log.New(log.WithStdout()) logger := log.New(log.WithStdout())
logger.Info(ctx, "same message") logger.Info(ctx, "same message")
@ -19,16 +27,14 @@ func ExampleNew() {
} }
func ExampleInfo() { func ExampleInfo() {
std.SetOutput(os.Stdout) setStdout()
std.SetFlags(0)
log.Info(ctx, "same message") log.Info(ctx, "same message")
// Output: msg="same message" level=info // Output: msg="same message" level=info
} }
func ExampleErrKV() { func ExampleErrKV() {
std.SetOutput(os.Stdout) setStdout()
std.SetFlags(0) log.ErrKVs(ctx, "same message", "key", "addition value")
log.ErrKV(ctx, "same message", "key", "addition value")
// Output: msg="same message" key=addition value level=error // Output: msg="same message" key=addition value level=error
} }
@ -39,13 +45,13 @@ func ExampleNew_errf() {
} }
func ExampleNew_debugKV() { func ExampleNew_debugKV() {
logger := log.New(log.WithStdout()).With(log.WithLevel(log.LevelDebug)) logger := log.New(log.WithStdout()).With(log.WithLevel("level", level.Debug))
logger.DebugKV(ctx, "same message", "error", os.ErrNotExist) logger.DebugKVs(ctx, "same message", "error", os.ErrNotExist)
// Output: msg="same message" error=file does not exist level=debug // Output: msg="same message" error=file does not exist level=debug
} }
func ExampleNew_level() { func ExampleNew_level() {
logger := log.New(log.WithStdout()).With(log.WithLevel(log.LevelError)) logger := log.New(log.WithStdout()).With(log.WithLevel("level", level.Error))
logger.Info(ctx, "same message") logger.Info(ctx, "same message")
// Output: // Output:
@ -56,35 +62,25 @@ func ExampleNew_level() {
func ExampleNew_jsonFormat() { func ExampleNew_jsonFormat() {
logger := log.New(log.WithStdout(), log.WithJSONFormat()). logger := log.New(log.WithStdout(), log.WithJSONFormat()).
With( With(
log.WithCaller(4, true), log.WithLevel("level", level.Debug),
log.WithLevel(log.LevelDebug),
log.GoVersion("go-version"), log.GoVersion("go-version"),
) )
logger.Err(ctx, "same error message") logger.Err(ctx, "same error message")
// Output: {"caller":"logger_example_test.go:63","go-version":"go1.14.2","level":"error","msg":"same error message"} // Output: {"go-version":"go1.15.2","level":"error","msg":"same error message"}
} }
func ExampleNew_withLogger() { func ExampleNew_textEncoding() {
stdlogger := std.New(os.Stdout, "same prefix ", std.Lshortfile)
logger := log.With( logger := log.With(
log.New( log.New(log.WithStdout()),
log.WithLogger( log.WithLevel("level", level.Debug),
stdlogger,
func(msg string, fields log.Fields) string {
return fmt.Sprint("msg=\"", msg, "\" ", fields)
},
),
log.WithCalldepth(9),
),
log.WithLevel(log.LevelDebug),
log.GoVersion("go-version"), log.GoVersion("go-version"),
) )
logger.Err(ctx, "same error message") logger.Err(ctx, "same error message")
logger.InfoKV(ctx, "same info message", "api-version", 0.1) logger.InfoKVs(ctx, "same info message", "api-version", 0.1)
// Output: // Output:
// same prefix logger_example_test.go:82: msg="same error message" level=error go-version=go1.14.2 // msg="same error message" level=error go-version=go1.15.2
// same prefix logger_example_test.go:83: msg="same info message" api-version=0.1 level=info go-version=go1.14.2 // msg="same info message" api-version=0.1 level=info go-version=go1.15.2
} }
type ctxKey string type ctxKey string
@ -93,8 +89,8 @@ func (c ctxKey) String() string {
return string(c) return string(c)
} }
func levelInfo(ctx context.Context, level log.Level, msg string, fields log.Fields, handler log.Logger) { func levelInfo(ctx context.Context, entry *entry.Entry, handler log.Logger) (int, error) {
handler(ctx, level, msg, append(fields, log.Field{Key: "level", Value: level})) return handler(ctx, entry.Add(field.String("level", entry.Level().String())))
} }
func ExampleWith() { func ExampleWith() {
@ -106,7 +102,7 @@ func ExampleWith() {
levelInfo, log.WithContextValue(requestID), log.KeyValue("api", "0.1.0"), log.GoVersion("go"), levelInfo, log.WithContextValue(requestID), log.KeyValue("api", "0.1.0"), log.GoVersion("go"),
) )
logger.Info(vctx, "same message") logger.Info(vctx, "same message")
// Output: msg="same message" level=info requestID=6a5fa048-7181-11ea-bc55-0242ac130003 api=0.1.0 go=go1.14.2 // Output: msg="same message" level=info requestID=6a5fa048-7181-11ea-bc55-0242ac130003 api=0.1.0 go=go1.15.2
} }
func ExampleLogger_Print() { func ExampleLogger_Print() {
@ -115,12 +111,31 @@ func ExampleLogger_Print() {
levelInfo, log.KeyValue("client", "http"), log.KeyValue("api", "0.1.0"), log.GoVersion("go"), levelInfo, log.KeyValue("client", "http"), log.KeyValue("api", "0.1.0"), log.GoVersion("go"),
) )
logger.Print("same message") logger.Print("same message")
// Output: msg="same message" level=info client=http api=0.1.0 go=go1.14.2 // Output: msg="same message" level=info client=http api=0.1.0 go=go1.15.2
} }
func ExamplePrint() { func ExamplePrint() {
std.SetOutput(os.Stdout) setStdout()
std.SetFlags(0)
log.Print("same message") log.Print("same message")
// Output: msg="same message" level=info // Output: msg="same message" level=info
} }
func ExampleWithClosure() {
cnt := int32(0)
closure := func() string {
d := fmt.Sprintf("additional error data: %d", cnt)
atomic.AddInt32(&cnt, 1)
return d
}
log := log.With(log.New(log.WithStdout()), log.WithLevel("level", level.Info), log.WithClosure)
log.DebugKVs(ctx, "debug message", "data", closure)
log.ErrKVs(ctx, "error message", "err", closure)
log.WarnKVs(ctx, "warn message", "warn", closure)
// Output:
// msg="error message" err=additional error data: 0 level=error
// msg="warn message" warn=additional error data: 1 level=warning
}

62
logger_example_trace_test.go

@ -0,0 +1,62 @@
package log_test
import (
"context"
"fmt"
"io"
"gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/handler/otel"
apitrace "go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/sdk/export/trace"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func ExampleNew_withTrace() {
logger := log.New(log.WithStdout()).With(otel.Middleware())
sctx, span := startSpan(ctx)
logger.Err(sctx, "log logrus")
logger.ErrKV(sctx, "log logrus kv", field.Int("int", 42))
logger.ErrKVs(sctx, "log logrus kv sugar", "err", io.EOF)
span.End()
// Output:
// msg="log logrus"
// msg="log logrus kv" int=42
// msg="log logrus kv sugar" err=EOF
// event: log logrus, SeverityText = ERROR, SeverityNumber = 17
// event: log logrus kv, SeverityText = ERROR, SeverityNumber = 17, int = 42
// event: log logrus kv sugar, SeverityText = ERROR, SeverityNumber = 17, err = EOF
}
func startSpan(ctx context.Context) (context.Context, apitrace.Span) {
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter{}))
return tp.Tracer("logger").Start(ctx, "operation")
}
type exporter struct{}
func (e exporter) Shutdown(_ context.Context) error {
return nil
}
func (e exporter) ExportSpans(ctx context.Context, spanData []*trace.SpanData) error {
for _, data := range spanData {
for _, events := range data.MessageEvents {
fmt.Print("event: ", events.Name)
for _, attr := range events.Attributes {
fmt.Printf(", %v = %v", attr.Key, attr.Value.AsInterface())
}
fmt.Print("\n")
}
}
return nil
}

28
logger_example_zap_test.go

@ -0,0 +1,28 @@
package log_test
import (
"io"
"os"
slogrus "github.com/sirupsen/logrus"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/handler/logrus"
)
func ExampleNew_logrusHandler() {
lgrs := slogrus.New()
lgrs.SetOutput(os.Stdout)
lgrs.SetFormatter(&slogrus.TextFormatter{
DisableTimestamp: true,
})
log := logrus.New(logrus.WithLogrus(lgrs))
log.Err(ctx, "log logrus")
log.ErrKV(ctx, "log logrus kv", field.Int("int", 42))
log.ErrKVs(ctx, "log logrus kv sugar", "err", io.EOF)
// Output:
// level=error msg="log logrus"
// level=error msg="log logrus kv" int=42
// level=error msg="log logrus kv sugar" err=EOF
}

85
logger_test.go

@ -4,28 +4,107 @@ import (
"bytes" "bytes"
"context" "context"
"os" "os"
"sync/atomic"
"testing" "testing"
"gitoa.ru/go-4devs/log" "gitoa.ru/go-4devs/log"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
) )
//nolint:gochecknoglobals
var requestID ctxKey = "requestID"
func TestFields(t *testing.T) { func TestFields(t *testing.T) {
type rObj struct { type rObj struct {
id string id string
} }
var cnt int32
ctx := context.Background() ctx := context.Background()
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
log := log.New(log.WithWriter(buf)) log := log.New(log.WithWriter(buf)).
success := "msg=\"message\" err=file already exists version=0.1.0 obj={id:uid}\n" With(log.WithLevel("level", level.Info), log.WithClosure)
success := "msg=\"message\" err=file already exists version=0.1.0 obj={id:uid} closure=some closure data level=info\n"
log.InfoKV(ctx, "message", log.InfoKVs(ctx, "message",
"err", os.ErrExist, "err", os.ErrExist,
"version", "0.1.0", "version", "0.1.0",
"obj", rObj{id: "uid"}, "obj", rObj{id: "uid"},
"closure", func() string {
atomic.AddInt32(&cnt, 1)
return "some closure data"
},
) )
log.DebugKVs(ctx, "debug message",
"closure", func() string {
atomic.AddInt32(&cnt, 1)
return "some debug data"
},
)
if success != buf.String() {
t.Errorf("invalid value\n got:%s\n exp:%s", buf, success)
}
if cnt != 1 {
t.Errorf("invalid cnt value\n got:%d\n exp:1", cnt)
}
}
func TestWriter(t *testing.T) {
ctx := context.Background()
success := "msg=\"info message\" err=file already exists requestID=6a5fa048-7181-11ea-bc55-0242ac1311113 level=info\n"
buf := &bytes.Buffer{}
logger := log.New(log.WithWriter(buf)).With(log.WithContextValue(requestID), log.WithLevel("level", level.Info))
_, _ = logger.Writer(
context.WithValue(ctx, requestID, "6a5fa048-7181-11ea-bc55-0242ac1311113"),
level.Info,
field.Error("err", os.ErrExist),
).Write([]byte("info message"))
if success != buf.String() { if success != buf.String() {
t.Errorf("invalid value\n got:%s\n exp:%s", buf, success) t.Errorf("invalid value\n got:%s\n exp:%s", buf, success)
} }
buf.Reset()
_, _ = logger.Writer(ctx, level.Debug).Write([]byte("debug message"))
if buf.String() != "" {
t.Errorf("invalid value\n got:%s\n exp:%s", buf, success)
}
}
func TestLogger(t *testing.T) {
ctx := context.Background()
buf := &bytes.Buffer{}
logger := log.New(log.WithWriter(buf)).With(log.WithContextValue(requestID), log.WithLevel("level", level.Info))
_, err := logger(ctx, nil)
if err != nil {
t.Fatalf("expected <nil> err, got: %v", err)
}
if buf.String() != "" {
t.Errorf("invalid value\n got:%+v\n exp:\"\"", buf)
}
_, err = logger(ctx, entry.New().SetLevel(level.Error))
if err != nil {
t.Fatalf("expected <nil> err, got: %v", err)
}
success := "msg=\"\" requestID=<nil> level=error\n"
if buf.String() != success {
t.Errorf("invalid value\n got:%+v\n exp:%+v", buf, success)
}
} }

122
middleware.go

@ -3,13 +3,19 @@ package log
import ( import (
"context" "context"
"fmt" "fmt"
"path/filepath" "os"
"runtime" "runtime"
"time" "time"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
) )
var _ Middleware = WithClosure
// Middleware handle. // Middleware handle.
type Middleware func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) type Middleware func(ctx context.Context, e *entry.Entry, handler Logger) (int, error)
// With add middleware to logger. // With add middleware to logger.
func With(logger Logger, mw ...Middleware) Logger { func With(logger Logger, mw ...Middleware) Logger {
@ -17,95 +23,129 @@ func With(logger Logger, mw ...Middleware) Logger {
case 0: case 0:
return logger return logger
case 1: case 1:
return func(ctx context.Context, level Level, msg string, fields Fields) { return func(ctx context.Context, entry *entry.Entry) (int, error) {
mw[0](ctx, level, msg, fields, logger) return mw[0](ctx, entry, logger)
} }
} }
lastI := len(mw) - 1 lastI := len(mw) - 1
return func(ctx context.Context, level Level, msg string, fields Fields) { return func(ctx context.Context, e *entry.Entry) (int, error) {
var ( var (
chainHandler func(ctx context.Context, level Level, msg string, fields Fields) chainHandler func(context.Context, *entry.Entry) (int, error)
curI int curI int
) )
chainHandler = func(currentCtx context.Context, currentLevel Level, currentMsg string, currentFields Fields) { chainHandler = func(currentCtx context.Context, currentEntry *entry.Entry) (int, error) {
if curI == lastI { if curI == lastI {
logger(currentCtx, currentLevel, currentMsg, currentFields) return logger(currentCtx, currentEntry)
return
} }
curI++ curI++
mw[curI](currentCtx, currentLevel, currentMsg, currentFields, chainHandler) n, err := mw[curI](currentCtx, currentEntry, chainHandler)
curI-- curI--
return n, err
} }
mw[0](ctx, level, msg, fields, chainHandler) return mw[0](ctx, e, chainHandler)
} }
} }
// WithLevel sets log level. // WithLevel sets log level.
func WithLevel(lvl Level) Middleware { func WithLevel(key string, lvl level.Level) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
if level <= lvl { if e.Level().Enabled(lvl) {
handler(ctx, level, msg, append(fields, Field{Key: "level", Value: level})) return handler(ctx, e.AddString(key, e.Level().String()))
}
return 0, nil
}
}
func WithClosure(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
for i, field := range e.Fields() {
if field.Type().IsAny() {
if f, ok := field.AsInterface().(func() string); ok {
e.Fields().Set(i, field.Key().String(f()))
}
} }
} }
return handler(ctx, e)
} }
// KeyValue add field by const key value. // KeyValue add field by const key value.
func KeyValue(key string, value interface{}) Middleware { func KeyValue(key string, value interface{}) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
handler(ctx, level, msg, append(fields, Field{Key: key, Value: value})) return handler(ctx, e.AddAny(key, value))
} }
} }
// GoVersion add field by go version. // GoVersion add field by go version.
func GoVersion(key string) Middleware { func GoVersion(key string) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
handler(ctx, level, msg, append(fields, Field{Key: key, Value: runtime.Version()})) return handler(ctx, e.AddString(key, runtime.Version()))
} }
} }
// WithContext add field by context key. // WithContext add field by context key.
func WithContextValue(keys ...fmt.Stringer) Middleware { func WithContextValue(keys ...fmt.Stringer) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
ctxFields := make(Fields, len(keys)) for _, key := range keys {
for i, key := range keys { e = e.AddAny(key.String(), ctx.Value(key))
ctxFields[i] = Field{Key: key.String(), Value: ctx.Value(key)}
} }
handler(ctx, level, msg, append(fields, ctxFields...)) return handler(ctx, e)
} }
} }
// WithCaller adds called file. // WithCaller adds called file.
func WithCaller(calldepth int, short bool) Middleware { func WithCaller(key string, depth int, full bool) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { const offset = 2
_, file, line, ok := runtime.Caller(calldepth)
if !ok { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
file, line = "???", 0 return handler(ctx, e.AddString(key, entry.Caller(depth*offset, full)))
}
} }
if short && ok { // WithTime adds time.
file = filepath.Base(file) func WithTime(key, format string) Middleware {
return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
return handler(ctx, e.Add(field.Time(key, time.Now())))
}
} }
handler(ctx, level, msg, append(fields, NewField("caller", fmt.Sprint(file, ":", line)))) // WithMetrics adds handle metrics.
func WithMetrics(metrics func(level level.Level)) Middleware {
return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
go metrics(e.Level())
return handler(ctx, e)
} }
} }
// WithTime adds time. // WithExit exit by level.
func WithTime(format string) Middleware { func WithExit(level level.Level) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
handler(ctx, level, msg, append(fields, NewField("time", time.Now().Format(format)))) n, err := handler(ctx, e)
if e.Level().Is(level) {
os.Exit(1)
}
return n, err
} }
} }
// WithMetrics adds handle metrics. // WithPanic panic by level.
func WithMetrics(metrics func(level Level)) Middleware { func WithPanic(level level.Level) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
go metrics(level) n, err := handler(ctx, e)
handler(ctx, level, msg, fields)
if e.Level().Is(level) {
panic(e.String())
}
return n, err
} }
} }

121
std.go

@ -1,121 +0,0 @@
package log
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"os"
)
const (
calldepth = 3
)
// New creates standart logger.
func New(opts ...Option) Logger {
logger := logger{
format: stringFormat,
output: log.Output,
calldepth: calldepth,
}
for _, opt := range opts {
opt(&logger)
}
return func(ctx context.Context, level Level, msg string, fields Fields) {
_ = logger.output(logger.calldepth, logger.format(msg, fields))
switch level {
case LevelEmergency:
panic(msg)
case LevelAlert:
os.Exit(1)
default:
}
}
}
// Option configure log.
type Option func(*logger)
// Format sets formats output message.
type Format func(msg string, fields Fields) string
type logger struct {
output func(calldepth int, s string) error
format Format
calldepth int
}
// WithWriter sets writer logger.
func WithWriter(writer io.Writer) Option {
return func(l *logger) {
l.output = log.New(writer, "", 0).Output
}
}
// WithStdout sets logged to os.Stdout.
func WithStdout() Option {
return func(l *logger) {
l.output = log.New(os.Stdout, "", 0).Output
}
}
// WithFormat sets format log.
func WithFormat(format Format) Option {
return func(l *logger) {
l.format = format
}
}
// WithStringFormat sets format as simple string.
func WithStringFormat() Option {
return func(l *logger) {
l.format = stringFormat
}
}
// WithJSONFormat sets json output format.
func WithJSONFormat() Option {
return func(l *logger) {
l.format = jsonFormat
}
}
// WithCalldepth sets depth filename.
func WithCalldepth(calldepth int) Option {
return func(l *logger) {
l.calldepth = calldepth
}
}
// WithLogger sets logger anf format.
func WithLogger(std *log.Logger, format Format) Option {
return func(l *logger) {
l.output = std.Output
l.format = format
}
}
func stringFormat(msg string, fields Fields) string {
return fmt.Sprint("msg=\"", msg, "\" ", fields)
}
func jsonFormat(msg string, fields Fields) string {
data := make(map[string]interface{}, len(fields)+1)
data["msg"] = msg
for _, field := range fields {
data[field.Key] = field.Value
}
res, err := json.Marshal(data)
if err != nil {
return stringFormat(msg, append(fields, FieldError(err)))
}
return string(res)
}

118
writter.go

@ -0,0 +1,118 @@
package log
import (
"bytes"
"context"
"encoding/json"
"io"
"os"
"strings"
"sync"
"gitoa.ru/go-4devs/log/entry"
)
// New creates standart logger.
func New(opts ...Option) Logger {
l := log{e: stringFormat(), w: os.Stderr}
for _, opt := range opts {
opt(&l)
}
return func(_ context.Context, entry *entry.Entry) (int, error) {
b, err := l.e(entry)
if err != nil {
return 0, err
}
return l.w.Write(b)
}
}
// Option configure log.
type Option func(*log)
// Encode sets formats and encode output message.
type Encode func(*entry.Entry) ([]byte, error)
type log struct {
w io.Writer
e Encode
}
// WithWriter sets writer logger.
func WithWriter(writer io.Writer) Option {
return func(l *log) {
l.w = writer
}
}
// WithStdout sets logged to os.Stdout.
func WithStdout() Option {
return func(l *log) {
l.w = os.Stdout
}
}
// WithEncode sets format log.
func WithEncode(e Encode) Option {
return func(l *log) {
l.e = e
}
}
// WithStringFormat sets format as simple string.
func WithStringFormat() Option {
return func(l *log) {
l.e = stringFormat()
}
}
// WithJSONFormat sets json output format.
func WithJSONFormat() Option {
return func(l *log) {
l.e = jsonFormat
}
}
func stringFormat() func(entry *entry.Entry) ([]byte, error) {
pool := sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
return func(entry *entry.Entry) ([]byte, error) {
b := pool.Get().(*bytes.Buffer)
b.Reset()
defer func() {
pool.Put(b)
}()
b.WriteString("msg=\"")
b.WriteString(strings.TrimSpace(entry.Message()))
b.WriteString("\"")
for _, field := range entry.Fields() {
b.WriteString(" ")
b.WriteString(string(field.Key()))
b.WriteString("=")
b.WriteString(field.Value().String())
}
b.WriteString("\n")
return b.Bytes(), nil
}
}
func jsonFormat(entry *entry.Entry) ([]byte, error) {
res, err := json.Marshal(entry.AddString("msg", entry.Message()).Fields().AsMap())
if err != nil {
return nil, err
}
return append(res, []byte("\n")...), nil
}
Loading…
Cancel
Save