From 57a908f894c9406dcc8efc6e60862ea7c6f3e0c1 Mon Sep 17 00:00:00 2001 From: andrey Date: Wed, 3 Jan 2024 21:35:21 +0300 Subject: [PATCH] add bracket format (#15) Reviewed-on: https://gitoa.ru/go-4devs/log/pulls/15 Co-authored-by: andrey Co-committed-by: andrey --- .drone.yml | 75 -------------------------------------- .golangci.yml | 1 + example/log.go | 7 +++- field/field.go | 11 ++++++ global.go | 3 +- handler/logrus/logger.go | 2 + handler/otel/logger.go | 1 + handler/otel/middleware.go | 1 + handler/zap/logger.go | 4 ++ writer_example_test.go | 37 +++++++++++++++++++ writter.go | 69 ++++++++++++++++++++++++++++++++--- 11 files changed, 128 insertions(+), 83 deletions(-) create mode 100644 writer_example_test.go diff --git a/.drone.yml b/.drone.yml index 83c8c7a..f49d33f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,78 +20,3 @@ steps: volumes: - name: deps temp: {} - ---- -kind: pipeline -type: docker -name: otel - -steps: -- name: test - image: golang:1.21.5 - volumes: - - name: deps - path: /go/src/mod - commands: - - cd handler/otel - - go test - -- name: golangci-lint - image: golangci/golangci-lint:v1.55 - commands: - - cd handler/otel - - golangci-lint run - -volumes: -- name: deps - temp: {} - ---- -kind: pipeline -type: docker -name: logrus - -steps: -- name: test - image: golang:1.21.5 - volumes: - - name: deps - path: /go/src/mod - commands: - - cd handler/logrus - - go test - -- name: golangci-lint - image: golangci/golangci-lint:v1.55 - commands: - - cd handler/logrus - - golangci-lint run - -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/.golangci.yml b/.golangci.yml index 394728e..e2ed2a9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -73,3 +73,4 @@ issues: - path: example/* linters: - gomnd + - lll diff --git a/example/log.go b/example/log.go index f313ca8..9325882 100644 --- a/example/log.go +++ b/example/log.go @@ -2,6 +2,7 @@ package main import ( "context" + "time" "gitoa.ru/go-4devs/log" "gitoa.ru/go-4devs/log/field" @@ -16,9 +17,13 @@ func main() { log.Err(ctx, "error message", 42) service(ctx, log.Log()) - logger := log.New(log.WithJSONFormat()).With(log.WithSource(10, log.TrimPath)) + logger := log.New(log.WithJSONFormat()).With(log.WithSource(10, log.TrimPath), log.WithTime(log.KeyTime, time.RFC3339)) logger.AlertKV(ctx, "alert message new logger", field.String("string", "value")) service(ctx, logger) + + strLogger := log.New(log.WithFormat(log.FormatWithBracket())).With(log.WithSource(10, log.TrimPath), log.WithTime(log.KeyTime, time.RFC3339)) + strLogger.AlertKV(ctx, "alert message new txt logger", field.String("string", "value")) + service(ctx, strLogger) } func service(ctx context.Context, logger log.Logger) { diff --git a/field/field.go b/field/field.go index d6ad541..ac32ce9 100644 --- a/field/field.go +++ b/field/field.go @@ -501,3 +501,14 @@ type Field struct { func (f Field) String() string { return fmt.Sprintf("%s=%+v", f.Key, f.Value) } + +// String implent stringer. +func (f Field) IsKey(keys ...string) bool { + for _, key := range keys { + if key == f.Key { + return true + } + } + + return false +} diff --git a/global.go b/global.go index 7114b45..97c3270 100644 --- a/global.go +++ b/global.go @@ -3,6 +3,7 @@ package log import ( "context" "io" + "time" "gitoa.ru/go-4devs/log/field" "gitoa.ru/go-4devs/log/level" @@ -10,7 +11,7 @@ import ( //nolint:gochecknoglobals,gomnd var global = With(New(), - WithSource(2, TrimPath), + WithTime(KeyTime, time.RFC3339), WithLevel(KeyLevel, level.Debug), WithExit(level.Alert), WithPanic(level.Emergency), diff --git a/handler/logrus/logger.go b/handler/logrus/logger.go index 7059a91..c042793 100644 --- a/handler/logrus/logger.go +++ b/handler/logrus/logger.go @@ -11,11 +11,13 @@ import ( ) // Standard create new standart logrus handler. +// Deprecated: delete after 0.7.0 func Standard() log.Logger { return New(logrus.StandardLogger()) } // New create new logrus handler. +// Deprecated: delete after 0.7.0 func New(log *logrus.Logger) log.Logger { return func(ctx context.Context, data *entry.Entry) (int, error) { lrgFields := make(logrus.Fields, data.Fields().Len()) diff --git a/handler/otel/logger.go b/handler/otel/logger.go index ba256f1..fbfdd33 100644 --- a/handler/otel/logger.go +++ b/handler/otel/logger.go @@ -7,6 +7,7 @@ import ( "gitoa.ru/go-4devs/log/entry" ) +// Deprecated: delete after 0.7.0 func New() log.Logger { return func(ctx context.Context, e *entry.Entry) (int, error) { addEvent(ctx, e) diff --git a/handler/otel/middleware.go b/handler/otel/middleware.go index 7b7be1d..19e0488 100644 --- a/handler/otel/middleware.go +++ b/handler/otel/middleware.go @@ -7,6 +7,7 @@ import ( "gitoa.ru/go-4devs/log/entry" ) +// Deprecated: delete after 0.7.0 func Middleware() log.Middleware { return func(ctx context.Context, e *entry.Entry, handler log.Logger) (int, error) { addEvent(ctx, e) diff --git a/handler/zap/logger.go b/handler/zap/logger.go index 8cb3bd6..56a415e 100644 --- a/handler/zap/logger.go +++ b/handler/zap/logger.go @@ -10,14 +10,17 @@ import ( "go.uber.org/zap" ) +// Deprecated: delete after 0.7.0 func Nop() log.Logger { return New(zap.NewNop()) } +// Deprecated: delete after 0.7.0 func Example(options ...zap.Option) log.Logger { return New(zap.NewExample(options...)) } +// Deprecated: delete after 0.7.0 func Production(options ...zap.Option) log.Logger { z, err := zap.NewProduction(options...) if err != nil { @@ -27,6 +30,7 @@ func Production(options ...zap.Option) log.Logger { return New(z) } +// Deprecated: delete after 0.7.0 func Development(options ...zap.Option) log.Logger { z, err := zap.NewDevelopment(options...) if err != nil { diff --git a/writer_example_test.go b/writer_example_test.go new file mode 100644 index 0000000..dfe6672 --- /dev/null +++ b/writer_example_test.go @@ -0,0 +1,37 @@ +package log_test + +import ( + "context" + "math" + "path/filepath" + "time" + + "gitoa.ru/go-4devs/log" + "gitoa.ru/go-4devs/log/entry" + "gitoa.ru/go-4devs/log/field" + "gitoa.ru/go-4devs/log/level" +) + +func exampleWithTime(key, format string) log.Middleware { + return func(ctx context.Context, e *entry.Entry, handler log.Logger) (int, error) { + return handler(ctx, e.Add(field.FormatTime(key, format, time.Unix(math.MaxInt32, 0).In(time.UTC)))) + } +} + +func ExampleFormatWithBracket() { + ctx := context.Background() + logger := log.New(log.WithFormat(log.FormatWithBracket()), log.WithStdout()).With( + log.WithSource(10, filepath.Base), + // log.WithTime(log.KeyTime, time.RFC3339), + exampleWithTime(log.KeyTime, time.RFC3339), + log.WithLevel(log.KeyLevel, level.Info), + ) + + logger.InfoKV(ctx, "imfo message", field.Int64("num", 42)) + + serviceLogger := logger.With(log.WithName("service_name")) + serviceLogger.Err(ctx, "error message") + // Output: + // 2038-01-19T03:14:07Z [info] writer_example_test.go:30 "imfo message" num=42 + // 2038-01-19T03:14:07Z [error][service_name] writer_example_test.go:33 "error message" +} diff --git a/writter.go b/writter.go index 0a79e04..7db5f7b 100644 --- a/writter.go +++ b/writter.go @@ -43,15 +43,18 @@ func WithStdout() func(*option) { // WithStringFormat sets format as simple string. func WithStringFormat() func(*option) { - return func(o *option) { - o.format = formatText() - } + return WithFormat(formatString()) } // WithJSONFormat sets json output format. func WithJSONFormat() func(*option) { + return WithFormat(formatJSON()) +} + +// WithFormat sets custom output format. +func WithFormat(format func(io.Writer, *entry.Entry) (int, error)) func(*option) { return func(o *option) { - o.format = formatJSON() + o.format = format } } @@ -63,7 +66,7 @@ type option struct { // New creates standart logger. func New(opts ...func(*option)) Logger { log := option{ - format: formatText(), + format: formatString(), out: os.Stderr, } @@ -76,7 +79,61 @@ func New(opts ...func(*option)) Logger { } } -func formatText() func(io.Writer, *entry.Entry) (int, error) { +func FormatWithBracket() func(io.Writer, *entry.Entry) (int, error) { + enc := field.NewEncoderText() + + appendValue := func(buf *buffer.Buffer, data field.Fields, key, prefix, suffix string) *buffer.Buffer { + data.Fields( + func(f field.Field) bool { + if f.IsKey(key) { + _, _ = buf.WriteString(prefix) + *buf = enc.AppendValue(*buf, f.Value) + _, _ = buf.WriteString(suffix) + + return false + } + + return true + }) + + return buf + } + + return func(w io.Writer, data *entry.Entry) (int, error) { + buf := buffer.New() + defer func() { + buf.Free() + }() + + fields := data.Fields() + buf = appendValue(buf, fields, KeyTime, "", " ") + _, _ = buf.WriteString("[") + *buf = enc.AppendValue(*buf, field.StringValue(data.Level().String())) + _, _ = buf.WriteString("]") + buf = appendValue(buf, fields, KeyName, "[", "]") + buf = appendValue(buf, fields, KeySource, " ", " ") + *buf = enc.AppendValue(*buf, field.StringValue(data.Message())) + + fields.Fields(func(f field.Field) bool { + if !f.IsKey(KeyTime, KeySource, KeyName, KeyLevel) { + *buf = enc.AppendField(*buf, f) + } + + return true + }) + + _, _ = buf.WriteString("\n") + + n, err := w.Write(*buf) + if err != nil { + return 0, fmt.Errorf("format text:%w", err) + } + + return n, nil + } +} + +func formatString() func(io.Writer, *entry.Entry) (int, error) { enc := field.NewEncoderText() return func(w io.Writer, entry *entry.Entry) (int, error) {