Skip to content

Commit

Permalink
chore: Switch from zerolog to slog (#2068)
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Henderson <[email protected]>
  • Loading branch information
hairyhenderson authored May 11, 2024
1 parent e25ea48 commit a55441b
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 135 deletions.
5 changes: 1 addition & 4 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
linters-settings:
govet:
check-shadowing: true
enable-all: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 10
dupl:
Expand All @@ -15,7 +12,6 @@ linters-settings:
lll:
line-length: 140
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
Expand Down Expand Up @@ -60,6 +56,7 @@ linters:
- prealloc
- revive
- rowserrcheck
- sloglint
- sqlclosecheck
- staticcheck
- stylecheck
Expand Down
2 changes: 1 addition & 1 deletion docs/content/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ The `GOMPLATE_LOG_FORMAT` environment variable can be used to control the format
of the log messages that gomplate may output, whether error messages or debug
messages when the [`--verbose`](#verbose) option is in use.

The value can be set to `json`, `logfmt`, `console`, or `simple`.
The value can be set to `json` or `logfmt`.

#### `json` format

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/hairyhenderson/gomplate/v4

go 1.22.1
go 1.22.3

require (
cuelang.org/go v0.8.2
Expand All @@ -21,7 +21,7 @@ require (
github.com/itchyny/gojq v0.12.15
github.com/johannesboyne/gofakes3 v0.0.0-20240217095638-c55a48f17be6
github.com/joho/godotenv v1.5.1
github.com/rs/zerolog v1.32.0
github.com/lmittmann/tint v1.0.4
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
github.com/ugorji/go/codec v1.2.12
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
Expand Down Expand Up @@ -188,7 +187,6 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
Expand Down Expand Up @@ -378,6 +376,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
Expand All @@ -392,7 +392,6 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
Expand Down Expand Up @@ -456,9 +455,6 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
Expand Down
7 changes: 2 additions & 5 deletions internal/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ package cmd
import (
"context"
"fmt"
"log/slog"
"time"

"github.com/hairyhenderson/gomplate/v4/conv"
"github.com/hairyhenderson/gomplate/v4/env"
"github.com/hairyhenderson/gomplate/v4/internal/config"
"github.com/hairyhenderson/gomplate/v4/internal/datafs"

"github.com/rs/zerolog"

"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -66,8 +65,6 @@ func pickConfigFile(cmd *cobra.Command) (cfgFile string, required bool) {
}

func readConfigFile(ctx context.Context, cmd *cobra.Command) (cfg *config.Config, err error) {
log := zerolog.Ctx(ctx)

cfgFile, configRequired := pickConfigFile(cmd)

// we only support loading configs from the local filesystem for now
Expand All @@ -89,7 +86,7 @@ func readConfigFile(ctx context.Context, cmd *cobra.Command) (cfg *config.Config
return cfg, fmt.Errorf("parsing config file %q: %w", cfgFile, err)
}

log.Debug().Str("cfgFile", cfgFile).Msg("using config file")
slog.DebugContext(ctx, "using config file", "cfgFile", cfgFile)

return cfg, nil
}
Expand Down
97 changes: 34 additions & 63 deletions internal/cmd/logger.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package cmd

import (
"context"
"fmt"
"io"
stdlog "log"
"log/slog"
"os"
"runtime"
"time"

"github.com/hairyhenderson/gomplate/v4/env"
"github.com/rs/zerolog"
zlog "github.com/rs/zerolog/log"
"github.com/lmittmann/tint"
"golang.org/x/term"
)

Expand All @@ -23,78 +19,53 @@ func logFormat(out io.Writer) string {
return env.Getenv("GOMPLATE_LOG_FORMAT", defaultFormat)
}

func fmtField(fname string) func(i interface{}) string {
return func(i interface{}) string {
if i == nil || i == "" {
return ""
}

if s, ok := i.(string); ok {
for _, c := range s {
if c <= 0x20 || c == '\\' || c == '"' {
return fmt.Sprintf("%s=%q", fname, i)
}
}
}
return fmt.Sprintf("%s=%s", fname, i)
}
}

func createLogger(format string, out io.Writer) zerolog.Logger {
zerolog.MessageFieldName = "msg"

l := zlog.Logger.Output(out)

stdlogger := l.With().Bool("stdlog", true).Logger()
stdlog.SetFlags(0)
func createLogHandler(format string, out io.Writer, level slog.Level) slog.Handler {
opts := &slog.HandlerOptions{Level: level}

var handler slog.Handler
switch format {
case "console":
// logFormat() already checks if this is a terminal, but we need to
// check again because the format may be overridden with `GOMPLATE_LOG_FORMAT`
useColour := false
if f, ok := out.(*os.File); ok && term.IsTerminal(int(f.Fd())) && runtime.GOOS != "windows" {
useColour = true
}
l = l.Output(zerolog.ConsoleWriter{
Out: out,
NoColor: !useColour,
handler = tint.NewHandler(out, &tint.Options{
Level: level,
TimeFormat: "15:04:05",
NoColor: !useColour,
})
stdlogger = stdlogger.Output(zerolog.ConsoleWriter{
Out: out,
NoColor: !useColour,
FormatLevel: func(_ interface{}) string { return "" },
case "simple":
handler = tint.NewHandler(out, &tint.Options{
Level: level,
NoColor: true,
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr {
if attr.Key == "level" {
attr.Value = slog.StringValue("")
}
if attr.Key == "time" {
attr.Value = slog.StringValue("")
}
return attr
},
})
case "logfmt":
w := zerolog.ConsoleWriter{
Out: out,
NoColor: true,
FormatMessage: fmtField(zerolog.MessageFieldName),
FormatLevel: fmtField(zerolog.LevelFieldName),
FormatTimestamp: fmtField(zerolog.TimestampFieldName),
}
l = l.Output(w)
stdlogger = stdlogger.Output(w)
case "simple":
w := zerolog.ConsoleWriter{
Out: out,
NoColor: true,
FormatLevel: func(_ interface{}) string { return "" },
FormatTimestamp: func(_ interface{}) string { return "" },
}
l = l.Output(w)
stdlogger = stdlogger.Output(w)
handler = slog.NewTextHandler(out, opts)
default:
// json is still default
handler = slog.NewJSONHandler(out, opts)
}
stdlog.SetOutput(stdlogger)

return l
return handler
}

func initLogger(ctx context.Context, out io.Writer) context.Context {
func initLogger(out io.Writer, level slog.Level) {
// default to warn level
zerolog.SetGlobalLevel(zerolog.WarnLevel)
zerolog.DurationFieldUnit = time.Second

l := createLogger(logFormat(out), out)
if level == 0 {
level = slog.LevelWarn
}

return l.WithContext(ctx)
handler := createLogHandler(logFormat(out), out, level)
slog.SetDefault(slog.New(handler))
}
66 changes: 45 additions & 21 deletions internal/cmd/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package cmd

import (
"bytes"
"context"
"log"
"log/slog"
"os"
"strings"
"testing"
"time"

"github.com/rs/zerolog"
zlog "github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
)

Expand All @@ -24,41 +25,64 @@ func TestLogFormat(t *testing.T) {
assert.Equal(t, "simple", logFormat(&bytes.Buffer{}))
}

func TestCreateLogger(t *testing.T) {
zerolog.SetGlobalLevel(zerolog.WarnLevel)
buf := &bytes.Buffer{}
// a slog handler that strips the 'time' field
type noTimestampHandler struct {
slog.Handler
}

var _ slog.Handler = (*noTimestampHandler)(nil)

func (h *noTimestampHandler) Handle(ctx context.Context, r slog.Record) error {
r.Time = time.Time{}

return h.Handler.Handle(ctx, r)
}

// avoid timestamps
zlog.Logger = zerolog.New(buf)
func TestCreateLogHandler(t *testing.T) {
buf := &bytes.Buffer{}
h := createLogHandler("", buf, slog.LevelWarn)
// strip the 'time' field for easier comparison
h = &noTimestampHandler{h}
l := slog.New(h)
slog.SetDefault(l)

l := createLogger("", buf)
l.Debug().Msg("this won't show up")
l.Warn().Msg("hello world")
l.Debug("this won't show up")
l.Warn("hello world")

actual := strings.TrimSpace(buf.String())
assert.Equal(t, `{"level":"warn","msg":"hello world"}`, actual)
assert.JSONEq(t, `{"level":"WARN","msg":"hello world"}`, actual)

buf.Reset()
zerolog.SetGlobalLevel(zerolog.DebugLevel)
l = createLogger("simple", buf)
l.Debug().Str("field", "a value").Msg("this will show up")
h = createLogHandler("simple", buf, slog.LevelDebug)
l = slog.New(h)
slog.SetDefault(l)

l.Debug("this will show up", "field", "a value")
log.Println("hello world")

actual = strings.TrimSpace(buf.String())
assert.Equal(t, "this will show up field=\"a value\"\nhello world stdlog=true", actual)
assert.Equal(t, "this will show up field=\"a value\"\n hello world", actual)

buf.Reset()
l = createLogger("console", buf)
l.Debug().Msg("hello")
h = createLogHandler("console", buf, slog.LevelDebug)
h = &noTimestampHandler{h}
l = slog.New(h)
slog.SetDefault(l)

l.Debug("hello")
log.Println("world")

actual = strings.TrimSpace(buf.String())
assert.Equal(t, "<nil> DBG hello\n<nil> world stdlog=true", actual)
assert.Equal(t, "DBG hello\nINF world", actual)

buf.Reset()
l = createLogger("logfmt", buf)
l.Info().Str("field", "a value").Int("num", 84).Msg("hello\"")
h = createLogHandler("logfmt", buf, slog.LevelInfo)
h = &noTimestampHandler{h}
l = slog.New(h)
slog.SetDefault(l)

l.Info("hello\"", "field", "a value", "num", 84)

actual = strings.TrimSpace(buf.String())
assert.Equal(t, "level=info msg=\"hello\\\"\" field=\"a value\" num=84", actual)
assert.Equal(t, "level=INFO msg=\"hello\\\"\" field=\"a value\" num=84", actual)
}
Loading

0 comments on commit a55441b

Please sign in to comment.