diff --git a/.drone.yml b/.drone.yml index cdf610a..83c8c7a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -70,3 +70,28 @@ steps: volumes: - name: deps temp: {} + +--- +kind: pipeline +type: docker +name: zap + +steps: +- name: test + image: golang:1.21.5 + volumes: + - name: deps + path: /go/src/mod + commands: + - cd handler/zap + - go test + +- name: golangci-lint + image: golangci/golangci-lint:v1.55 + commands: + - cd handler/zap + - golangci-lint run + +volumes: +- name: deps + temp: {} diff --git a/handler/zap/go.mod b/handler/zap/go.mod new file mode 100644 index 0000000..18e22d8 --- /dev/null +++ b/handler/zap/go.mod @@ -0,0 +1,10 @@ +module gitoa.ru/go-4devs/log/handler/zap + +go 1.21.5 + +require ( + gitoa.ru/go-4devs/log v0.5.1 + go.uber.org/zap v1.26.0 +) + +require go.uber.org/multierr v1.10.0 // indirect diff --git a/handler/zap/go.sum b/handler/zap/go.sum new file mode 100644 index 0000000..95e5ca9 --- /dev/null +++ b/handler/zap/go.sum @@ -0,0 +1,16 @@ +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gitoa.ru/go-4devs/log v0.5.1 h1:rrIyjpUaw8AjDCf7ZuH0HgCRf370O3TV29yrU1xizWM= +gitoa.ru/go-4devs/log v0.5.1/go.mod h1:tREtjEH2cTHl0p3uCVcH9g5tlqtsVNI/tDQVfq53Ty4= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handler/zap/logger.go b/handler/zap/logger.go new file mode 100644 index 0000000..8cb3bd6 --- /dev/null +++ b/handler/zap/logger.go @@ -0,0 +1,66 @@ +package zap + +import ( + "context" + + "gitoa.ru/go-4devs/log" + "gitoa.ru/go-4devs/log/entry" + "gitoa.ru/go-4devs/log/field" + "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, 0, data.Fields().Len()) + data.Fields().Fields(func(f field.Field) bool { + zf = append(zf, zap.Any(f.Key, f.Value.Any())) + + return true + }) + + 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 + } +} diff --git a/handler/zap/logger_example_test.go b/handler/zap/logger_example_test.go new file mode 100644 index 0000000..071b4a3 --- /dev/null +++ b/handler/zap/logger_example_test.go @@ -0,0 +1,23 @@ +package zap_test + +import ( + "context" + "io" + + "gitoa.ru/go-4devs/log/field" + "gitoa.ru/go-4devs/log/handler/zap" + uzap "go.uber.org/zap" +) + +func ExampleNew_zapHandler() { + ctx := context.Background() + 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"} +} diff --git a/handler/zap/logger_test.go b/handler/zap/logger_test.go new file mode 100644 index 0000000..88e9907 --- /dev/null +++ b/handler/zap/logger_test.go @@ -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) + } +}