Skip to content

Commit

Permalink
v2: store zap fields instead of full logger to reach better performan…
Browse files Browse the repository at this point in the history
…ce (#12)
  • Loading branch information
yuseferi committed Nov 9, 2023
1 parent 57b0c7f commit d142945
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 46 deletions.
46 changes: 26 additions & 20 deletions README.md
Expand Up @@ -11,24 +11,28 @@ Zax is a library that adds context to [Zap Logger](https://github.com/uber-go/za
### Installation

```shell
go get -u github.com/yuseferi/zax
go get -u github.com/yuseferi/zax/v2
```

### Usage:
To add something to the context and carry it along, simply use zap.Set:

ctx = zax.Set(ctx, logger, zap.String("trace_id", "my-trace-id"))
ctx = zax.Set(ctx, []zap.Field{zap.String("trace_id", "my-trace-id")})

To retrieve a logger with the contexted fields, use zax.Get:
To retrieve stored zap fields in context, use zax.Get:

zax.Get(ctx) // this retrive stored zap fields in context

To retrieve stored zap fields in context and log them :

logger.With(zax.Get(ctx)...).Info("just a test record")

zax.Get(ctx)

After that, you can use the output as a regular logger and perform logging operations:

```Go
zax.Get(ctx).Info(....)
zax.Get(ctx).Debug(....)
.....
logger.With(zax.Get(ctx)...).Info("message")
logger.With(zax.Get(ctx)...).Debug("message")
```


Expand All @@ -41,9 +45,9 @@ func main() {
logger, _ := zap.NewProduction()
ctx := context.Background()
s := NewServiceA(logger)
ctx = zax.Set(ctx, logger, zap.String("trace_id", "my-trace-id"))
ctx = zax.Set(ctx, zap.String("trace_id", "my-trace-id"))
// and if you want to add multiple of them at once
//ctx = zax.Set(ctx, logger, []zap.Field{zap.String("trace_id", "my-trace-id"),zap.String("span_id", "my-span-id")})
//ctx = zax.Set(ctx, []zap.Field{zap.String("trace_id", "my-trace-id"),zap.String("span_id", "my-span-id")})
s.funcA(ctx)
}

Expand All @@ -59,24 +63,26 @@ func NewServiceA(logger *zap.Logger) *ServiceA {

func (s *ServiceA) funcA(ctx context.Context) {
s.logger.Info("func A") // it does not contain trace_id, you need to add it manually
zax.Get(ctx).Info("func A") // it will logged with "trace_id" = "my-trace-id"
s.logger.With(zax.Get(ctx)...).Info("func A") // it will logged with "trace_id" = "my-trace-id"
}

```
### benchmark
We have benchmarked Zax against Zap using the same fields. Here are the benchmark results:

```
BenchmarkLoggingWithOnlyZap-10 31756287 34.97 ns/op
BenchmarkLoggingWithOnlyZap-10 35056582 35.06 ns/op
BenchmarkLoggingWithOnlyZap-10 32982284 35.90 ns/op
BenchmarkLoggingWithOnlyZap-10 35061405 34.95 ns/op
BenchmarkLoggingWithOnlyZap-10 33266068 34.86 ns/op
BenchmarkLoggingWithZax-10 18442729 64.53 ns/op
BenchmarkLoggingWithZax-10 18592747 65.57 ns/op
BenchmarkLoggingWithZax-10 17492030 65.26 ns/op
BenchmarkLoggingWithZax-10 18640606 64.66 ns/op
BenchmarkLoggingWithZax-10 18700837 64.58 ns/op
pkg: github.com/yuseferi/zax/v2
BenchmarkLoggingWithOnlyZap-10 344718321 35.23 ns/op
BenchmarkLoggingWithOnlyZap-10 340526908 36.74 ns/op
BenchmarkLoggingWithOnlyZap-10 337279976 36.17 ns/op
BenchmarkLoggingWithOnlyZap-10 338681052 36.18 ns/op
BenchmarkLoggingWithOnlyZap-10 339414484 35.48 ns/op
BenchmarkLoggingWithZax-10 201602071 56.58 ns/op
BenchmarkLoggingWithZax-10 213688218 57.44 ns/op
BenchmarkLoggingWithZax-10 206059045 56.66 ns/op
BenchmarkLoggingWithZax-10 211847756 58.14 ns/op
BenchmarkLoggingWithZax-10 210184916 56.69 ns/op
```

### Contributing
Expand Down
4 changes: 2 additions & 2 deletions go.mod
@@ -1,4 +1,4 @@
module github.com/yuseferi/zax
module github.com/yuseferi/zax/v2

go 1.21

Expand All @@ -10,6 +10,6 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -6,8 +6,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
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/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.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/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
22 changes: 10 additions & 12 deletions zax.go
Expand Up @@ -4,26 +4,24 @@ package zax

import (
"context"

"go.uber.org/zap"
)

type Key string

// Key name which used for save logger in context
// Key name which used for save fields in context
const loggerKey = Key("zax")

// Set Add passed fields to logger and store zap.Logger as variable in context
func Set(ctx context.Context, logger *zap.Logger, fields []zap.Field) context.Context {
if len(fields) > 0 {
logger = logger.With(fields...)
}
return context.WithValue(ctx, loggerKey, logger)
// Set Add passed fields in context
func Set(ctx context.Context, fields []zap.Field) context.Context {
return context.WithValue(ctx, loggerKey, fields)
}

// Get zap.Logger from context
func Get(ctx context.Context) *zap.Logger {
if logger, ok := ctx.Value(loggerKey).(*zap.Logger); ok {
return logger
// Get zap stored fields from context
func Get(ctx context.Context) []zap.Field {
if loggerFields, ok := ctx.Value(loggerKey).([]zap.Field); ok {
return loggerFields
}
return zap.L()
return nil
}
7 changes: 4 additions & 3 deletions zax_benchmark_test.go
Expand Up @@ -2,9 +2,10 @@ package zax

import (
"context"
"testing"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"testing"
)

var someFields = []zap.Field{
Expand All @@ -19,8 +20,8 @@ func LogWithZap(logger *zap.Logger) {

func LogWithZax(logger *zap.Logger) {
ctx := context.Background()
Set(ctx, logger, someFields)
Get(ctx).Info("logging something")
Set(ctx, someFields)
logger.With(Get(ctx)...).Info("logging something")
}

func BenchmarkLoggingWithOnlyZap(b *testing.B) {
Expand Down
22 changes: 15 additions & 7 deletions zax_test.go
Expand Up @@ -43,6 +43,9 @@ func (l *Logger) AssertLogEntryExist(t assert.TestingT, key, value string) bool
}
}
}
if key == "" && value == "" {
return true
}
return assert.Fail(t, fmt.Sprintf("log entry does not exist with, %s = %s", key, value))
}

Expand All @@ -69,13 +72,18 @@ func TestSet(t *testing.T) {
expectedLoggerKey string
expectedLoggerValue string
}{
"context for zax filed is empty": {
context: Set(ctx, nil),
expectedLoggerKey: "",
expectedLoggerValue: "",
},
"context with trace-id": {
context: Set(ctx, testLog.logger, []zap.Field{zap.String(traceIDKey, testTraceID)}),
context: Set(ctx, []zap.Field{zap.String(traceIDKey, testTraceID)}),
expectedLoggerKey: traceIDKey,
expectedLoggerValue: testTraceID,
},
"context with trace-id with new value(to check it will be updated)": {
context: Set(ctx, testLog.logger, []zap.Field{zap.String(traceIDKey, testTraceID2)}),
context: Set(ctx, []zap.Field{zap.String(traceIDKey, testTraceID2)}),
expectedLoggerKey: traceIDKey,
expectedLoggerValue: testTraceID2,
},
Expand All @@ -84,7 +92,7 @@ func TestSet(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
ctx := tc.context
logger := ctx.Value(loggerKey).(*zap.Logger)
logger := testLog.logger.With(Get(ctx)...)
logger.Info("just a test record")
assert.NotNil(t, logger)
testLog.AssertLogEntryExist(t, tc.expectedLoggerKey, tc.expectedLoggerValue)
Expand All @@ -101,20 +109,20 @@ func TestGet(t *testing.T) {
context context.Context
expectedLoggerKey *string
}{
"context with trace-id": {
"context empty": {
context: context.TODO(),
expectedLoggerKey: nil,
},
"context with trace-id with new value(to check it will be updated)": {
context: Set(ctx, testLog.logger, []zap.Field{zap.String(traceIDKey, testTraceID)}),
"context with trace-id field": {
context: Set(ctx, []zap.Field{zap.String(traceIDKey, testTraceID)}),
expectedLoggerKey: &traceIDKey,
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
ctx := tc.context
Get(ctx).Info("just a test record")
testLog.logger.With(Get(ctx)...).Info("just a test record")
if tc.expectedLoggerKey != nil {
testLog.AssertLogEntryKeyExist(t, *tc.expectedLoggerKey)
}
Expand Down

0 comments on commit d142945

Please sign in to comment.