10 Commits

Author SHA1 Message Date
81eaf8c8b2 update opentelemetry version (#6)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Co-authored-by: andrey1s <andrey@4devs.pro>
Reviewed-on: #6
Co-authored-by: andrey <andrey@4devs.io>
Co-committed-by: andrey <andrey@4devs.io>
2022-09-06 22:25:04 +03:00
7570ade82a upddate caller write method (#5)
All checks were successful
continuous-integration/drone/push Build is passing
Co-authored-by: andrey1s <andrey@4devs.pro>
Reviewed-on: #5
Co-authored-by: andrey <andrey@4devs.io>
Co-committed-by: andrey <andrey@4devs.io>
2022-03-14 09:58:23 +03:00
012e2ce197 Merge pull request 'update golang' (#4) from fields into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #4
2022-03-13 13:17:01 +03:00
andrey1s
51b844d50b update golang
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-03-13 12:01:36 +03:00
f70653066a Merge pull request 'add test append fields' (#2) from fields into master
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #2
2022-03-13 11:10:16 +03:00
andrey1s
21c14e24be update go version
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-12 20:00:01 +03:00
andrey1s
d7310ed1df add test append fields 2022-03-12 10:01:11 +03:00
b80e6d1a29 Delete '.DS_Store' 2022-02-04 22:48:48 +03:00
b56ca08811 update zap/logrus hanler (#1)
Co-authored-by: andrey1s <andrey_simfi@list.ru>
Reviewed-on: #1
Co-authored-by: andrey <andrey@4devs.io>
Co-committed-by: andrey <andrey@4devs.io>
2022-01-02 14:32:19 +03:00
andrey1s
deb67b0008 restore v0.3.0 2021-09-19 17:25:47 +03:00
40 changed files with 3639 additions and 372 deletions

View File

@@ -3,7 +3,7 @@ name: default
steps: steps:
- name: test - name: test
image: golang:1.14.2 image: golang:1.18.1
volumes: volumes:
- name: deps - name: deps
path: /go/src/mod path: /go/src/mod
@@ -11,40 +11,10 @@ steps:
- go test - go test
- name: golangci-lint - name: golangci-lint
image: golangci/golangci-lint:v1.29 image: golangci/golangci-lint:v1.49
commands: commands:
- golangci-lint run - golangci-lint run
- name: logrus golangci-lint
image: golangci/golangci-lint:v1.29
commands:
- cd logrus
- golangci-lint run
- name: logrus test
image: golang:1.14.2
volumes:
- name: deps
path: /go/src/mod
commands:
- cd logrus
- go test
- name: zap golangci-lint
image: golangci/golangci-lint:v1.29
commands:
- cd zap
- golangci-lint run
- name: zap test
image: golang:1.14.2
volumes:
- name: deps
path: /go/src/mod
commands:
- cd zap
- go test
volumes: volumes:
- name: deps - name: deps
temp: {} temp: {}

View File

@@ -9,21 +9,45 @@ linters-settings:
min-occurrences: 2 min-occurrences: 2
gocyclo: gocyclo:
min-complexity: 15 min-complexity: 15
golint:
min-confidence: 0
govet: govet:
check-shadowing: true check-shadowing: true
lll: lll:
line-length: 140 line-length: 140
maligned: fieldalignment:
suggest-new: true suggest-new: true
misspell: misspell:
locale: US locale: US
exhaustive: exhaustive:
default-signifies-exhaustive: true default-signifies-exhaustive: true
varnamelen:
min-name-length: 2
ignore-names:
- err
- "n"
- i
tagliatelle:
case:
use-field-name: true
rules:
json: snake
yaml: camel
xml: camel
bson: camel
avro: snake
linters: linters:
enable-all: true enable-all: true
disable:
- exhaustivestruct
- maligned
- deadcode
- interfacer
- golint
- varcheck
- nosnakecase
- scopelint
- ifshort
- structcheck
issues: issues:
# Excluding configuration per-path, per-linter, per-text and per-source # Excluding configuration per-path, per-linter, per-text and per-source
@@ -31,3 +55,5 @@ issues:
- path: _test\.go - path: _test\.go
linters: linters:
- gomnd - gomnd
- ireturn
- exhaustruct

View File

@@ -1,4 +1,4 @@
MIT License Copyright (c) 2020 go-4devs MIT License Copyright (c) 2020-2022 4devs
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

177
bench_test.go Normal file
View File

@@ -0,0 +1,177 @@
package log_test
import (
"context"
"errors"
"fmt"
"io"
"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(io.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 Normal file
View File

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

163
entry/entry.go Normal file
View File

@@ -0,0 +1,163 @@
package entry
import (
"fmt"
"strings"
"gitoa.ru/go-4devs/log/field"
"gitoa.ru/go-4devs/log/level"
)
const (
defaultCap = 5
)
type Option func(*Entry)
func WithCapacity(c int) Option {
return func(e *Entry) {
e.fields = make(field.Fields, 0, c+1)
}
}
func WithFields(fields ...field.Field) Option {
return func(e *Entry) {
e.fields = fields
}
}
func WithMessage(msg string) Option {
return func(e *Entry) {
e.format = msg
}
}
func WithMessagef(format string, args ...interface{}) Option {
return func(e *Entry) {
e.format = format
e.args = args
}
}
func WithLevel(lvl level.Level) Option {
return func(e *Entry) {
e.level = lvl
}
}
func New(opts ...Option) *Entry {
entry := &Entry{
fields: make(field.Fields, 0, defaultCap+1),
level: level.Debug,
format: "",
args: make([]interface{}, 0, defaultCap+1),
}
for _, opt := range opts {
opt(entry)
}
return entry
}
// Entry slice field.
type Entry struct {
format string
args []interface{}
level level.Level
fields field.Fields
}
func (e *Entry) Reset() {
e.fields = e.fields[:0]
e.args = e.args[:0]
e.format = ""
}
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.Message()
for i, field := range e.fields {
str[i+1] = field.String()
}
return strings.Join(str, " ")
}
func (e *Entry) Message() string {
switch {
case len(e.args) > 0 && e.format != "":
return fmt.Sprintf(e.format, e.args...)
case len(e.args) > 0:
return fmt.Sprint(e.args...)
default:
return e.format
}
}
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.format = msg
return e
}
func (e *Entry) SetMessagef(format string, args ...interface{}) *Entry {
if e == nil {
return New().SetMessagef(format, args...)
}
e.format = format
e.args = append(e.args[:0], args...)
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))
}

22
entry/pool.go Normal file
View File

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

View File

@@ -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)
} }

32
field/encoder.go Normal file
View File

@@ -0,0 +1,32 @@
package field
import "time"
//nolint:interfacebloat
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 Normal file
View File

@@ -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,cyclop
func (f Field) AddTo(enc Encoder) {
key := string(f.key)
switch {
case f.value.IsArray():
enc.AddArray(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 Normal file
View File

@@ -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 {
fields := make(MapField, len(f))
for _, field := range f {
fields[field.Key()] = field.Value()
}
return fields
}

18
field/fields_test.go Normal file
View File

@@ -0,0 +1,18 @@
package field_test
import (
"testing"
"gitoa.ru/go-4devs/log/field"
)
func TestFields_Append(t *testing.T) {
t.Parallel()
fields := field.Fields{field.Any("any", "value")}
fields = fields.Append(field.String("string", "value"))
if len(fields) != 2 {
t.Fatalf("require 2 field got %v", len(fields))
}
}

570
field/key.go Normal file
View File

@@ -0,0 +1,570 @@
package field
import (
"time"
)
type Key string
//nolint:funlen,cyclop,gocyclo
func (k Key) Any(value interface{}) Field {
switch val := value.(type) {
case string:
return k.String(val)
case *string:
return k.Stringp(val)
case []string:
return k.Strings(val...)
case bool:
return k.Bool(val)
case *bool:
return k.Boolp(val)
case []bool:
return k.Bools(val...)
case int8:
return k.Int8(val)
case []int8:
return k.Int8s(val...)
case *int8:
return k.Int8p(val)
case int16:
return k.Int16(val)
case []int16:
return k.Int16s(val...)
case *int16:
return k.Int16p(val)
case int32:
return k.Int32(val)
case []int32:
return k.Int32s(val...)
case *int32:
return k.Int32p(val)
case int64:
return k.Int64(val)
case []int64:
return k.Int64s(val...)
case *int64:
return k.Int64p(val)
case uint:
return k.Uint(val)
case []uint:
return k.Uints(val...)
case *uint:
return k.Uintp(val)
case uint8:
return k.Uint8(val)
case *uint8:
return k.Uint8p(val)
case uint16:
return k.Uint16(val)
case []uint16:
return k.Uint16s(val...)
case *uint16:
return k.Uint16p(val)
case uint32:
return k.Uint32(val)
case []uint32:
return k.Uint32s(val...)
case *uint32:
return k.Uint32p(val)
case uint64:
return k.Uint64(val)
case []uint64:
return k.Uint64s(val...)
case *uint64:
return k.Uint64p(val)
case float32:
return k.Float32(val)
case []float32:
return k.Float32s(val...)
case *float32:
return k.Float32p(val)
case float64:
return k.Float64(val)
case []float64:
return k.Float64s(val...)
case *float64:
return k.Float64p(val)
case complex64:
return k.Complex64(val)
case []complex64:
return k.Complex64s(val...)
case *complex64:
return k.Complex64p(val)
case uintptr:
return k.Uintptr(val)
case []uintptr:
return k.Uintptrs(val...)
case *uintptr:
return k.Uintptrp(val)
case []byte:
return k.Bytes(val)
case time.Duration:
return k.Dureation(val)
case []time.Duration:
return k.Dureations(val)
case *time.Duration:
return k.Dureationp(val)
case time.Time:
return k.Time(val)
case []time.Time:
return k.Times(val...)
case *time.Time:
return k.Timep(val)
case error:
return k.Error(val)
case []error:
return k.Errors(val...)
}
return Field{
key: k,
value: Value{
value: value,
vtype: TypeAny,
numeric: 0,
stringly: "",
},
}
}
func (k Key) String(value string) Field {
return Field{
key: k,
value: stringValue(value),
}
}
func (k Key) Strings(value ...string) Field {
return Field{
key: k,
value: stringsValue(value),
}
}
func (k Key) Stringp(value *string) Field {
return Field{
key: k,
value: stringpValue(value),
}
}
func (k Key) Bool(value bool) Field {
return Field{
key: k,
value: boolValue(value),
}
}
func (k Key) Bools(value ...bool) Field {
return Field{
key: k,
value: boolsValue(value),
}
}
func (k Key) Boolp(value *bool) Field {
return Field{
key: k,
value: boolpValue(value),
}
}
func (k Key) Int(value int) Field {
return Field{
key: k,
value: intValue(value),
}
}
func (k Key) Ints(value ...int) Field {
return Field{
key: k,
value: intsValue(value),
}
}
func (k Key) Intp(value *int) Field {
return Field{
key: k,
value: intpValue(value),
}
}
func (k Key) Int8(value int8) Field {
return Field{
key: k,
value: int8Value(value),
}
}
func (k Key) Int8s(value ...int8) Field {
return Field{
key: k,
value: int8sValue(value),
}
}
func (k Key) Int8p(value *int8) Field {
return Field{
key: k,
value: int8pValue(value),
}
}
func (k Key) Int16(value int16) Field {
return Field{
key: k,
value: int16Value(value),
}
}
func (k Key) Int16s(value ...int16) Field {
return Field{
key: k,
value: int16sValue(value),
}
}
func (k Key) Int16p(value *int16) Field {
return Field{
key: k,
value: int16pValue(value),
}
}
func (k Key) Int32(value int32) Field {
return Field{
key: k,
value: int32Value(value),
}
}
func (k Key) Int32s(value ...int32) Field {
return Field{
key: k,
value: int32sValue(value),
}
}
func (k Key) Int32p(value *int32) Field {
return Field{
key: k,
value: int32pValue(value),
}
}
func (k Key) Int64(value int64) Field {
return Field{
key: k,
value: int64Value(value),
}
}
func (k Key) Int64s(value ...int64) Field {
return Field{
key: k,
value: int64sValue(value),
}
}
func (k Key) Int64p(value *int64) Field {
return Field{
key: k,
value: int64pValue(value),
}
}
func (k Key) Uint(value uint) Field {
return Field{
key: k,
value: uintValue(value),
}
}
func (k Key) Uints(value ...uint) Field {
return Field{
key: k,
value: uintsValue(value),
}
}
func (k Key) Uintp(value *uint) Field {
return Field{
key: k,
value: uintpValue(value),
}
}
func (k Key) Uint8(value uint8) Field {
return Field{
key: k,
value: uint8Value(value),
}
}
func (k Key) Uint8s(value ...uint8) Field {
return Field{
key: k,
value: uint8sValue(value),
}
}
func (k Key) Uint8p(value *uint8) Field {
return Field{
key: k,
value: uint8pValue(value),
}
}
func (k Key) Uint16(value uint16) Field {
return Field{
key: k,
value: uint16Value(value),
}
}
func (k Key) Uint16s(value ...uint16) Field {
return Field{
key: k,
value: uint16sValue(value),
}
}
func (k Key) Uint16p(value *uint16) Field {
return Field{
key: k,
value: uint16pValue(value),
}
}
func (k Key) Uint32(value uint32) Field {
return Field{
key: k,
value: uint32Value(value),
}
}
func (k Key) Uint32s(value ...uint32) Field {
return Field{
key: k,
value: uint32sValue(value),
}
}
func (k Key) Uint32p(value *uint32) Field {
return Field{
key: k,
value: uint32pValue(value),
}
}
func (k Key) Uint64(value uint64) Field {
return Field{
key: k,
value: uint64Value(value),
}
}
func (k Key) Uint64s(value ...uint64) Field {
return Field{
key: k,
value: uint64sValue(value),
}
}
func (k Key) Uint64p(value *uint64) Field {
return Field{
key: k,
value: uint64pValue(value),
}
}
func (k Key) Float32(value float32) Field {
return Field{
key: k,
value: float32Value(value),
}
}
func (k Key) Float32s(value ...float32) Field {
return Field{
key: k,
value: float32sValue(value),
}
}
func (k Key) Float32p(value *float32) Field {
return Field{
key: k,
value: float32pValue(value),
}
}
func (k Key) Float64(value float64) Field {
return Field{
key: k,
value: float64Value(value),
}
}
func (k Key) Float64s(value ...float64) Field {
return Field{
key: k,
value: float64sValue(value),
}
}
func (k Key) Float64p(value *float64) Field {
return Field{
key: k,
value: float64pValue(value),
}
}
func (k Key) Complex64(value complex64) Field {
return Field{
key: k,
value: complex64Value(value),
}
}
func (k Key) Complex64s(value ...complex64) Field {
return Field{
key: k,
value: complex64sValue(value),
}
}
func (k Key) Complex64p(value *complex64) Field {
return Field{
key: k,
value: complex64pValue(value),
}
}
func (k Key) Complex128(value complex128) Field {
return Field{
key: k,
value: complex128Value(value),
}
}
func (k Key) Complex128s(value []complex128) Field {
return Field{
key: k,
value: complex128sValue(value),
}
}
func (k Key) Complex128p(value *complex128) Field {
return Field{
key: k,
value: complex128pValue(value),
}
}
func (k Key) Uintptr(value uintptr) Field {
return Field{
key: k,
value: uintptrValue(value),
}
}
func (k Key) Uintptrs(value ...uintptr) Field {
return Field{
key: k,
value: uintptrsValue(value),
}
}
func (k Key) Uintptrp(value *uintptr) Field {
return Field{
key: k,
value: uintptrpValue(value),
}
}
func (k Key) Bytes(value []byte) Field {
return Field{
key: k,
value: bytesValue(value),
}
}
func (k Key) Dureation(value time.Duration) Field {
return Field{
key: k,
value: durationValue(value),
}
}
func (k Key) Dureations(value []time.Duration) Field {
return Field{
key: k,
value: durationsValue(value),
}
}
func (k Key) Dureationp(value *time.Duration) Field {
return Field{
key: k,
value: durationpValue(value),
}
}
func (k Key) Time(value time.Time) Field {
return Field{
key: k,
value: timeValue(value),
}
}
func (k Key) Times(value ...time.Time) Field {
return Field{
key: k,
value: timesValue(value),
}
}
func (k Key) Timep(value *time.Time) Field {
return Field{
key: k,
value: timepValue(value),
}
}
func (k Key) FormatTime(format string, value time.Time) Field {
return Field{
key: k,
value: formatTimeValue(format, value),
}
}
func (k Key) FormatTimes(format string, value ...time.Time) Field {
return Field{
key: k,
value: formatTimesValue(format, value),
}
}
func (k Key) FormatTimep(format string, value *time.Time) Field {
return Field{
key: k,
value: formatTimepValue(format, value),
}
}
func (k Key) Error(value error) Field {
return Field{
key: k,
value: errorValue(value),
}
}
func (k Key) Errors(value ...error) Field {
return Field{
key: k,
value: errorsValue(value),
}
}

126
field/type.go Normal file
View File

@@ -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
}

862
field/value.go Normal file
View File

@@ -0,0 +1,862 @@
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) {
b, err := json.Marshal(v.AsInterface())
if err != nil {
return nil, fmt.Errorf("marshal err: %w", err)
}
return b, nil
}
//nolint:gocyclo,gomnd,cyclop
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,cyclop
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 {
cmplex, _ := v.value.(complex64)
return cmplex
}
func (v Value) asComplex128() complex128 {
cmplex, _ := v.value.(complex128)
return cmplex
}
func (v Value) asUintptr() uintptr {
val, _ := v.value.(uintptr)
return val
}
func (v Value) asBinary() []byte {
bytes, _ := v.value.([]byte)
return bytes
}
func (v Value) asDuration() time.Duration {
duration, _ := v.value.(time.Duration)
return duration
}
func (v Value) asTime() time.Time {
value, _ := v.value.(time.Time)
return value
}
func (v Value) asError() error {
err, _ := v.value.(error)
return err
}
func nilValue(t Type) Value {
return Value{
vtype: t | TypeNil,
value: nil,
numeric: 0,
stringly: "",
}
}
func stringValue(v string) Value {
return Value{
stringly: v,
vtype: TypeString,
numeric: 0,
value: nil,
}
}
func stringsValue(v []string) Value {
return Value{
value: v,
vtype: TypeString | TypeArray,
numeric: 0,
stringly: "",
}
}
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,
value: nil,
stringly: "",
}
}
return Value{
vtype: TypeBool,
value: nil,
numeric: 0,
stringly: "",
}
}
func boolsValue(b []bool) Value {
return Value{
value: b,
vtype: TypeBool | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func intsValue(i []int) Value {
return Value{
value: i,
vtype: TypeInt | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func int8sValue(i []int8) Value {
return Value{
value: i,
vtype: TypeInt8 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: 0,
stringly: "",
}
}
func int16sValue(i []int16) Value {
return Value{
value: i,
vtype: TypeInt16 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func int32sValue(i []int32) Value {
return Value{
value: i,
vtype: TypeInt32 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func int64sValue(i []int64) Value {
return Value{
value: i,
vtype: TypeInt64 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func uintsValue(i []uint) Value {
return Value{
value: i,
vtype: TypeUint | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func uint8sValue(i []uint8) Value {
return Value{
value: i,
vtype: TypeUint8 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func uint16sValue(i []uint16) Value {
return Value{
value: i,
vtype: TypeUint16 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func uint32sValue(i []uint32) Value {
return Value{
value: i,
vtype: TypeUint32 | TypeArray,
numeric: 0,
stringly: "",
}
}
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,
value: nil,
stringly: "",
}
}
func uint64sValue(i []uint64) Value {
return Value{
value: i,
vtype: TypeUint64 | TypeArray,
numeric: 0,
stringly: "",
}
}
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)),
value: nil,
stringly: "",
}
}
func float32sValue(i []float32) Value {
return Value{
value: i,
vtype: TypeFloat32 | TypeArray,
numeric: 0,
stringly: "",
}
}
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),
value: nil,
stringly: "",
}
}
func float64sValue(i []float64) Value {
return Value{
value: i,
vtype: TypeFloat64 | TypeArray,
numeric: 0,
stringly: "",
}
}
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,
numeric: 0,
stringly: "",
}
}
func complex64sValue(in []complex64) Value {
return Value{
vtype: TypeComplex64 | TypeArray,
value: in,
numeric: 0,
stringly: "",
}
}
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,
numeric: 0,
stringly: "",
}
}
func complex128sValue(in []complex128) Value {
return Value{
vtype: TypeComplex128 | TypeArray,
value: in,
numeric: 0,
stringly: "",
}
}
func complex128pValue(in *complex128) Value {
if in != nil {
return complex128Value(*in)
}
return nilValue(TypeComplex128)
}
func uintptrValue(in uintptr) Value {
return Value{
vtype: TypeUintptr,
numeric: 0,
stringly: "",
value: in,
}
}
func uintptrsValue(in []uintptr) Value {
return Value{
vtype: TypeUintptr | TypeArray,
value: in,
numeric: 0,
stringly: "",
}
}
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,
numeric: 0,
stringly: "",
}
}
func durationValue(in time.Duration) Value {
return Value{
vtype: TypeDuration,
value: in,
numeric: 0,
stringly: "",
}
}
func durationsValue(in []time.Duration) Value {
return Value{
vtype: TypeDuration | TypeArray,
value: in,
numeric: 0,
stringly: "",
}
}
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,
numeric: 0,
}
}
func formatTimesValue(format string, in []time.Time) Value {
return Value{
vtype: TypeTime | TypeArray,
value: in,
stringly: format,
numeric: 0,
}
}
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,
numeric: 0,
stringly: "",
}
}
return nilValue(TypeError)
}
func errorsValue(in []error) Value {
return Value{
vtype: TypeError | TypeArray,
value: in,
numeric: 0,
stringly: "",
}
}

View File

@@ -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)
}

18
go.mod
View File

@@ -1,3 +1,19 @@
module gitoa.ru/go-4devs/log module gitoa.ru/go-4devs/log
go 1.14 go 1.17
require (
github.com/sirupsen/logrus v1.8.1
go.opentelemetry.io/otel v1.9.0
go.opentelemetry.io/otel/sdk v1.9.0
go.opentelemetry.io/otel/trace v1.9.0
go.uber.org/zap v1.21.0
)
require (
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
)

77
go.sum Normal file
View File

@@ -0,0 +1,77 @@
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw=
go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
go.opentelemetry.io/otel/sdk v1.9.0 h1:LNXp1vrr83fNXTHgU8eO89mhzxb/bbWAsHG6fNf3qWo=
go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4=
go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc=
go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

44
handler/logrus/logger.go Normal file
View File

@@ -0,0 +1,44 @@
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"
)
// Standard create new standart logrus handler.
func Standard() log.Logger {
return New(logrus.StandardLogger())
}
// New create new logrus handler.
func New(log *logrus.Logger) log.Logger {
return func(ctx context.Context, data *entry.Entry) (int, error) {
lrgFields := make(logrus.Fields, data.Fields().Len())
for _, field := range data.Fields() {
lrgFields[string(field.Key())] = field.AsInterface()
}
entry := log.WithContext(ctx).WithFields(lrgFields)
switch data.Level() {
case level.Emergency:
entry.Panic(data.Message())
case level.Alert:
entry.Fatal(data.Message())
case level.Critical, level.Error:
entry.Error(data.Message())
case level.Warning:
entry.Warn(data.Message())
case level.Notice, level.Info:
entry.Info(data.Message())
case level.Debug:
entry.Debug(data.Message())
}
return 0, nil
}
}

View File

@@ -0,0 +1,38 @@
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) {
t.Parallel()
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(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 Normal file
View File

@@ -0,0 +1,56 @@
package otel
import (
"context"
"gitoa.ru/go-4devs/log/entry"
"gitoa.ru/go-4devs/log/level"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
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, data *entry.Entry) {
span := trace.SpanFromContext(ctx)
attrs := make([]attribute.KeyValue, 0, data.Fields().Len()+levelFields)
lvl := levels(data.Level())
attrs = append(attrs,
attribute.String(fieldSeverityText, lvl.String()),
attribute.Int(fieldSeverityNumber, int(lvl)),
)
for _, field := range data.Fields() {
attrs = append(attrs, attribute.String(string(field.Key()), field.Value().String()))
}
span.AddEvent(data.Message(), trace.WithAttributes(attrs...))
}

16
handler/otel/level.go Normal file
View File

@@ -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
)

View File

@@ -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 Normal file
View File

@@ -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
}
}

View File

@@ -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)
}
}

63
handler/zap/logger.go Normal file
View File

@@ -0,0 +1,63 @@
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"
)
func Nop() log.Logger {
return New(zap.NewNop())
}
func Example(options ...zap.Option) log.Logger {
return New(zap.NewExample(options...))
}
func Production(options ...zap.Option) log.Logger {
z, err := zap.NewProduction(options...)
if err != nil {
panic(err)
}
return New(z)
}
func Development(options ...zap.Option) log.Logger {
z, err := zap.NewDevelopment(options...)
if err != nil {
panic(err)
}
return New(z)
}
// New create handler by zap logger.
func New(logger *zap.Logger) log.Logger {
return func(ctx context.Context, data *entry.Entry) (int, error) {
zf := make([]zap.Field, data.Fields().Len())
for i, field := range data.Fields() {
zf[i] = zap.Any(string(field.Key()), field.AsInterface())
}
switch data.Level() {
case level.Emergency:
logger.Fatal(data.Message(), zf...)
case level.Alert:
logger.Panic(data.Message(), zf...)
case level.Critical, level.Error:
logger.Error(data.Message(), zf...)
case level.Warning:
logger.Warn(data.Message(), zf...)
case level.Notice, level.Info:
logger.Info(data.Message(), zf...)
case level.Debug:
logger.Debug(data.Message(), zf...)
}
return 0, nil
}
}

View File

@@ -0,0 +1,43 @@
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) {
t.Parallel()
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(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)
}
}

View File

@@ -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
}

79
level/level.go Normal file
View File

@@ -0,0 +1,79 @@
package level
import (
"encoding/json"
"fmt"
"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) {
b, err := json.Marshal(l.String())
if err != nil {
return nil, fmt.Errorf("marshal err: %w", err)
}
return b, nil
}
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 fmt.Errorf("unmarshal err: %w", 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
}
}

View File

@@ -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"

241
logger.go
View File

@@ -2,39 +2,73 @@ 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) Write(in []byte) (int, error) {
l(ctx, level, fmt.Sprint(args...), nil) return l.write(context.Background(), level.Info, string(in))
} }
func (l Logger) logKV(ctx context.Context, level Level, msg string, args ...interface{}) { func (l Logger) write(ctx context.Context, level level.Level, msg string, fields ...field.Field) (int, error) {
l(ctx, level, msg, l.kv(ctx, args...)) data := entry.Get()
defer func() {
entry.Put(data)
}()
return l(ctx, data.SetLevel(level).SetMessage(msg).Add(fields...))
} }
func (l Logger) logf(ctx context.Context, level Level, format string, args ...interface{}) { func (l Logger) writef(ctx context.Context, level level.Level, format string, args ...interface{}) (int, error) {
l(ctx, level, fmt.Sprintf(format, args...), nil) data := entry.Get()
defer func() {
entry.Put(data)
}()
return l(ctx, data.SetLevel(level).SetMessagef(format, args...))
} }
func (l Logger) logln(ctx context.Context, level Level, args ...interface{}) { func (l Logger) kv(ctx context.Context, args ...interface{}) field.Fields {
l(ctx, level, fmt.Sprintln(args...), nil) kvEntry := entry.Get()
}
func (l Logger) kv(ctx context.Context, args ...interface{}) []Field { defer func() {
fields := make([]Field, 0, len(args)) entry.Put(kvEntry)
}()
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) kvEntry = kvEntry.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) writeOutput(l.write(ctx, level.Critical, fmt.Sprint("Ignored key without a value.", args[i]), kvEntry.Fields()...))
break break
} }
@@ -42,14 +76,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}) kvEntry = kvEntry.AddAny(keyStr, val)
continue continue
} }
l(ctx, LevelCritical, fmt.Sprint("Ignored key-value pairs with non-string keys.", key, val), fields) writeOutput(l.write(ctx, level.Critical, fmt.Sprint("Ignored key-value pairs with non-string keys.", key, val), kvEntry.Fields()...))
} }
return fields return kvEntry.Fields()
} }
// With adds middlewares to logger. // With adds middlewares to logger.
@@ -59,165 +94,253 @@ 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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.write(context.Background(), level.Info, fmt.Sprintln(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...) writeOutput(l.write(context.Background(), level.Alert, fmt.Sprintln(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...) writeOutput(l.write(context.Background(), level.Emergency, fmt.Sprintln(args...)))
}
// EmergKVs sugared log by emergency level and key-values.
func (l Logger) EmergKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Emergency, msg, l.kv(ctx, args...)...))
}
// AlertKVs sugared log by alert level and key-values.
func (l Logger) AlertKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Alert, msg, l.kv(ctx, args...)...))
}
// CritKVs sugared log by critcal level and key-values.
func (l Logger) CritKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Critical, msg, l.kv(ctx, args...)...))
}
// ErrKVs sugared log by error level and key-values.
func (l Logger) ErrKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Error, msg, l.kv(ctx, args...)...))
}
// WarnKVs sugared log by warning level and key-values.
func (l Logger) WarnKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Warning, msg, l.kv(ctx, args...)...))
}
// NoticeKVs sugared log by notice level and key-values.
func (l Logger) NoticeKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Notice, msg, l.kv(ctx, args...)...))
}
// InfoKVs sugared log by info level and key-values.
func (l Logger) InfoKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Info, msg, l.kv(ctx, args...)...))
}
// DebugKVs sugared log by debug level and key-values.
func (l Logger) DebugKVs(ctx context.Context, msg string, args ...interface{}) {
writeOutput(l.write(ctx, level.Debug, msg, l.kv(ctx, 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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.write(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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...) writeOutput(l.writef(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,
}
}
//nolint:containedctx
type writer struct {
ctx context.Context
level level.Level
fields []field.Field
Logger
}
func (w writer) WithLevel(level level.Level) writer {
return writer{
level: level,
Logger: w.Logger,
ctx: w.ctx,
fields: w.fields,
}
}
func (w writer) WithContext(ctx context.Context) writer {
return writer{
level: w.level,
Logger: w.Logger,
ctx: ctx,
fields: w.fields,
}
}
func (w writer) WithFields(fields ...field.Field) writer {
return writer{
level: w.level,
Logger: w.Logger,
ctx: w.ctx,
fields: fields,
}
}
func (w writer) Write(in []byte) (int, error) {
return w.write(w.ctx, w.level, string(in), w.fields...)
} }

View File

@@ -0,0 +1,22 @@
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)
_, _ = logger.Write([]byte("same write message"))
// 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
// msg="same write message" level=info caller=logger_example_caller_test.go:16
}

View File

@@ -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(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
}

View File

@@ -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,52 +45,45 @@ 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")
// Output:
logger.Err(ctx, "same error message") logger.Err(ctx, "same error message")
// Output: msg="same error message" level=error // Output: msg="same error message" level=error
} }
func ExampleNew_level_info() {
logger := log.New(log.WithStdout()).With(log.WithLevel("level", level.Error))
logger.Info(ctx, "same message")
// Output:
}
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.18.1","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.18.1
// 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.18.1
} }
type ctxKey string type ctxKey string
@@ -93,8 +92,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 +105,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.18.1
} }
func ExampleLogger_Print() { func ExampleLogger_Print() {
@@ -115,12 +114,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.18.1
} }
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
}

View File

@@ -0,0 +1,61 @@
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"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/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, trace.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 []sdktrace.ReadOnlySpan) error {
for _, data := range spanData {
for _, events := range data.Events() {
fmt.Print("event: ", events.Name)
for _, attr := range events.Attributes {
fmt.Printf(", %v = %v", attr.Key, attr.Value.AsInterface())
}
fmt.Print("\n")
}
}
return nil
}

View File

@@ -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(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"}
}

View File

@@ -4,28 +4,113 @@ 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) {
t.Parallel()
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() { 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)
} }
if cnt != 1 {
t.Errorf("invalid cnt value\n got:%d\n exp:1", cnt)
}
}
func TestWriter(t *testing.T) {
t.Parallel()
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() {
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) {
t.Parallel()
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)
}
} }

View File

@@ -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, data *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, data, 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, data *entry.Entry, handler Logger) (int, error) {
for i, field := range data.Fields() {
if field.Type().IsAny() {
if f, ok := field.AsInterface().(func() string); ok {
data.Fields().Set(i, field.Key().String(f()))
} }
} }
} }
return handler(ctx, data)
}
// 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 {
file, line = "???", 0
}
if short && ok { return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
file = filepath.Base(file) return handler(ctx, e.AddString(key, entry.Caller(depth*offset, full)))
}
handler(ctx, level, msg, append(fields, NewField("caller", fmt.Sprint(file, ":", line))))
} }
} }
// WithTime adds time. // WithTime adds time.
func WithTime(format string) Middleware { func WithTime(key, format 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, NewField("time", time.Now().Format(format)))) return handler(ctx, e.Add(field.Time(key, time.Now())))
} }
} }
// WithMetrics adds handle metrics. // WithMetrics adds handle metrics.
func WithMetrics(metrics func(level Level)) Middleware { func WithMetrics(metrics func(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) go metrics(e.Level())
handler(ctx, level, msg, fields)
return handler(ctx, e)
}
}
// WithExit exit by level.
func WithExit(level level.Level) Middleware {
return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
n, err := handler(ctx, e)
if e.Level().Is(level) {
os.Exit(1)
}
return n, err
}
}
// WithPanic panic by level.
func WithPanic(level level.Level) Middleware {
return func(ctx context.Context, e *entry.Entry, handler Logger) (int, error) {
n, err := handler(ctx, e)
if e.Level().Is(level) {
panic(e.String())
}
return n, err
} }
} }

121
std.go
View File

@@ -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)
}

119
writter.go Normal file
View File

@@ -0,0 +1,119 @@
package log
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"sync"
"gitoa.ru/go-4devs/log/entry"
)
// New creates standart logger.
func New(opts ...Option) Logger {
logger := log{e: stringFormat(), w: os.Stderr}
for _, opt := range opts {
opt(&logger)
}
return func(_ context.Context, entry *entry.Entry) (int, error) {
b, err := logger.e(entry)
if err != nil {
return 0, fmt.Errorf("enode err: %w", err)
}
n, err := logger.w.Write(b)
if err != nil {
return 0, fmt.Errorf("failed write: %w", err)
}
return n, nil
}
}
// Option configure log.
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 WithWriter(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 WithEncode(stringFormat())
}
// WithJSONFormat sets json output format.
func WithJSONFormat() Option {
return WithEncode(jsonFormat)
}
//nolint:forcetypeassert
func stringFormat() func(entry *entry.Entry) ([]byte, error) {
pool := sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
return func(entry *entry.Entry) ([]byte, error) {
buf := pool.Get().(*bytes.Buffer)
buf.Reset()
defer func() {
pool.Put(buf)
}()
buf.WriteString("msg=\"")
buf.WriteString(strings.TrimSpace(entry.Message()))
buf.WriteString("\"")
for _, field := range entry.Fields() {
buf.WriteString(" ")
buf.WriteString(string(field.Key()))
buf.WriteString("=")
buf.WriteString(field.Value().String())
}
buf.WriteString("\n")
return buf.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, fmt.Errorf("marshal err: %w", err)
}
return append(res, []byte("\n")...), nil
}