Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ jobs:
go vet ./...
go build -o /dev/null ./...

- name: Test zap logger module
if: matrix.go-version == 'stable'
working-directory: logger/zaplogger
run: |
go vet ./...
go test -v -race -coverprofile=../../coverage-zaplogger.txt -covermode=atomic ./...

- uses: codecov/codecov-action@v4
if: matrix.go-version == 'stable'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.txt,./coverage-zaplogger.txt

# end-to-end tests with the real external systems in Docker, see e2e/README.md
e2e:
Expand Down
3 changes: 3 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ tinyconf fills config structs with values from drivers. When initializing `tinyc
as well as drivers. Drivers are applied in the order of addition, the next driver overrides values of the previous one,
so the last driver has the highest priority. You can also add your own driver.

Loggers: `logger.NewFmtLogger`, `logger.NewSlogLogger` (Go 1.21+), and the adapter of zap in the separate module
`github.com/insei/tinyconf/logger/zaplogger`, so zap is not a dependency of the core.

`tinyconf.Manager` has methods:

| Method | Description |
Expand Down
18 changes: 17 additions & 1 deletion docs/features/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,26 @@ import "github.com/insei/tinyconf/logger"
// prints to stdout, levels: FATAL, ERROR, WARN, INFO, DEBUG, TRACE
tinyconf.WithLogger(logger.NewFmtLogger(logger.INFO))

// wraps *slog.Logger, Go 1.21 or newer
tinyconf.WithLogger(logger.NewSlogLogger(slog.Default()))
```

The adapter of [zap](https://github.com/uber-go/zap) is a separate module, so zap is not a dependency of tinyconf for the applications with other loggers:

```bash
go get github.com/insei/tinyconf/logger/zaplogger
```

```go
import "github.com/insei/tinyconf/logger/zaplogger"

// wraps *zap.Logger
tinyconf.WithLogger(logger.NewZapLogger(zapLogger))
tinyconf.WithLogger(zaplogger.New(zapLogger))
```

!!! note "Migration"
`logger.NewZapLogger(l)` of the previous versions is `zaplogger.New(l)` from the `github.com/insei/tinyconf/logger/zaplogger` module.

## Custom logger

Implement the `tinyconf.Logger` interface:
Expand Down
10 changes: 6 additions & 4 deletions e2e/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,12 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
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.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
Expand Down
2 changes: 0 additions & 2 deletions example/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/insei/cast v1.1.1 // indirect
github.com/insei/fmap/v3 v3.3.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/sys v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Expand Down
6 changes: 0 additions & 6 deletions example/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ require (
github.com/insei/cast v1.1.1
github.com/insei/fmap/v3 v3.3.0
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
)
5 changes: 0 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
19 changes: 0 additions & 19 deletions logger/fields_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import (
"strings"
"testing"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"

"github.com/insei/tinyconf"
)

Expand All @@ -22,18 +18,3 @@ func TestFmtLogger_Fields(t *testing.T) {
}
}
}

func TestZapLogger_With(t *testing.T) {
core, logs := observer.New(zapcore.DebugLevel)
logger := NewZapLogger(zap.New(core)).With(tinyconf.Field{Key: "driver", Value: "env"})
logger.Info("message", tinyconf.Field{Key: "field", Value: "Name"})

entries := logs.All()
if len(entries) != 1 {
t.Fatalf("Expected 1 log entry, but got %d", len(entries))
}
fields := entries[0].ContextMap()
if entries[0].Message != "message" || fields["driver"] != "env" || fields["field"] != "Name" {
t.Errorf("Unexpected log entry: %s %v", entries[0].Message, fields)
}
}
56 changes: 56 additions & 0 deletions logger/slog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//go:build go1.21

package logger

import (
"context"
"log/slog"

"github.com/insei/tinyconf"
)

type slogLogger struct {
l *slog.Logger
}

func attrs(fields []tinyconf.Field) []any {
args := make([]any, 0, len(fields))
for _, field := range fields {
args = append(args, slog.Any(field.Key, field.Value))
}
return args
}

func (s *slogLogger) log(level slog.Level, msg string, fields []tinyconf.Field) {
s.l.Log(context.Background(), level, msg, attrs(fields)...)
}

// Debug logs a message at the DEBUG level.
func (s *slogLogger) Debug(msg string, fields ...tinyconf.Field) {
s.log(slog.LevelDebug, msg, fields)
}

// Error logs a message at the ERROR level.
func (s *slogLogger) Error(msg string, fields ...tinyconf.Field) {
s.log(slog.LevelError, msg, fields)
}

// Warn logs a message at the WARN level.
func (s *slogLogger) Warn(msg string, fields ...tinyconf.Field) {
s.log(slog.LevelWarn, msg, fields)
}

// Info logs a message at the INFO level.
func (s *slogLogger) Info(msg string, fields ...tinyconf.Field) {
s.log(slog.LevelInfo, msg, fields)
}

// With returns the logger with the fields.
func (s *slogLogger) With(fields ...tinyconf.Field) tinyconf.Logger {
return &slogLogger{l: s.l.With(attrs(fields)...)}
}

// NewSlogLogger returns the logger of tinyconf that writes to the slog logger, it needs Go 1.21 or newer.
func NewSlogLogger(l *slog.Logger) tinyconf.Logger {
return &slogLogger{l: l}
}
37 changes: 37 additions & 0 deletions logger/slog_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//go:build go1.21

package logger

import (
"bytes"
"log/slog"
"testing"

"github.com/insei/tinyconf"
)

func TestSlogLogger(t *testing.T) {
var out bytes.Buffer
handler := slog.NewTextHandler(&out, &slog.HandlerOptions{
Level: slog.LevelDebug,
// the time is removed for the stable output
ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
return slog.Attr{}
}
return a
},
})
logger := NewSlogLogger(slog.New(handler)).With(tinyconf.Field{Key: "driver", Value: "env"})
logger.Debug("debug", tinyconf.Field{Key: "field", Value: "Name"})
logger.Info("info")
logger.Warn("warn")
logger.Error("error", tinyconf.Field{Key: "details", Value: "failed"})
want := "level=DEBUG msg=debug driver=env field=Name\n" +
"level=INFO msg=info driver=env\n" +
"level=WARN msg=warn driver=env\n" +
"level=ERROR msg=error driver=env details=failed\n"
if out.String() != want {
t.Errorf("unexpected output:\n%s\nwant:\n%s", out.String(), want)
}
}
49 changes: 0 additions & 49 deletions logger/zap.go

This file was deleted.

87 changes: 0 additions & 87 deletions logger/zap_test.go

This file was deleted.

Loading
Loading