From bf8eb7b7cfa83631f4e1df91e245b7ecd8b9db7f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 8 Feb 2024 00:57:23 +0100 Subject: [PATCH 1/2] chore: Switch to log/slog for logging --- .golangci.yml | 2 +- assets/chezmoi.io/docs/developer/testing.md | 2 +- go.mod | 5 +- go.sum | 4 +- internal/chezmoi/ageencryption.go | 9 +- internal/chezmoi/attr.go | 49 ++-- internal/chezmoi/chezmoi_test.go | 12 - internal/chezmoi/debugencryption.go | 38 +-- internal/chezmoi/debugpersistentstate.go | 70 ++--- internal/chezmoi/debugsystem.go | 153 +++++----- internal/chezmoi/entrystate.go | 33 +-- internal/chezmoi/externaldiffsystem.go | 3 +- internal/chezmoi/gpgencryption.go | 3 +- internal/chezmoi/interpreter.go | 17 +- internal/chezmoi/patternset.go | 17 +- internal/chezmoi/realsystem.go | 3 +- internal/chezmoi/sourcestate.go | 13 +- internal/chezmoi/sourcestateentry.go | 62 ++-- internal/chezmoilog/chezmoilog.go | 269 ++++++++++-------- internal/chezmoilog/chezmoilog_test.go | 66 ----- internal/chezmoilog/nullhandler.go | 14 + internal/chezmoilog/nullhandler_test.go | 5 + internal/chezmoitest/chezmoitest.go | 5 +- internal/cmd/bitwardensecretstemplatefuncs.go | 2 +- internal/cmd/bitwardentemplatefuncs.go | 2 +- internal/cmd/cmd.go | 16 +- internal/cmd/config.go | 110 +++---- internal/cmd/dashlanetemplatefuncs.go | 2 +- internal/cmd/doctorcmd.go | 3 +- internal/cmd/doctorcmd_unix.go | 3 +- internal/cmd/doctorcmd_windows.go | 3 +- internal/cmd/dopplertemplatefuncs.go | 2 +- internal/cmd/editcmd.go | 14 +- internal/cmd/gopasstemplatefuncs.go | 2 +- internal/cmd/hcpvaultsecretsttemplatefuncs.go | 2 +- internal/cmd/initcmd.go | 59 ++-- internal/cmd/keepassxctemplatefuncs.go | 6 +- internal/cmd/keepertemplatefuncs.go | 2 +- internal/cmd/lastpasstemplatefuncs.go | 2 +- internal/cmd/onepasswordtemplatefuncs.go | 4 +- internal/cmd/passholetemplatefuncs.go | 2 +- internal/cmd/passtemplatefuncs.go | 2 +- internal/cmd/pinentry.go | 2 +- internal/cmd/rbwtemplatefuncs.go | 2 +- internal/cmd/secrettemplatefuncs.go | 2 +- internal/cmd/statuscmd.go | 14 +- internal/cmd/templatefuncs.go | 4 +- internal/cmd/textconv.go | 3 +- internal/cmd/upgradecmd.go | 8 +- internal/cmd/upgradecmd_unix.go | 5 +- internal/cmd/vaulttemplatefuncs.go | 2 +- 51 files changed, 551 insertions(+), 583 deletions(-) delete mode 100644 internal/chezmoilog/chezmoilog_test.go create mode 100644 internal/chezmoilog/nullhandler.go create mode 100644 internal/chezmoilog/nullhandler_test.go diff --git a/.golangci.yml b/.golangci.yml index 7c6ea7de9fe..dea30d89e87 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,7 +10,6 @@ linters: - contextcheck - decorder - dogsled - - dupl - dupword - durationcheck - errcheck @@ -81,6 +80,7 @@ linters: - asasalint - cyclop - depguard + - dupl - exhaustive - exhaustruct - funlen diff --git a/assets/chezmoi.io/docs/developer/testing.md b/assets/chezmoi.io/docs/developer/testing.md index ec1c653b17a..c93f84d0697 100644 --- a/assets/chezmoi.io/docs/developer/testing.md +++ b/assets/chezmoi.io/docs/developer/testing.md @@ -9,7 +9,7 @@ chezmoi uses multiple levels of testing: `internal/chezmoi/*_test.go`. 2. Filesystem integration tests, using `testing` and - [`github.com/twpayne/go-vfs/v5`](https://pkg.go.dev/github.com/twpayne/go-vfs/v4), + [`github.com/twpayne/go-vfs/v5`](https://pkg.go.dev/github.com/twpayne/go-vfs/v5), test chezmoi's effects on the filesystem. This include some tests in `internal/chezmoi/*_test.go`, and higher level command tests in `internal/cmd/*cmd_test.go`. diff --git a/go.mod b/go.mod index a3eab448ceb..2aee8768d1c 100644 --- a/go.mod +++ b/go.mod @@ -32,12 +32,11 @@ require ( github.com/muesli/termenv v0.15.2 github.com/pelletier/go-toml/v2 v2.1.1 github.com/rogpeppe/go-internal v1.12.0 - github.com/rs/zerolog v1.32.0 github.com/sergi/go-diff v1.1.0 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a - github.com/twpayne/go-pinentry v0.3.0 + github.com/twpayne/go-pinentry/v3 v3.0.1 github.com/twpayne/go-shell v0.4.0 github.com/twpayne/go-vfs/v5 v5.0.1 github.com/twpayne/go-xdg/v6 v6.1.2 @@ -133,8 +132,8 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.6 // indirect + github.com/rs/zerolog v1.32.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect diff --git a/go.sum b/go.sum index f387b013c60..7ae642d3544 100644 --- a/go.sum +++ b/go.sum @@ -423,8 +423,8 @@ github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29X github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= github.com/twpayne/go-expect v0.0.1 h1:cRJ552FIdQzs4z98Q2OLQsGLSbkB7Xpm/IU6cyQ6mUM= github.com/twpayne/go-expect v0.0.1/go.mod h1:+ffr+YtUt8ifebyvRQ3NhVTiLch/HnfxsAQqO5LeXss= -github.com/twpayne/go-pinentry v0.3.0 h1:Rr+fEOZXmeItOb4thjeVaBWJKB9Xa/eojolycyF/26c= -github.com/twpayne/go-pinentry v0.3.0/go.mod h1:iOIZD+9np/2V24OdCGos7Y1/xX90wc6VEAZsgb+r9D4= +github.com/twpayne/go-pinentry/v3 v3.0.1 h1:rop+jK2x2CT5/Bwpf0EYMl0DVpDWc7IQYlNn8hdOOvM= +github.com/twpayne/go-pinentry/v3 v3.0.1/go.mod h1:l8V/3baYFXtN5NLMgFQe2FFPlGdZRUaoCW61Hxs+1oA= github.com/twpayne/go-shell v0.4.0 h1:RAAMbjEj7mcwDdwC7SiFHGUKR+WDAURU6mnyd3r2p2E= github.com/twpayne/go-shell v0.4.0/go.mod h1:MP3aUA0TQ3IGoJc15ahjb+7A7wZH4NeGrvLZ/aFQsHc= github.com/twpayne/go-vfs/v4 v4.3.0 h1:rTqFzzOQ/6ESKTSiwVubHlCBedJDOhQyVSnw8rQNZhU= diff --git a/internal/chezmoi/ageencryption.go b/internal/chezmoi/ageencryption.go index 7f6290fe1f7..fdc3895af8f 100644 --- a/internal/chezmoi/ageencryption.go +++ b/internal/chezmoi/ageencryption.go @@ -6,6 +6,7 @@ package chezmoi import ( "bytes" "io" + "log/slog" "os" "os/exec" @@ -42,7 +43,7 @@ func (e *AgeEncryption) Decrypt(ciphertext []byte) ([]byte, error) { cmd := exec.Command(e.Command, append(e.decryptArgs(), e.Args...)...) //nolint:gosec cmd.Stdin = bytes.NewReader(ciphertext) cmd.Stderr = os.Stderr - return chezmoilog.LogCmdOutput(cmd) + return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // DecryptToFile implements Encryption.DecryptToFile. @@ -59,7 +60,7 @@ func (e *AgeEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byt cmd := exec.Command(e.Command, args...) //nolint:gosec cmd.Stdin = bytes.NewReader(ciphertext) cmd.Stderr = os.Stderr - return chezmoilog.LogCmdRun(cmd) + return chezmoilog.LogCmdRun(slog.Default(), cmd) } // Encrypt implements Encryption.Encrypt. @@ -71,7 +72,7 @@ func (e *AgeEncryption) Encrypt(plaintext []byte) ([]byte, error) { cmd := exec.Command(e.Command, append(e.encryptArgs(), e.Args...)...) //nolint:gosec cmd.Stdin = bytes.NewReader(plaintext) cmd.Stderr = os.Stderr - return chezmoilog.LogCmdOutput(cmd) + return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // EncryptFile implements Encryption.EncryptFile. @@ -87,7 +88,7 @@ func (e *AgeEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { args := append(append(e.encryptArgs(), e.Args...), plaintextAbsPath.String()) cmd := exec.Command(e.Command, args...) //nolint:gosec cmd.Stderr = os.Stderr - return chezmoilog.LogCmdOutput(cmd) + return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // EncryptedSuffix implements Encryption.EncryptedSuffix. diff --git a/internal/chezmoi/attr.go b/internal/chezmoi/attr.go index 530700eed52..3c1d83e7822 100644 --- a/internal/chezmoi/attr.go +++ b/internal/chezmoi/attr.go @@ -2,9 +2,8 @@ package chezmoi import ( "io/fs" + "log/slog" "strings" - - "github.com/rs/zerolog" ) // A SourceFileTargetType is a the type of a target represented by a file in the @@ -99,15 +98,16 @@ func parseDirAttr(name string) DirAttr { } } -// MarshalZerologObject implements -// github.com/rs/zerolog.ObjectMarshaler.MarshalZerologObject. -func (da DirAttr) MarshalZerologObject(e *zerolog.Event) { - e.Str("TargetName", da.TargetName) - e.Bool("Exact", da.Exact) - e.Bool("External", da.External) - e.Bool("Private", da.Private) - e.Bool("ReadOnly", da.ReadOnly) - e.Bool("Remove", da.Remove) +// LogValue implements log/slog.LogValuer.LogValue. +func (da DirAttr) LogValue() slog.Value { + return slog.GroupValue( + slog.String("TargetName", da.TargetName), + slog.Bool("Exact", da.Exact), + slog.Bool("External", da.External), + slog.Bool("Private", da.Private), + slog.Bool("ReadOnly", da.ReadOnly), + slog.Bool("Remove", da.Remove), + ) } // SourceName returns da's source name. @@ -246,19 +246,20 @@ func parseFileAttr(sourceName, encryptedSuffix string) FileAttr { } } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (fa FileAttr) MarshalZerologObject(e *zerolog.Event) { - e.Str("TargetName", fa.TargetName) - e.Str("Type", sourceFileTypeStrs[fa.Type]) - e.Str("Condition", string(fa.Condition)) - e.Bool("Empty", fa.Empty) - e.Bool("Encrypted", fa.Encrypted) - e.Bool("Executable", fa.Executable) - e.Int("Order", int(fa.Order)) - e.Bool("Private", fa.Private) - e.Bool("ReadOnly", fa.ReadOnly) - e.Bool("Template", fa.Template) +// LogValue implements log/slog.LogValuer.LogValue. +func (fa FileAttr) LogValue() slog.Value { + return slog.GroupValue( + slog.String("TargetName", fa.TargetName), + slog.String("Type", sourceFileTypeStrs[fa.Type]), + slog.String("Condition", string(fa.Condition)), + slog.Bool("Empty", fa.Empty), + slog.Bool("Encrypted", fa.Encrypted), + slog.Bool("Executable", fa.Executable), + slog.Int("Order", int(fa.Order)), + slog.Bool("Private", fa.Private), + slog.Bool("ReadOnly", fa.ReadOnly), + slog.Bool("Template", fa.Template), + ) } // SourceName returns fa's source name. diff --git a/internal/chezmoi/chezmoi_test.go b/internal/chezmoi/chezmoi_test.go index a1521b77a1a..9b76e605a97 100644 --- a/internal/chezmoi/chezmoi_test.go +++ b/internal/chezmoi/chezmoi_test.go @@ -1,27 +1,15 @@ package chezmoi import ( - "os" "strings" "testing" "github.com/alecthomas/assert/v2" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/rs/zerolog/pkgerrors" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/chezmoi/v2/internal/chezmoitest" ) -func init() { - log.Logger = log.Output(zerolog.ConsoleWriter{ - Out: os.Stderr, - NoColor: true, - }) - zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack //nolint:reassign -} - func TestEtcHostsFQDNHostname(t *testing.T) { for _, tc := range []struct { name string diff --git a/internal/chezmoi/debugencryption.go b/internal/chezmoi/debugencryption.go index 892f883d86c..262113bd664 100644 --- a/internal/chezmoi/debugencryption.go +++ b/internal/chezmoi/debugencryption.go @@ -1,20 +1,20 @@ package chezmoi import ( - "github.com/rs/zerolog" + "log/slog" "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) // A DebugEncryption logs all calls to an Encryption. type DebugEncryption struct { - logger *zerolog.Logger + logger *slog.Logger encryption Encryption } // NewDebugEncryption returns a new DebugEncryption that logs methods on // encryption to logger. -func NewDebugEncryption(encryption Encryption, logger *zerolog.Logger) *DebugEncryption { +func NewDebugEncryption(encryption Encryption, logger *slog.Logger) *DebugEncryption { return &DebugEncryption{ logger: logger, encryption: encryption, @@ -24,40 +24,40 @@ func NewDebugEncryption(encryption Encryption, logger *zerolog.Logger) *DebugEnc // Decrypt implements Encryption.Decrypt. func (e *DebugEncryption) Decrypt(ciphertext []byte) ([]byte, error) { plaintext, err := e.encryption.Decrypt(ciphertext) - e.logger.Err(err). - Bytes("ciphertext", chezmoilog.Output(ciphertext, err)). - Bytes("plaintext", chezmoilog.Output(plaintext, err)). - Msg("Decrypt") + chezmoilog.InfoOrError(e.logger, "Decrypt", err, + chezmoilog.FirstFewBytes("ciphertext", ciphertext), + chezmoilog.FirstFewBytes("plaintext", plaintext), + ) return plaintext, err } // DecryptToFile implements Encryption.DecryptToFile. func (e *DebugEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error { err := e.encryption.DecryptToFile(plaintextAbsPath, ciphertext) - e.logger.Err(err). - Stringer("plaintextAbsPath", plaintextAbsPath). - Bytes("ciphertext", chezmoilog.Output(ciphertext, err)). - Msg("DecryptToFile") + chezmoilog.InfoOrError(e.logger, "DecryptToFile", err, + chezmoilog.Stringer("plaintextAbsPath", plaintextAbsPath), + chezmoilog.FirstFewBytes("ciphertext", ciphertext), + ) return err } // Encrypt implements Encryption.Encrypt. func (e *DebugEncryption) Encrypt(plaintext []byte) ([]byte, error) { ciphertext, err := e.encryption.Encrypt(plaintext) - e.logger.Err(err). - Bytes("plaintext", chezmoilog.Output(plaintext, err)). - Bytes("ciphertext", chezmoilog.Output(ciphertext, err)). - Msg("Encrypt") + chezmoilog.InfoOrError(e.logger, "Encrypt", err, + chezmoilog.FirstFewBytes("plaintext", plaintext), + chezmoilog.FirstFewBytes("ciphertext", ciphertext), + ) return ciphertext, err } // EncryptFile implements Encryption.EncryptFile. func (e *DebugEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { ciphertext, err := e.encryption.EncryptFile(plaintextAbsPath) - e.logger.Err(err). - Stringer("plaintextAbsPath", plaintextAbsPath). - Bytes("ciphertext", chezmoilog.Output(ciphertext, err)). - Msg("EncryptFile") + chezmoilog.InfoOrError(e.logger, "EncryptFile", err, + chezmoilog.Stringer("plaintextAbsPath", plaintextAbsPath), + chezmoilog.FirstFewBytes("ciphertext", ciphertext), + ) return ciphertext, err } diff --git a/internal/chezmoi/debugpersistentstate.go b/internal/chezmoi/debugpersistentstate.go index b877e38ed66..3b7b99e6bf5 100644 --- a/internal/chezmoi/debugpersistentstate.go +++ b/internal/chezmoi/debugpersistentstate.go @@ -1,18 +1,20 @@ package chezmoi import ( - "github.com/rs/zerolog" + "log/slog" + + "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) // A DebugPersistentState logs calls to a PersistentState. type DebugPersistentState struct { - logger *zerolog.Logger + logger *slog.Logger persistentState PersistentState } // NewDebugPersistentState returns a new debugPersistentState that logs methods // on persistentState to logger. -func NewDebugPersistentState(persistentState PersistentState, logger *zerolog.Logger) *DebugPersistentState { +func NewDebugPersistentState(persistentState PersistentState, logger *slog.Logger) *DebugPersistentState { return &DebugPersistentState{ logger: logger, persistentState: persistentState, @@ -22,44 +24,42 @@ func NewDebugPersistentState(persistentState PersistentState, logger *zerolog.Lo // Close implements PersistentState.Close. func (s *DebugPersistentState) Close() error { err := s.persistentState.Close() - s.logger.Err(err). - Msg("Close") + chezmoilog.InfoOrError(s.logger, "Close", err) return err } // CopyTo implements PersistentState.CopyTo. func (s *DebugPersistentState) CopyTo(p PersistentState) error { err := s.persistentState.CopyTo(p) - s.logger.Err(err). - Msg("CopyTo") + chezmoilog.InfoOrError(s.logger, "CopyTo", err) return err } // Data implements PersistentState.Data. func (s *DebugPersistentState) Data() (any, error) { data, err := s.persistentState.Data() - s.logger.Err(err). - Interface("data", data). - Msg("Data") + chezmoilog.InfoOrError(s.logger, "Data", err, + slog.Any("data", data), + ) return data, err } // Delete implements PersistentState.Delete. func (s *DebugPersistentState) Delete(bucket, key []byte) error { err := s.persistentState.Delete(bucket, key) - s.logger.Err(err). - Bytes("bucket", bucket). - Bytes("key", key). - Msg("Delete") + chezmoilog.InfoOrError(s.logger, "Delete", err, + chezmoilog.Bytes("bucket", bucket), + chezmoilog.Bytes("key", key), + ) return err } // DeleteBucket implements PersistentState.DeleteBucket. func (s *DebugPersistentState) DeleteBucket(bucket []byte) error { err := s.persistentState.DeleteBucket(bucket) - s.logger.Err(err). - Bytes("bucket", bucket). - Msg("DeleteBucket") + chezmoilog.InfoOrError(s.logger, "DeleteBucket", err, + chezmoilog.Bytes("bucket", bucket), + ) return err } @@ -67,37 +67,37 @@ func (s *DebugPersistentState) DeleteBucket(bucket []byte) error { func (s *DebugPersistentState) ForEach(bucket []byte, fn func(k, v []byte) error) error { err := s.persistentState.ForEach(bucket, func(k, v []byte) error { err := fn(k, v) - s.logger.Err(err). - Bytes("bucket", bucket). - Bytes("key", k). - Bytes("value", v). - Msg("ForEach") + chezmoilog.InfoOrError(s.logger, "ForEach", err, + chezmoilog.Bytes("bucket", bucket), + chezmoilog.Bytes("key", k), + chezmoilog.Bytes("value", v), + ) return err }) - s.logger.Err(err). - Bytes("bucket", bucket). - Msg("ForEach") + chezmoilog.InfoOrError(s.logger, "ForEach", err, + chezmoilog.Bytes("bucket", bucket), + ) return err } // Get implements PersistentState.Get. func (s *DebugPersistentState) Get(bucket, key []byte) ([]byte, error) { value, err := s.persistentState.Get(bucket, key) - s.logger.Err(err). - Bytes("bucket", bucket). - Bytes("key", key). - Bytes("value", value). - Msg("Get") + chezmoilog.InfoOrError(s.logger, "Get", err, + chezmoilog.Bytes("bucket", bucket), + chezmoilog.Bytes("key", key), + chezmoilog.Bytes("value", value), + ) return value, err } // Set implements PersistentState.Set. func (s *DebugPersistentState) Set(bucket, key, value []byte) error { err := s.persistentState.Set(bucket, key, value) - s.logger.Err(err). - Bytes("bucket", bucket). - Bytes("key", key). - Bytes("value", value). - Msg("Set") + chezmoilog.InfoOrError(s.logger, "Set", err, + chezmoilog.Bytes("bucket", bucket), + chezmoilog.Bytes("key", key), + chezmoilog.Bytes("value", value), + ) return err } diff --git a/internal/chezmoi/debugsystem.go b/internal/chezmoi/debugsystem.go index cdabff6a8ed..edc03d5b41e 100644 --- a/internal/chezmoi/debugsystem.go +++ b/internal/chezmoi/debugsystem.go @@ -2,23 +2,23 @@ package chezmoi import ( "io/fs" + "log/slog" "os/exec" "time" - "github.com/rs/zerolog" - vfs "github.com/twpayne/go-vfs/v5" + "github.com/twpayne/go-vfs/v5" "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) // A DebugSystem logs all calls to a System. type DebugSystem struct { - logger *zerolog.Logger + logger *slog.Logger system System } // NewDebugSystem returns a new DebugSystem that logs methods on system to logger. -func NewDebugSystem(system System, logger *zerolog.Logger) *DebugSystem { +func NewDebugSystem(system System, logger *slog.Logger) *DebugSystem { return &DebugSystem{ logger: logger, system: system, @@ -28,60 +28,60 @@ func NewDebugSystem(system System, logger *zerolog.Logger) *DebugSystem { // Chtimes implements System.Chtimes. func (s *DebugSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { err := s.system.Chtimes(name, atime, mtime) - s.logger.Err(err). - Stringer("name", name). - Time("atime", atime). - Time("mtime", mtime). - Msg("Chtimes") + chezmoilog.InfoOrError(s.logger, "Chtimes", err, + chezmoilog.Stringer("name", name), + slog.Time("atime", atime), + slog.Time("mtime", mtime), + ) return err } // Chmod implements System.Chmod. func (s *DebugSystem) Chmod(name AbsPath, mode fs.FileMode) error { err := s.system.Chmod(name, mode) - s.logger.Err(err). - Stringer("name", name). - Int("mode", int(mode)). - Msg("Chmod") + chezmoilog.InfoOrError(s.logger, "Chmod", err, + chezmoilog.Stringer("name", name), + slog.Int("mode", int(mode)), + ) return err } // Glob implements System.Glob. func (s *DebugSystem) Glob(name string) ([]string, error) { matches, err := s.system.Glob(name) - s.logger.Err(err). - Str("name", name). - Strs("matches", matches). - Msg("Glob") + chezmoilog.InfoOrError(s.logger, "Glob", err, + slog.String("name", name), + slog.Any("matches", matches), + ) return matches, err } // Link implements System.Link. func (s *DebugSystem) Link(oldpath, newpath AbsPath) error { err := s.system.Link(oldpath, newpath) - s.logger.Err(err). - Stringer("oldpath", oldpath). - Stringer("newpath", newpath). - Msg("Link") + chezmoilog.InfoOrError(s.logger, "Link", err, + chezmoilog.Stringer("oldpath", oldpath), + chezmoilog.Stringer("newpath", newpath), + ) return err } // Lstat implements System.Lstat. func (s *DebugSystem) Lstat(name AbsPath) (fs.FileInfo, error) { fileInfo, err := s.system.Lstat(name) - s.logger.Err(err). - Stringer("name", name). - Msg("Lstat") + chezmoilog.InfoOrError(s.logger, "Lstat", err, + chezmoilog.Stringer("name", name), + ) return fileInfo, err } // Mkdir implements System.Mkdir. func (s *DebugSystem) Mkdir(name AbsPath, perm fs.FileMode) error { err := s.system.Mkdir(name, perm) - s.logger.Err(err). - Stringer("name", name). - Int("perm", int(perm)). - Msg("Mkdir") + chezmoilog.InfoOrError(s.logger, "Mkdir", err, + chezmoilog.Stringer("name", name), + slog.Int("perm", int(perm)), + ) return err } @@ -93,91 +93,94 @@ func (s *DebugSystem) RawPath(path AbsPath) (AbsPath, error) { // ReadDir implements System.ReadDir. func (s *DebugSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { dirEntries, err := s.system.ReadDir(name) - s.logger.Err(err). - Stringer("name", name). - Msg("ReadDir") + chezmoilog.InfoOrError(s.logger, "ReadDir", err, + chezmoilog.Stringer("name", name), + ) return dirEntries, err } // ReadFile implements System.ReadFile. func (s *DebugSystem) ReadFile(name AbsPath) ([]byte, error) { data, err := s.system.ReadFile(name) - s.logger.Err(err). - Stringer("name", name). - Bytes("data", chezmoilog.Output(data, err)). - Int("size", len(data)). - Msg("ReadFile") + chezmoilog.InfoOrError(s.logger, "ReadFile", err, + chezmoilog.Stringer("name", name), + slog.Int("size", len(data)), + chezmoilog.FirstFewBytes("data", data), + ) return data, err } // Readlink implements System.Readlink. func (s *DebugSystem) Readlink(name AbsPath) (string, error) { linkname, err := s.system.Readlink(name) - s.logger.Err(err). - Stringer("name", name). - Str("linkname", linkname). - Msg("Readlink") + chezmoilog.InfoOrError(s.logger, "ReadLink", err, + slog.String("linkname", linkname), + ) return linkname, err } // Remove implements System.Remove. func (s *DebugSystem) Remove(name AbsPath) error { err := s.system.Remove(name) - s.logger.Err(err). - Stringer("name", name). - Msg("Remove") + chezmoilog.InfoOrError(s.logger, "Remove", err, + chezmoilog.Stringer("name", name), + ) return err } // RemoveAll implements System.RemoveAll. func (s *DebugSystem) RemoveAll(name AbsPath) error { err := s.system.RemoveAll(name) - s.logger.Err(err). - Stringer("name", name). - Msg("RemoveAll") + chezmoilog.InfoOrError(s.logger, "RemoveAll", err, + chezmoilog.Stringer("name", name), + ) return err } // Rename implements System.Rename. func (s *DebugSystem) Rename(oldpath, newpath AbsPath) error { err := s.system.Rename(oldpath, newpath) - s.logger.Err(err). - Stringer("oldpath", oldpath). - Stringer("newpath", newpath). - Msg("Rename") + chezmoilog.InfoOrError(s.logger, "RemoveAll", err, + chezmoilog.Stringer("oldpath", oldpath), + chezmoilog.Stringer("newpath", newpath), + ) return err } // RunCmd implements System.RunCmd. func (s *DebugSystem) RunCmd(cmd *exec.Cmd) error { + start := time.Now() err := s.system.RunCmd(cmd) - s.logger.Err(err). - EmbedObject(chezmoilog.OSExecCmdLogObject{Cmd: cmd}). - EmbedObject(chezmoilog.OSExecExitErrorLogObject{Err: err}). - Msg("RunCmd") + attrs := []slog.Attr{ + slog.Any("cmd", chezmoilog.OSExecCmdLogValuer{Cmd: cmd}), + slog.Duration("duration", time.Since(start)), + } + attrs = chezmoilog.AppendExitErrorAttrs(attrs, err) + chezmoilog.InfoOrError(s.logger, "RunCmd", err, attrs...) return err } // RunScript implements System.RunScript. func (s *DebugSystem) RunScript(scriptname RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { err := s.system.RunScript(scriptname, dir, data, options) - s.logger.Err(err). - Stringer("scriptname", scriptname). - Stringer("dir", dir). - Bytes("data", chezmoilog.Output(data, err)). - Object("interpreter", options.Interpreter). - Str("condition", string(options.Condition)). - EmbedObject(chezmoilog.OSExecExitErrorLogObject{Err: err}). - Msg("RunScript") + attrs := []slog.Attr{ + chezmoilog.Stringer("scriptname", scriptname), + chezmoilog.Stringer("dir", dir), + chezmoilog.FirstFewBytes("data", data), + slog.Any("interpreter", options.Interpreter), + slog.String("condition", string(options.Condition)), + } + attrs = chezmoilog.AppendExitErrorAttrs(attrs, err) + chezmoilog.InfoOrError(s.logger, "RunScript", err, attrs...) return err } // Stat implements System.Stat. func (s *DebugSystem) Stat(name AbsPath) (fs.FileInfo, error) { fileInfo, err := s.system.Stat(name) - s.logger.Err(err). - Stringer("name", name). - Msg("Stat") + chezmoilog.InfoOrError(s.logger, "Stat", err, + chezmoilog.Stringer("name", name), + ) return fileInfo, err } @@ -189,21 +192,21 @@ func (s *DebugSystem) UnderlyingFS() vfs.FS { // WriteFile implements System.WriteFile. func (s *DebugSystem) WriteFile(name AbsPath, data []byte, perm fs.FileMode) error { err := s.system.WriteFile(name, data, perm) - s.logger.Err(err). - Stringer("name", name). - Bytes("data", chezmoilog.Output(data, err)). - Int("perm", int(perm)). - Int("size", len(data)). - Msg("WriteFile") + chezmoilog.InfoOrError(s.logger, "WriteFile", err, + chezmoilog.Stringer("name", name), + slog.Int("size", len(data)), + chezmoilog.FirstFewBytes("data", data), + slog.Int("perm", int(perm)), + ) return err } // WriteSymlink implements System.WriteSymlink. func (s *DebugSystem) WriteSymlink(oldname string, newname AbsPath) error { err := s.system.WriteSymlink(oldname, newname) - s.logger.Err(err). - Str("oldname", oldname). - Stringer("newname", newname). - Msg("WriteSymlink") + chezmoilog.InfoOrError(s.logger, "WriteSymlink", err, + slog.String("oldname", oldname), + chezmoilog.Stringer("newname", newname), + ) return err } diff --git a/internal/chezmoi/entrystate.go b/internal/chezmoi/entrystate.go index cf715a41a5f..21aa1e759cb 100644 --- a/internal/chezmoi/entrystate.go +++ b/internal/chezmoi/entrystate.go @@ -3,10 +3,9 @@ package chezmoi import ( "bytes" "io/fs" + "log/slog" "runtime" - "github.com/rs/zerolog" - "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) @@ -60,24 +59,26 @@ func (s *EntryState) Equivalent(other *EntryState) bool { } } -// Overwrite returns true if s should be overwritten by default. -func (s *EntryState) Overwrite() bool { - return s.overwrite -} - -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (s *EntryState) MarshalZerologObject(e *zerolog.Event) { +// LogValue implements log/slog.LogValuer.LogValue. +func (s *EntryState) LogValue() slog.Value { if s == nil { - return + return slog.Value{} + } + attrs := []slog.Attr{ + slog.String("Type", string(s.Type)), + slog.Int("Mode", int(s.Mode)), + chezmoilog.Stringer("ContentsSHA256", s.ContentsSHA256), } - e.Str("Type", string(s.Type)) - e.Int("Mode", int(s.Mode)) - e.Stringer("ContentsSHA256", s.ContentsSHA256) if len(s.contents) != 0 { - e.Bytes("contents", chezmoilog.FirstFewBytes(s.contents)) + attrs = append(attrs, chezmoilog.FirstFewBytes("contents", s.contents)) } if s.overwrite { - e.Bool("overwrite", s.overwrite) + attrs = append(attrs, slog.Bool("overwrite", s.overwrite)) } + return slog.GroupValue(attrs...) +} + +// Overwrite returns true if s should be overwritten by default. +func (s *EntryState) Overwrite() bool { + return s.overwrite } diff --git a/internal/chezmoi/externaldiffsystem.go b/internal/chezmoi/externaldiffsystem.go index f953356776b..bc6123e583a 100644 --- a/internal/chezmoi/externaldiffsystem.go +++ b/internal/chezmoi/externaldiffsystem.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "io/fs" + "log/slog" "os" "os/exec" "strconv" @@ -328,7 +329,7 @@ func (s *ExternalDiffSystem) runDiffCommand(destAbsPath, targetAbsPath AbsPath) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err := chezmoilog.LogCmdRun(cmd) + err := chezmoilog.LogCmdRun(slog.Default(), cmd) // Swallow exit status 1 errors if the files differ as diff commands // traditionally exit with code 1 in this case. diff --git a/internal/chezmoi/gpgencryption.go b/internal/chezmoi/gpgencryption.go index da274d23805..65c97cf45b1 100644 --- a/internal/chezmoi/gpgencryption.go +++ b/internal/chezmoi/gpgencryption.go @@ -1,6 +1,7 @@ package chezmoi import ( + "log/slog" "os" "os/exec" "runtime" @@ -142,7 +143,7 @@ func (e *GPGEncryption) run(args []string) error { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - return chezmoilog.LogCmdRun(cmd) + return chezmoilog.LogCmdRun(slog.Default(), cmd) } // withPrivateTempDir creates a private temporary and calls f. diff --git a/internal/chezmoi/interpreter.go b/internal/chezmoi/interpreter.go index 5116d6abac6..c82ea0efbc0 100644 --- a/internal/chezmoi/interpreter.go +++ b/internal/chezmoi/interpreter.go @@ -1,9 +1,8 @@ package chezmoi import ( + "log/slog" "os/exec" - - "github.com/rs/zerolog" ) // An Interpreter interprets scripts. @@ -25,16 +24,14 @@ func (i *Interpreter) None() bool { return i == nil || i.Command == "" } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (i *Interpreter) MarshalZerologObject(event *zerolog.Event) { - if i == nil { - return - } +// LogValue implements log/slog.LogValuer.LogValue. +func (i *Interpreter) LogValue() slog.Value { + var attrs []slog.Attr if i.Command != "" { - event.Str("command", i.Command) + attrs = append(attrs, slog.String("command", i.Command)) } if i.Args != nil { - event.Strs("args", i.Args) + attrs = append(attrs, slog.Any("args", i.Args)) } + return slog.GroupValue(attrs...) } diff --git a/internal/chezmoi/patternset.go b/internal/chezmoi/patternset.go index 9144562c652..04afc26bcea 100644 --- a/internal/chezmoi/patternset.go +++ b/internal/chezmoi/patternset.go @@ -2,13 +2,13 @@ package chezmoi import ( "fmt" + "log/slog" "path" "path/filepath" "sort" "github.com/bmatcuk/doublestar/v4" - "github.com/rs/zerolog" - vfs "github.com/twpayne/go-vfs/v5" + "github.com/twpayne/go-vfs/v5" ) type patternSetIncludeType bool @@ -40,14 +40,15 @@ func newPatternSet() *patternSet { } } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (ps *patternSet) MarshalZerologObject(e *zerolog.Event) { +// LogValue implements log/slog.LogValuer.LogValue. +func (ps *patternSet) LogValue() slog.Value { if ps == nil { - return + return slog.Value{} } - e.Strs("includePatterns", ps.includePatterns.elements()) - e.Strs("excludePatterns", ps.excludePatterns.elements()) + return slog.GroupValue( + slog.Any("includePatterns", ps.includePatterns.elements()), + slog.Any("excludePatterns", ps.excludePatterns.elements()), + ) } // add adds a pattern to ps. diff --git a/internal/chezmoi/realsystem.go b/internal/chezmoi/realsystem.go index 51cdcd0726b..7d6969b4747 100644 --- a/internal/chezmoi/realsystem.go +++ b/internal/chezmoi/realsystem.go @@ -3,6 +3,7 @@ package chezmoi import ( "errors" "io/fs" + "log/slog" "os" "os/exec" "path/filepath" @@ -79,7 +80,7 @@ func (s *RealSystem) Rename(oldpath, newpath AbsPath) error { // RunCmd implements System.RunCmd. func (s *RealSystem) RunCmd(cmd *exec.Cmd) error { - return chezmoilog.LogCmdRun(cmd) + return chezmoilog.LogCmdRun(slog.Default(), cmd) } // RunScript implements System.RunScript. diff --git a/internal/chezmoi/sourcestate.go b/internal/chezmoi/sourcestate.go index b88941f2749..552cbd7c2bc 100644 --- a/internal/chezmoi/sourcestate.go +++ b/internal/chezmoi/sourcestate.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/http" "net/url" "os" @@ -29,8 +30,6 @@ import ( "github.com/coreos/go-semver/semver" "github.com/mitchellh/copystructure" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" "github.com/twpayne/chezmoi/v2/internal/chezmoierrors" "github.com/twpayne/chezmoi/v2/internal/chezmoilog" @@ -123,7 +122,7 @@ type SourceState struct { remove *patternSet interpreters map[string]*Interpreter httpClient *http.Client - logger *zerolog.Logger + logger *slog.Logger version semver.Version mode Mode defaultTemplateDataFunc func() map[string]any @@ -194,7 +193,7 @@ func WithInterpreters(interpreters map[string]*Interpreter) SourceStateOption { } // WithLogger sets the logger. -func WithLogger(logger *zerolog.Logger) SourceStateOption { +func WithLogger(logger *slog.Logger) SourceStateOption { return func(s *SourceState) { s.logger = logger } @@ -290,7 +289,7 @@ func NewSourceState(options ...SourceStateOption) *SourceState { ignore: newPatternSet(), remove: newPatternSet(), httpClient: http.DefaultClient, - logger: &log.Logger, + logger: slog.Default(), readTemplateData: true, readTemplates: true, priorityTemplateData: make(map[string]any), @@ -1644,7 +1643,7 @@ func (s *SourceState) getExternalData( cmd := exec.Command(external.Filter.Command, external.Filter.Args...) //nolint:gosec cmd.Stdin = bytes.NewReader(data) cmd.Stderr = os.Stderr - data, err = chezmoilog.LogCmdOutput(cmd) + data, err = chezmoilog.LogCmdOutput(s.logger, cmd) if err != nil { return nil, fmt.Errorf("%s: %s: %w", externalRelPath, external.URL, err) } @@ -1860,7 +1859,7 @@ func (s *SourceState) newModifyTargetStateEntryFunc( ) cmd.Stdin = bytes.NewReader(currentContents) cmd.Stderr = os.Stderr - contents, err = chezmoilog.LogCmdOutput(cmd) + contents, err = chezmoilog.LogCmdOutput(s.logger, cmd) return } return &TargetStateFile{ diff --git a/internal/chezmoi/sourcestateentry.go b/internal/chezmoi/sourcestateentry.go index 60af2a235b3..7de3b54c4bc 100644 --- a/internal/chezmoi/sourcestateentry.go +++ b/internal/chezmoi/sourcestateentry.go @@ -2,8 +2,7 @@ package chezmoi import ( "encoding/hex" - - "github.com/rs/zerolog" + "log/slog" "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) @@ -27,7 +26,7 @@ type SourceStateOriginAbsPath AbsPath // A SourceStateEntry represents the state of an entry in the source state. type SourceStateEntry interface { - zerolog.LogObjectMarshaler + slog.LogValuer Evaluate() error Order() ScriptOrder Origin() SourceStateOrigin @@ -92,11 +91,12 @@ func (s *SourceStateCommand) Evaluate() error { return nil } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (s *SourceStateCommand) MarshalZerologObject(e *zerolog.Event) { - e.EmbedObject(chezmoilog.OSExecCmdLogObject{Cmd: s.cmd.Command()}) - e.Str("origin", s.origin.OriginString()) +// LogValue implements log/slog.LogValuer.LogValue. +func (s *SourceStateCommand) LogValue() slog.Value { + return slog.GroupValue( + slog.Any("cmd", chezmoilog.OSExecCmdLogValuer{Cmd: s.cmd.Command()}), + slog.String("origin", s.origin.OriginString()), + ) } // Order returns s's order. @@ -129,11 +129,12 @@ func (s *SourceStateDir) Evaluate() error { return nil } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (s *SourceStateDir) MarshalZerologObject(e *zerolog.Event) { - e.Stringer("sourceRelPath", s.sourceRelPath) - e.Object("attr", s.Attr) +// LogValue implements log/slog.LogValuer.LogValue. +func (s *SourceStateDir) LogValue() slog.Value { + return slog.GroupValue( + chezmoilog.Stringer("sourceRelPath", s.sourceRelPath), + slog.Any("attr", s.Attr), + ) } // Order returns s's order. @@ -162,22 +163,23 @@ func (s *SourceStateFile) Evaluate() error { return err } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (s *SourceStateFile) MarshalZerologObject(e *zerolog.Event) { - e.Stringer("sourceRelPath", s.sourceRelPath) - e.Interface("attr", s.Attr) +// LogValue implements log/slog.LogValuer.LogValue. +func (s *SourceStateFile) LogValue() slog.Value { + attrs := []slog.Attr{ + chezmoilog.Stringer("sourceRelPath", s.sourceRelPath), + slog.Any("attr", s.Attr), + } contents, contentsErr := s.Contents() - e.Bytes("contents", chezmoilog.FirstFewBytes(contents)) + attrs = append(attrs, chezmoilog.FirstFewBytes("contents", contents)) if contentsErr != nil { - e.Str("contentsErr", contentsErr.Error()) + attrs = append(attrs, slog.Any("contentsErr", contentsErr)) } - e.Err(contentsErr) contentsSHA256, contentsSHA256Err := s.ContentsSHA256() - e.Str("contentsSHA256", hex.EncodeToString(contentsSHA256)) + attrs = append(attrs, slog.String("contentsSHA256", hex.EncodeToString(contentsSHA256))) if contentsSHA256Err != nil { - e.Str("contentsSHA256Err", contentsSHA256Err.Error()) + attrs = append(attrs, slog.Any("contentsSHA256Err", contentsSHA256Err)) } + return slog.GroupValue(attrs...) } // Order returns s's order. @@ -209,9 +211,9 @@ func (s *SourceStateImplicitDir) Evaluate() error { return nil } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (s *SourceStateImplicitDir) MarshalZerologObject(e *zerolog.Event) { +// LogValue implements log/slog.LogValuer.LogValue. +func (s *SourceStateImplicitDir) LogValue() slog.Value { + return slog.GroupValue() } // Order returns s's order. @@ -239,9 +241,11 @@ func (s *SourceStateRemove) Evaluate() error { return nil } -// MarshalZerologObject implements zerolog.LogObjectMarshaler. -func (s *SourceStateRemove) MarshalZerologObject(e *zerolog.Event) { - e.Stringer("targetRelPath", s.targetRelPath) +// LogValue implements log/slog.LogValuer.LogValue. +func (s *SourceStateRemove) LogValue() slog.Value { + return slog.GroupValue( + chezmoilog.Stringer("targetRelPath", s.targetRelPath), + ) } // Order returns s's order. diff --git a/internal/chezmoilog/chezmoilog.go b/internal/chezmoilog/chezmoilog.go index 8bdd2bce623..4c4b56e1132 100644 --- a/internal/chezmoilog/chezmoilog.go +++ b/internal/chezmoilog/chezmoilog.go @@ -2,196 +2,239 @@ package chezmoilog import ( + "context" "errors" + "fmt" + "log/slog" "net/http" "os" "os/exec" "slices" "time" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" ) const few = 64 -// An OSExecCmdLogObject wraps an *os/exec.Cmd and adds -// github.com/rs/zerolog.LogObjectMarshaler functionality. -type OSExecCmdLogObject struct { +// An OSExecCmdLogValuer wraps an *os/exec.Cmd and adds log/slog.LogValuer +// functionality. +type OSExecCmdLogValuer struct { *exec.Cmd } -// An OSExecExitErrorLogObject wraps an error and adds -// github.com/rs/zerolog.LogObjectMarshaler functionality if the wrapped error -// is an os/exec.ExitError. -type OSExecExitErrorLogObject struct { - Err error +// An OSExecExitErrorLogValuer wraps an *os/exec.ExitError and adds +// log/slog.LogValuer. +type OSExecExitErrorLogValuer struct { + *exec.ExitError } -// An OSProcessStateLogObject wraps an *os.ProcessState and adds -// github.com/rs/zerolog.LogObjectMarshaler functionality. -type OSProcessStateLogObject struct { +// An OSProcessStateLogValuer wraps an *os.ProcessState and adds +// log/slog.LogValuer functionality. +type OSProcessStateLogValuer struct { *os.ProcessState } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (cmd OSExecCmdLogObject) MarshalZerologObject(event *zerolog.Event) { - if cmd.Cmd == nil { - return - } +// LogValuer implements log/slog.LogValuer.LogValue. +func (cmd OSExecCmdLogValuer) LogValuer() slog.Value { + var attrs []slog.Attr if cmd.Path != "" { - event.Str("path", cmd.Path) + attrs = append(attrs, slog.String("path", cmd.Path)) } - if cmd.Args != nil { - event.Strs("args", cmd.Args) + if len(cmd.Args) != 0 { + attrs = append(attrs, slog.Any("args", cmd.Args)) } if cmd.Dir != "" { - event.Str("dir", cmd.Dir) + attrs = append(attrs, slog.String("dir", cmd.Dir)) } - if cmd.Env != nil { - event.Strs("env", cmd.Env) + if len(cmd.Env) != 0 { + attrs = append(attrs, slog.Any("env", cmd.Env)) } + return slog.GroupValue(attrs...) } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (err OSExecExitErrorLogObject) MarshalZerologObject(event *zerolog.Event) { - if err.Err == nil { - return +// LogValuer implements log/slog.LogValuer.LogValue. +func (err OSExecExitErrorLogValuer) LogValuer() slog.Value { + attrs := []slog.Attr{ + slog.Any("processState", OSProcessStateLogValuer{err.ExitError.ProcessState}), } - if osExecExitError := (&exec.ExitError{}); errors.As(err.Err, &osExecExitError) { - event.EmbedObject(OSProcessStateLogObject{osExecExitError.ProcessState}) - if osExecExitError.Stderr != nil { - event.Bytes("stderr", osExecExitError.Stderr) - return - } + if osExecExitError := (&exec.ExitError{}); errors.As(err, &osExecExitError) { + attrs = append(attrs, Bytes("stderr", err.ExitError.Stderr)) } + return slog.GroupValue(attrs...) } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (p OSProcessStateLogObject) MarshalZerologObject(event *zerolog.Event) { - if p.ProcessState == nil { - return - } - if p.Exited() { - if !p.Success() { - event.Int("exitCode", p.ExitCode()) +// LogValue implements log/slog.LogValuer.LogValue. +func (p OSProcessStateLogValuer) LogValue() slog.Value { + var attrs []slog.Attr + if p.ProcessState != nil { + if p.Exited() { + if !p.Success() { + attrs = append(attrs, slog.Int("exitCode", p.ExitCode())) + } + } else { + attrs = append(attrs, slog.Int("pid", p.Pid())) + } + if userTime := p.UserTime(); userTime != 0 { + attrs = append(attrs, slog.Duration("userTime", userTime)) + } + if systemTime := p.SystemTime(); systemTime != 0 { + attrs = append(attrs, slog.Duration("systemTime", systemTime)) } - } else { - event.Int("pid", p.Pid()) } - if userTime := p.UserTime(); userTime != 0 { - event.Dur("userTime", userTime) + return slog.GroupValue(attrs...) +} + +func AppendExitErrorAttrs(attrs []slog.Attr, err error) []slog.Attr { + var execExitError *exec.ExitError + if !errors.As(err, &execExitError) { + return append(attrs, slog.Any("err", err)) } - if systemTime := p.SystemTime(); systemTime != 0 { - event.Dur("systemTime", systemTime) + + if execExitError.ProcessState != nil { + if execExitError.Exited() { + attrs = append(attrs, slog.Int("exitCode", execExitError.ExitCode())) + } else { + attrs = append(attrs, slog.Int("pid", execExitError.Pid())) + } + if userTime := execExitError.UserTime(); userTime != 0 { + attrs = append(attrs, slog.Duration("userTime", userTime)) + } + if systemTime := execExitError.SystemTime(); systemTime != 0 { + attrs = append(attrs, slog.Duration("systemTime", systemTime)) + } } + + return attrs } -// FirstFewBytes returns the first few bytes of data in a human-readable form. -func FirstFewBytes(data []byte) []byte { - if len(data) > few { - data = slices.Clone(data[:few]) - data = append(data, '.', '.', '.') - } - return data +// Bytes returns an slog.Attr with the value data. +func Bytes(key string, data []byte) slog.Attr { + return slog.String(key, string(data)) +} + +// FirstFewBytes returns an slog.Attr with the value of the first few bytes of +// data. +func FirstFewBytes(key string, data []byte) slog.Attr { + return slog.String(key, string(firstFewBytes(data))) } // LogHTTPRequest calls httpClient.Do, logs the result to logger, and returns // the result. -func LogHTTPRequest(logger *zerolog.Logger, client *http.Client, req *http.Request) (*http.Response, error) { +func LogHTTPRequest(logger *slog.Logger, client *http.Client, req *http.Request) (*http.Response, error) { start := time.Now() resp, err := client.Do(req) + attrs := []slog.Attr{ + slog.Duration("duration", time.Since(start)), + slog.String("method", req.Method), + Stringer("url", req.URL), + } if resp != nil { - logger.Err(err). - Stringer("duration", time.Since(start)). - Str("method", req.Method). - Int64("size", resp.ContentLength). - Int("statusCode", resp.StatusCode). - Str("status", resp.Status). - Stringer("url", req.URL). - Msg("HTTPRequest") - } else { - logger.Err(err). - Stringer("duration", time.Since(start)). - Str("method", req.Method). - Stringer("url", req.URL). - Msg("HTTPRequest") + attrs = append(attrs, + slog.Int("statusCode", resp.StatusCode), + slog.String("status", resp.Status), + slog.Int("contentLength", int(resp.ContentLength)), + ) } + InfoOrError(logger, "HTTPRequest", err, attrs...) return resp, err } // LogCmdCombinedOutput calls cmd.CombinedOutput, logs the result, and returns the result. -func LogCmdCombinedOutput(cmd *exec.Cmd) ([]byte, error) { +func LogCmdCombinedOutput(logger *slog.Logger, cmd *exec.Cmd) ([]byte, error) { start := time.Now() combinedOutput, err := cmd.CombinedOutput() - log.Err(err). - EmbedObject(OSExecCmdLogObject{Cmd: cmd}). - EmbedObject(OSExecExitErrorLogObject{Err: err}). - Bytes("combinedOutput", Output(combinedOutput, err)). - Stringer("duration", time.Since(start)). - Int("size", len(combinedOutput)). - Msg("CombinedOutput") + attrs := []slog.Attr{ + slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), + slog.Duration("duration", time.Since(start)), + slog.Int("size", len(combinedOutput)), + slog.Any("combinedOutput", firstFewBytes(combinedOutput)), + } + attrs = AppendExitErrorAttrs(attrs, err) + InfoOrError(logger, "Output", err, attrs...) return combinedOutput, err } // LogCmdOutput calls cmd.Output, logs the result, and returns the result. -func LogCmdOutput(cmd *exec.Cmd) ([]byte, error) { +func LogCmdOutput(logger *slog.Logger, cmd *exec.Cmd) ([]byte, error) { start := time.Now() output, err := cmd.Output() - log.Err(err). - EmbedObject(OSExecCmdLogObject{Cmd: cmd}). - EmbedObject(OSExecExitErrorLogObject{Err: err}). - Stringer("duration", time.Since(start)). - Bytes("output", Output(output, err)). - Int("size", len(output)). - Msg("Output") + attrs := []slog.Attr{ + slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), + slog.Duration("duration", time.Since(start)), + slog.Int("size", len(output)), + slog.Any("output", firstFewBytes(output)), + } + attrs = AppendExitErrorAttrs(attrs, err) + InfoOrError(logger, "Output", err, attrs...) return output, err } // LogCmdRun calls cmd.Run, logs the result, and returns the result. -func LogCmdRun(cmd *exec.Cmd) error { +func LogCmdRun(logger *slog.Logger, cmd *exec.Cmd) error { start := time.Now() err := cmd.Run() - log.Err(err). - EmbedObject(OSExecCmdLogObject{Cmd: cmd}). - EmbedObject(OSExecExitErrorLogObject{Err: err}). - Stringer("duration", time.Since(start)). - Msg("Run") + attrs := []slog.Attr{ + slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), + slog.Duration("duration", time.Since(start)), + } + attrs = AppendExitErrorAttrs(attrs, err) + InfoOrError(logger, "Run", err, attrs...) return err } // LogCmdStart calls cmd.Start, logs the result, and returns the result. -func LogCmdStart(cmd *exec.Cmd) error { +func LogCmdStart(logger *slog.Logger, cmd *exec.Cmd) error { start := time.Now() err := cmd.Start() - log.Err(err). - EmbedObject(OSExecCmdLogObject{Cmd: cmd}). - EmbedObject(OSExecExitErrorLogObject{Err: err}). - Time("start", start). - Msg("Start") + attrs := []slog.Attr{ + slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), + slog.Time("start", start), + } + attrs = AppendExitErrorAttrs(attrs, err) + InfoOrError(logger, "Start", err, attrs...) return err } // LogCmdWait calls cmd.Wait, logs the result, and returns the result. -func LogCmdWait(cmd *exec.Cmd) error { +func LogCmdWait(logger *slog.Logger, cmd *exec.Cmd) error { err := cmd.Wait() end := time.Now() - log.Err(err). - EmbedObject(OSExecCmdLogObject{Cmd: cmd}). - EmbedObject(OSExecExitErrorLogObject{Err: err}). - Time("end", end). - Msg("Wait") + attrs := []slog.Attr{ + slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), + slog.Time("end", end), + } + attrs = AppendExitErrorAttrs(attrs, err) + InfoOrError(logger, "Wait", err, attrs...) return err } -// Output returns the first few bytes of output if err is nil, otherwise it -// returns the full output. -func Output(data []byte, err error) []byte { +func InfoOrError(logger *slog.Logger, msg string, err error, attrs ...slog.Attr) { + if logger == nil { + return + } + args := make([]any, 0, len(attrs)+1) if err != nil { - return data + args = append(args, slog.Any("err", err)) } - return FirstFewBytes(data) + for _, attr := range attrs { + args = append(args, attr) + } + level := slog.LevelInfo + if err != nil { + level = slog.LevelError + } + logger.Log(context.TODO(), level, msg, args...) +} + +// Stringer returns an slog.Attr with value. +func Stringer(key string, value fmt.Stringer) slog.Attr { + return slog.String(key, value.String()) +} + +// firstFewBytes returns the first few bytes of data. +func firstFewBytes(data []byte) []byte { + if len(data) > few { + data = slices.Clone(data[:few]) + data = append(data, '.', '.', '.') + } + return data } diff --git a/internal/chezmoilog/chezmoilog_test.go b/internal/chezmoilog/chezmoilog_test.go deleted file mode 100644 index 6c1dc24dede..00000000000 --- a/internal/chezmoilog/chezmoilog_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package chezmoilog - -import ( - "errors" - "strconv" - "testing" - - "github.com/alecthomas/assert/v2" -) - -func TestOutput(t *testing.T) { - nonNilError := errors.New("") - for i, tc := range []struct { - data []byte - err error - expected []byte - }{ - { - data: nil, - err: nil, - expected: nil, - }, - { - data: newByteSlice(0), - err: nil, - expected: newByteSlice(0), - }, - { - data: newByteSlice(16), - err: nil, - expected: newByteSlice(16), - }, - { - data: newByteSlice(2 * few), - err: nil, - expected: append(newByteSlice(few), []byte("...")...), - }, - { - data: newByteSlice(0), - err: nonNilError, - expected: newByteSlice(0), - }, - { - data: newByteSlice(few), - err: nonNilError, - expected: newByteSlice(few), - }, - { - data: newByteSlice(2 * few), - err: nonNilError, - expected: newByteSlice(2 * few), - }, - } { - t.Run(strconv.Itoa(i), func(t *testing.T) { - assert.Equal(t, tc.expected, Output(tc.data, tc.err)) - }) - } -} - -func newByteSlice(n int) []byte { - s := make([]byte, 0, n) - for i := 0; i < n; i++ { - s = append(s, byte(i)) - } - return s -} diff --git a/internal/chezmoilog/nullhandler.go b/internal/chezmoilog/nullhandler.go new file mode 100644 index 00000000000..4fd9f09288a --- /dev/null +++ b/internal/chezmoilog/nullhandler.go @@ -0,0 +1,14 @@ +package chezmoilog + +import ( + "context" + "log/slog" +) + +// A NullHandler implements log/slog.Handler and drops all output. +type NullHandler struct{} + +func (NullHandler) Enabled(context.Context, slog.Level) bool { return false } +func (NullHandler) Handle(context.Context, slog.Record) error { return nil } +func (h NullHandler) WithAttrs([]slog.Attr) slog.Handler { return h } +func (h NullHandler) WithGroup(string) slog.Handler { return h } diff --git a/internal/chezmoilog/nullhandler_test.go b/internal/chezmoilog/nullhandler_test.go new file mode 100644 index 00000000000..703f1555537 --- /dev/null +++ b/internal/chezmoilog/nullhandler_test.go @@ -0,0 +1,5 @@ +package chezmoilog + +import "log/slog" + +var _ slog.Handler = NullHandler{} diff --git a/internal/chezmoitest/chezmoitest.go b/internal/chezmoitest/chezmoitest.go index e84fecce29b..a4936e6f2f2 100644 --- a/internal/chezmoitest/chezmoitest.go +++ b/internal/chezmoitest/chezmoitest.go @@ -4,6 +4,7 @@ package chezmoitest import ( "fmt" "io/fs" + "log/slog" "os" "os/exec" "regexp" @@ -25,7 +26,7 @@ var ageRecipientRx = regexp.MustCompile(`(?m)^Public key: ([0-9a-z]+)\s*$`) // recipient. func AgeGenerateKey(command, identityFile string) (string, error) { cmd := exec.Command(command+"-keygen", "--output", identityFile) //nolint:gosec - output, err := chezmoilog.LogCmdCombinedOutput(cmd) + output, err := chezmoilog.LogCmdCombinedOutput(slog.Default(), cmd) if err != nil { return "", err } @@ -52,7 +53,7 @@ func GPGGenerateKey(command, homeDir string) (key, passphrase string, err error) ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err = chezmoilog.LogCmdRun(cmd) + err = chezmoilog.LogCmdRun(slog.Default(), cmd) return } diff --git a/internal/cmd/bitwardensecretstemplatefuncs.go b/internal/cmd/bitwardensecretstemplatefuncs.go index 36af71e0d37..8450d8d79f9 100644 --- a/internal/cmd/bitwardensecretstemplatefuncs.go +++ b/internal/cmd/bitwardensecretstemplatefuncs.go @@ -46,7 +46,7 @@ func (c *Config) bitwardenSecretsOutput(args []string) ([]byte, error) { cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/bitwardentemplatefuncs.go b/internal/cmd/bitwardentemplatefuncs.go index 990dd2e523e..6548f5b2047 100644 --- a/internal/cmd/bitwardentemplatefuncs.go +++ b/internal/cmd/bitwardentemplatefuncs.go @@ -78,7 +78,7 @@ func (c *Config) bitwardenOutput(args []string) ([]byte, error) { cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index e7d952d39fc..b66b403c3e9 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -4,6 +4,7 @@ package cmd import ( "errors" "fmt" + "log/slog" "os" "os/exec" "regexp" @@ -12,7 +13,6 @@ import ( "strings" "github.com/charmbracelet/glamour" - "github.com/rs/zerolog" "github.com/spf13/cobra" "go.etcd.io/bbolt" @@ -97,13 +97,13 @@ func init() { } } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -func (v VersionInfo) MarshalZerologObject(e *zerolog.Event) { - e.Str("version", v.Version) - e.Str("commit", v.Commit) - e.Str("date", v.Date) - e.Str("builtBy", v.BuiltBy) +func (v VersionInfo) LogValue() slog.Value { + return slog.GroupValue( + slog.String("version", v.Version), + slog.String("commit", v.Commit), + slog.String("date", v.Date), + slog.String("builtBy", v.BuiltBy), + ) } // Main runs chezmoi and returns an exit code. diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 7a2374ef3a6..d5e7a486ae3 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/fs" + "log/slog" "maps" "net/http" "os" @@ -35,8 +36,6 @@ import ( "github.com/gregjones/httpcache/diskcache" "github.com/mitchellh/mapstructure" "github.com/muesli/termenv" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/twpayne/go-shell" @@ -225,7 +224,7 @@ type Config struct { destSystem chezmoi.System persistentState chezmoi.PersistentState httpClient *http.Client - logger *zerolog.Logger + logger *slog.Logger // Computed configuration. commandDirAbsPath chezmoi.AbsPath @@ -322,7 +321,7 @@ func newConfig(options ...configOption) (*Config, error) { return nil, err } - logger := zerolog.Nop() + logger := slog.Default() c := &Config{ ConfigFile: newConfigFile(bds), @@ -379,7 +378,7 @@ func newConfig(options ...configOption) (*Config, error) { // Configuration. fileSystem: vfs.OSFS, bds: bds, - logger: &logger, + logger: logger, // Computed configuration. homeDirAbsPath: homeDirAbsPath, @@ -524,9 +523,9 @@ func (c *Config) Close() error { errs := make([]error, 0, len(c.tempDirs)) for _, tempDirAbsPath := range c.tempDirs { err := os.RemoveAll(tempDirAbsPath.String()) - c.logger.Err(err). - Stringer("tempDir", tempDirAbsPath). - Msg("RemoveAll") + chezmoilog.InfoOrError(c.logger, "RemoveAll", err, + chezmoilog.Stringer("tempDir", tempDirAbsPath), + ) errs = append(errs, err) } pprof.StopCPUProfile() @@ -705,7 +704,7 @@ func (c *Config) cmdOutput(dirAbsPath chezmoi.AbsPath, name string, args []strin } cmd.Dir = dirRawAbsPath.String() } - return chezmoilog.LogCmdOutput(cmd) + return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // colorAutoFunc detects whether color should be used. @@ -952,12 +951,12 @@ func (c *Config) defaultPreApplyFunc( targetRelPath chezmoi.RelPath, targetEntryState, lastWrittenEntryState, actualEntryState *chezmoi.EntryState, ) error { - c.logger.Info(). - Stringer("targetRelPath", targetRelPath). - Object("targetEntryState", targetEntryState). - Object("lastWrittenEntryState", lastWrittenEntryState). - Object("actualEntryState", actualEntryState). - Msg("defaultPreApplyFunc") + c.logger.Info("defaultPreApplyFunc", + chezmoilog.Stringer("targetRelPath", targetRelPath), + slog.Any("targetEntryState", targetEntryState), + slog.Any("lastWrittenEntryState", lastWrittenEntryState), + slog.Any("actualEntryState", actualEntryState), + ) switch { case c.force: @@ -1691,7 +1690,7 @@ func (c *Config) newSourceState( return nil, err } - sourceStateLogger := c.logger.With().Str(logComponentKey, logComponentValueSourceState).Logger() + sourceStateLogger := c.logger.With(logComponentKey, logComponentValueSourceState) c.SourceDirAbsPath, err = c.getSourceDirAbsPath(nil) if err != nil { @@ -1712,7 +1711,7 @@ func (c *Config) newSourceState( chezmoi.WithEncryption(c.encryption), chezmoi.WithHTTPClient(httpClient), chezmoi.WithInterpreters(c.Interpreters), - chezmoi.WithLogger(&sourceStateLogger), + chezmoi.WithLogger(sourceStateLogger), chezmoi.WithMode(c.Mode), chezmoi.WithPriorityTemplateData(c.Data), chezmoi.WithSourceDir(c.SourceDirAbsPath), @@ -1756,7 +1755,7 @@ func (c *Config) persistentPostRunRootE(cmd *cobra.Command, args []string) error return err } if c.diffPagerCmd.Process != nil { - if err := chezmoilog.LogCmdWait(c.diffPagerCmd); err != nil { + if err := chezmoilog.LogCmdWait(c.logger, c.diffPagerCmd); err != nil { return err } } @@ -1828,7 +1827,7 @@ func (c *Config) pageDiffOutput(output string) error { return c.writeOutputString(output) default: pagerCmd.Stdin = bytes.NewBufferString(output) - return chezmoilog.LogCmdRun(pagerCmd) + return chezmoilog.LogCmdRun(c.logger, pagerCmd) } } @@ -1905,34 +1904,29 @@ func (c *Config) persistentPreRunRootE(cmd *cobra.Command, args []string) error } // Configure the logger. - log.Logger = log.Output(zerolog.NewConsoleWriter( - func(w *zerolog.ConsoleWriter) { - w.Out = c.stderr - w.NoColor = !c.Color.Value(c.colorAutoFunc) - w.TimeFormat = time.RFC3339 - }, - )) + var handler slog.Handler if c.debug { - zerolog.SetGlobalLevel(zerolog.InfoLevel) + handler = slog.NewTextHandler(c.stderr, nil) } else { - zerolog.SetGlobalLevel(zerolog.Disabled) + handler = chezmoilog.NullHandler{} } - c.logger = &log.Logger + c.logger = slog.New(handler) + slog.SetDefault(c.logger) // Log basic information. - c.logger.Info(). - Object("version", c.versionInfo). - Strs("args", os.Args). - Str("goVersion", runtime.Version()). - Msg("persistentPreRunRootE") + c.logger.Info("persistentPreRunRootE", + slog.Any("version", c.versionInfo), + slog.Any("args", os.Args), + slog.String("goVersion", runtime.Version()), + ) realSystem := chezmoi.NewRealSystem(c.fileSystem, chezmoi.RealSystemWithSafe(c.Safe), chezmoi.RealSystemWithScriptTempDir(c.ScriptTempDir), ) c.baseSystem = realSystem if c.debug { - systemLogger := c.logger.With().Str(logComponentKey, logComponentValueSystem).Logger() - c.baseSystem = chezmoi.NewDebugSystem(c.baseSystem, &systemLogger) + systemLogger := c.logger.With(slog.String(logComponentKey, logComponentValueSystem)) + c.baseSystem = chezmoi.NewDebugSystem(c.baseSystem, systemLogger) } // Set up the persistent state. @@ -1992,10 +1986,8 @@ func (c *Config) persistentPreRunRootE(cmd *cobra.Command, args []string) error c.persistentState = chezmoi.NullPersistentState{} } if c.debug && c.persistentState != nil { - persistentStateLogger := c.logger.With(). - Str(logComponentKey, logComponentValuePersistentState). - Logger() - c.persistentState = chezmoi.NewDebugPersistentState(c.persistentState, &persistentStateLogger) + persistentStateLogger := c.logger.With(slog.String(logComponentKey, logComponentValuePersistentState)) + c.persistentState = chezmoi.NewDebugPersistentState(c.persistentState, persistentStateLogger) } // Set up the source and destination systems. @@ -2025,7 +2017,7 @@ func (c *Config) persistentPreRunRootE(cmd *cobra.Command, args []string) error pipeReader, pipeWriter := io.Pipe() pagerCmd.Stdin = pipeReader lazyWriter := newLazyWriter(func() (io.WriteCloser, error) { - if err := chezmoilog.LogCmdStart(pagerCmd); err != nil { + if err := chezmoilog.LogCmdStart(c.logger, pagerCmd); err != nil { return nil, err } return pipeWriter, nil @@ -2232,39 +2224,27 @@ func (c *Config) newTemplateData(cmd *cobra.Command) *templateData { if rawGroup, err := user.LookupGroupId(currentUser.Gid); err == nil { group = rawGroup.Name } else { - c.logger.Info(). - Str("gid", currentUser.Gid). - Err(err). - Msg("user.LookupGroupId") + c.logger.Info("user.LookupGroupId", slog.Any("err", err), slog.String("gid", currentUser.Gid)) } } } else { - c.logger.Info(). - Err(err). - Msg("user.Current") + c.logger.Error("user.Current", slog.Any("err", err)) var ok bool username, ok = os.LookupEnv("USER") if !ok { - c.logger.Info(). - Str("key", "USER"). - Bool("ok", ok). - Msg("os.LookupEnv") + c.logger.Info("os.LookupEnv", slog.String("key", "USER"), slog.Bool("ok", ok)) } } fqdnHostname, err := chezmoi.FQDNHostname(c.fileSystem) if err != nil { - c.logger.Info(). - Err(err). - Msg("chezmoi.FQDNHostname") + c.logger.Info("chezmoi.FQDNHostname", slog.Any("err", err)) } hostname, _, _ := strings.Cut(fqdnHostname, ".") kernel, err := chezmoi.Kernel(c.fileSystem) if err != nil { - c.logger.Info(). - Err(err). - Msg("chezmoi.Kernel") + c.logger.Info("chezmoi.Kernel", slog.Any("err", err)) } var osRelease map[string]any @@ -2276,9 +2256,7 @@ func (c *Config) newTemplateData(cmd *cobra.Command) *templateData { if rawOSRelease, err := chezmoi.OSRelease(c.fileSystem); err == nil { osRelease = upperSnakeCaseToCamelCaseMap(rawOSRelease) } else { - c.logger.Info(). - Err(err). - Msg("chezmoi.OSRelease") + c.logger.Info("chezmoi.OSRelease", slog.Any("err", err)) } } @@ -2417,10 +2395,8 @@ func (c *Config) setEncryption() error { } if c.debug { - encryptionLogger := c.logger.With(). - Str(logComponentKey, logComponentValueEncryption). - Logger() - c.encryption = chezmoi.NewDebugEncryption(c.encryption, &encryptionLogger) + encryptionLogger := c.logger.With(logComponentKey, logComponentValueEncryption) + c.encryption = chezmoi.NewDebugEncryption(c.encryption, encryptionLogger) } return nil @@ -2614,9 +2590,7 @@ func (c *Config) tempDir(key string) (chezmoi.AbsPath, error) { return tempDirAbsPath, nil } tempDir, err := os.MkdirTemp("", key) - c.logger.Err(err). - Str("tempDir", tempDir). - Msg("MkdirTemp") + chezmoilog.InfoOrError(c.logger, "MkirTemp", err, slog.String("tempDir", tempDir)) if err != nil { return chezmoi.EmptyAbsPath, err } diff --git a/internal/cmd/dashlanetemplatefuncs.go b/internal/cmd/dashlanetemplatefuncs.go index 8358c130365..52e618023cf 100644 --- a/internal/cmd/dashlanetemplatefuncs.go +++ b/internal/cmd/dashlanetemplatefuncs.go @@ -64,7 +64,7 @@ func (c *Config) dashlaneOutput(args ...string) ([]byte, error) { args = append(slices.Clone(c.Dashlane.Args), args...) cmd := exec.Command(name, args...) cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, err } diff --git a/internal/cmd/doctorcmd.go b/internal/cmd/doctorcmd.go index 88994669200..229b09f9285 100644 --- a/internal/cmd/doctorcmd.go +++ b/internal/cmd/doctorcmd.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "net/http" "os" "os/exec" @@ -480,7 +481,7 @@ func (c *binaryCheck) Run(system chezmoi.System, homeDirAbsPath chezmoi.AbsPath) } cmd := exec.Command(pathAbsPath.String(), c.versionArgs...) //nolint:gosec - output, err := chezmoilog.LogCmdCombinedOutput(cmd) + output, err := chezmoilog.LogCmdCombinedOutput(slog.Default(), cmd) if err != nil { return checkResultFailed, err.Error() } diff --git a/internal/cmd/doctorcmd_unix.go b/internal/cmd/doctorcmd_unix.go index 38170ade7b0..79765bcc2bc 100644 --- a/internal/cmd/doctorcmd_unix.go +++ b/internal/cmd/doctorcmd_unix.go @@ -5,6 +5,7 @@ package cmd import ( "bytes" "fmt" + "log/slog" "os" "os/exec" "runtime" @@ -45,7 +46,7 @@ func (unameCheck) Run(system chezmoi.System, homeDirAbsPath chezmoi.AbsPath) (ch } cmd := exec.Command("uname", "-a") cmd.Stderr = os.Stderr - data, err := chezmoilog.LogCmdOutput(cmd) + data, err := chezmoilog.LogCmdOutput(slog.Default(), cmd) if err != nil { return checkResultFailed, err.Error() } diff --git a/internal/cmd/doctorcmd_windows.go b/internal/cmd/doctorcmd_windows.go index e6df5a47577..92dcc4b16df 100644 --- a/internal/cmd/doctorcmd_windows.go +++ b/internal/cmd/doctorcmd_windows.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "fmt" + "log/slog" "os/exec" "strings" @@ -23,7 +24,7 @@ func (systeminfoCheck) Name() string { func (systeminfoCheck) Run(system chezmoi.System, homeDirAbsPath chezmoi.AbsPath) (checkResult, string) { cmd := exec.Command("systeminfo") - data, err := chezmoilog.LogCmdOutput(cmd) + data, err := chezmoilog.LogCmdOutput(slog.Default(), cmd) if err != nil { return checkResultFailed, err.Error() } diff --git a/internal/cmd/dopplertemplatefuncs.go b/internal/cmd/dopplertemplatefuncs.go index d6108bc8f33..4f99436dc33 100644 --- a/internal/cmd/dopplertemplatefuncs.go +++ b/internal/cmd/dopplertemplatefuncs.go @@ -89,7 +89,7 @@ func (c *Config) dopplerOutput(args []string) ([]byte, error) { cmd.Dir = c.DestDirAbsPath.String() cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/editcmd.go b/internal/cmd/editcmd.go index 87a4d35d5cd..ad9d859ee0e 100644 --- a/internal/cmd/editcmd.go +++ b/internal/cmd/editcmd.go @@ -1,6 +1,7 @@ package cmd import ( + "log/slog" "os" "runtime" "time" @@ -9,6 +10,7 @@ import ( "github.com/spf13/cobra" "github.com/twpayne/chezmoi/v2/internal/chezmoi" + "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) type editCmdConfig struct { @@ -222,20 +224,14 @@ TARGET_REL_PATH: if !ok { return } - c.logger.Debug(). - Stringer("Op", event.Op). - Str("Name", event.Name). - Msg("watcher.Events") + c.logger.Debug("watcher.Events", slog.String("Name", event.Name), chezmoilog.Stringer("Op", event.Op)) err := postEditFunc() - c.logger.Err(err). - Msg("postEditFunc") + chezmoilog.InfoOrError(c.logger, "postEditFunc", err) case _, ok := <-watcher.Errors: if !ok { return } - c.logger.Error(). - Err(err). - Msg("watcher.Errors") + chezmoilog.InfoOrError(c.logger, "watcher.Errors", err) } } }() diff --git a/internal/cmd/gopasstemplatefuncs.go b/internal/cmd/gopasstemplatefuncs.go index c80efe77cbd..0af78a22aaa 100644 --- a/internal/cmd/gopasstemplatefuncs.go +++ b/internal/cmd/gopasstemplatefuncs.go @@ -72,7 +72,7 @@ func (c *Config) gopassOutput(args ...string) ([]byte, error) { cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/hcpvaultsecretsttemplatefuncs.go b/internal/cmd/hcpvaultsecretsttemplatefuncs.go index 374071d9ab1..2e4e49cbe88 100644 --- a/internal/cmd/hcpvaultsecretsttemplatefuncs.go +++ b/internal/cmd/hcpvaultsecretsttemplatefuncs.go @@ -96,7 +96,7 @@ func (c *Config) vltOutput(args []string) ([]byte, error) { cmd.Dir = c.DestDirAbsPath.String() cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/initcmd.go b/internal/cmd/initcmd.go index ea84c7926cf..f34f79fc175 100644 --- a/internal/cmd/initcmd.go +++ b/internal/cmd/initcmd.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "regexp" "strconv" @@ -11,10 +12,10 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/rs/zerolog" "github.com/spf13/cobra" "github.com/twpayne/chezmoi/v2/internal/chezmoi" + "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) type initCmdConfig struct { @@ -79,9 +80,9 @@ var repoGuesses = []struct { }, } -// A loggableGitCloneOptions is a git.CloneOptions that implements -// github.com/rs/zerolog.LogObjectMarshaler. -type loggableGitCloneOptions git.CloneOptions +// A gitCloneOptionsLogValuer is a git.CloneOptions that implements +// log/slog.LogValuer. +type gitCloneOptionsLogValuer git.CloneOptions func (c *Config) newInitCmd() *cobra.Command { initCmd := &cobra.Command{ @@ -250,11 +251,11 @@ func (c *Config) builtinGitClone(repoURLStr string, workingTreeRawPath chezmoi.A for { _, err := git.PlainClone(workingTreeRawPath.String(), isBare, &cloneOptions) - c.logger.Err(err). - Stringer("path", workingTreeRawPath). - Bool("isBare", isBare). - Object("o", loggableGitCloneOptions(cloneOptions)). - Msg("PlainClone") + chezmoilog.InfoOrError(c.logger, "PlainClone", err, + chezmoilog.Stringer("path", workingTreeRawPath), + slog.Bool("isBare", isBare), + slog.Any("options", gitCloneOptionsLogValuer(cloneOptions)), + ) if !errors.Is(err, transport.ErrAuthenticationRequired) { return err } @@ -277,52 +278,50 @@ func (c *Config) builtinGitClone(repoURLStr string, workingTreeRawPath chezmoi.A func (c *Config) builtinGitInit(workingTreeRawPath chezmoi.AbsPath) error { isBare := false _, err := git.PlainInit(workingTreeRawPath.String(), isBare) - c.logger.Err(err). - Stringer("path", workingTreeRawPath). - Bool("isBare", isBare). - Msg("PlainInit") + chezmoilog.InfoOrError(c.logger, "PlainInit", err, + chezmoilog.Stringer("path", workingTreeRawPath), + slog.Bool("isBare", isBare), + ) return err } -// MarshalZerologObject implements -// github.com/rs/zerolog.LogObjectMarshaler.MarshalZerologObject. -// -// We cannot use zerolog's default object marshaler because it logs the auth -// credentials. -func (o loggableGitCloneOptions) MarshalZerologObject(e *zerolog.Event) { +// LogValue implements log/slog.LogValuer.LogValue. +func (o gitCloneOptionsLogValuer) LogValue() slog.Value { + var attrs []slog.Attr if o.URL != "" { - e.Str("URL", o.URL) + attrs = append(attrs, slog.String("URL", o.URL)) } if o.Auth != nil { - e.Stringer("Auth", o.Auth) + attrs = append(attrs, chezmoilog.Stringer("Auth", o.Auth)) } if o.RemoteName != "" { - e.Str("RemoteName", o.RemoteName) + attrs = append(attrs, slog.String("RemoteName", o.RemoteName)) } if o.ReferenceName != "" { - e.Stringer("ReferenceName", o.ReferenceName) + attrs = append(attrs, slog.String("ReferenceName", string(o.ReferenceName))) } if o.SingleBranch { - e.Bool("SingleBranch", o.SingleBranch) + attrs = append(attrs, slog.Bool("SingleBranch", o.SingleBranch)) } if o.NoCheckout { - e.Bool("NoCheckout", o.NoCheckout) + attrs = append(attrs, slog.Bool("NoCheckout", o.NoCheckout)) } if o.Depth != 0 { - e.Int("Depth", o.Depth) + attrs = append(attrs, slog.Int("Depth", o.Depth)) } if o.RecurseSubmodules != 0 { - e.Uint("RecurseSubmodules", uint(o.RecurseSubmodules)) + attrs = append(attrs, slog.Int("RecurseSubmodules", int(o.RecurseSubmodules))) } if o.Tags != 0 { - e.Int("Tags", int(o.Tags)) + attrs = append(attrs, slog.Int("Tags", int(o.Tags))) } if o.InsecureSkipTLS { - e.Bool("InsecureSkipTLS", o.InsecureSkipTLS) + attrs = append(attrs, slog.Bool("InsecureSkipTLS", o.InsecureSkipTLS)) } if o.CABundle != nil { - e.Bytes("CABundle", o.CABundle) + attrs = append(attrs, slog.Any("CABundle", o.CABundle)) } + return slog.GroupValue(attrs...) } // guessRepoURL guesses the user's username and repo from arg. diff --git a/internal/cmd/keepassxctemplatefuncs.go b/internal/cmd/keepassxctemplatefuncs.go index 4b5df48994b..a9f8799f5f9 100644 --- a/internal/cmd/keepassxctemplatefuncs.go +++ b/internal/cmd/keepassxctemplatefuncs.go @@ -179,7 +179,7 @@ func (c *Config) keepassxcOutputCachePassword(command string, args ...string) ([ } cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } @@ -211,7 +211,7 @@ func (c *Config) keepassxcOutputOpen(command string, args ...string) ([]byte, er cmd.Stdin = console.Tty() cmd.Stdout = console.Tty() cmd.Stderr = console.Tty() - if err := chezmoilog.LogCmdStart(cmd); err != nil { + if err := chezmoilog.LogCmdStart(c.logger, cmd); err != nil { return nil, err } @@ -332,7 +332,7 @@ func (c *Config) keepassxcClose() error { if _, err := c.Keepassxc.console.ExpectString("exit\r\n"); err != nil { return err } - if err := chezmoilog.LogCmdWait(c.Keepassxc.cmd); err != nil { + if err := chezmoilog.LogCmdWait(c.logger, c.Keepassxc.cmd); err != nil { return err } if err := c.Keepassxc.console.Close(); err != nil { diff --git a/internal/cmd/keepertemplatefuncs.go b/internal/cmd/keepertemplatefuncs.go index 6b7a74fca34..00892cd1e1c 100644 --- a/internal/cmd/keepertemplatefuncs.go +++ b/internal/cmd/keepertemplatefuncs.go @@ -70,7 +70,7 @@ func (c *Config) keeperOutput(args []string) ([]byte, error) { cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/lastpasstemplatefuncs.go b/internal/cmd/lastpasstemplatefuncs.go index 884cae19580..599c3647780 100644 --- a/internal/cmd/lastpasstemplatefuncs.go +++ b/internal/cmd/lastpasstemplatefuncs.go @@ -82,7 +82,7 @@ func (c *Config) lastpassOutput(args ...string) ([]byte, error) { cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, err } diff --git a/internal/cmd/onepasswordtemplatefuncs.go b/internal/cmd/onepasswordtemplatefuncs.go index fb9cf261fbc..bc7c2281700 100644 --- a/internal/cmd/onepasswordtemplatefuncs.go +++ b/internal/cmd/onepasswordtemplatefuncs.go @@ -161,7 +161,7 @@ func (c *Config) onepasswordGetOrRefreshSessionToken(args *onepasswordArgs) (str cmd := exec.Command(c.Onepassword.Command, commandArgs...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return "", newCmdOutputError(cmd, output, err) } @@ -215,7 +215,7 @@ func (c *Config) onepasswordOutput(args *onepasswordArgs, withSessionToken withS cmd := exec.Command(c.Onepassword.Command, commandArgs...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/passholetemplatefuncs.go b/internal/cmd/passholetemplatefuncs.go index ecd212fbef4..29d4abd7bb4 100644 --- a/internal/cmd/passholetemplatefuncs.go +++ b/internal/cmd/passholetemplatefuncs.go @@ -66,7 +66,7 @@ func (c *Config) passholeOutput(name string, args []string, stdin io.Reader) (st cmd := exec.Command(name, args...) cmd.Stdin = stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return "", newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/passtemplatefuncs.go b/internal/cmd/passtemplatefuncs.go index fc7e18c6cf6..e34bec8ded1 100644 --- a/internal/cmd/passtemplatefuncs.go +++ b/internal/cmd/passtemplatefuncs.go @@ -54,7 +54,7 @@ func (c *Config) passOutput(id string) ([]byte, error) { cmd := exec.Command(c.Pass.Command, args...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/pinentry.go b/internal/cmd/pinentry.go index 676c687839b..7dd23f66692 100644 --- a/internal/cmd/pinentry.go +++ b/internal/cmd/pinentry.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/twpayne/go-pinentry" + "github.com/twpayne/go-pinentry/v3" "github.com/twpayne/chezmoi/v2/internal/chezmoierrors" ) diff --git a/internal/cmd/rbwtemplatefuncs.go b/internal/cmd/rbwtemplatefuncs.go index 6e700001d17..8434be7163c 100644 --- a/internal/cmd/rbwtemplatefuncs.go +++ b/internal/cmd/rbwtemplatefuncs.go @@ -61,7 +61,7 @@ func (c *Config) rbwOutput(args []string) ([]byte, error) { cmd := exec.Command(c.RBW.Command, args...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/secrettemplatefuncs.go b/internal/cmd/secrettemplatefuncs.go index 04aaaa8893b..b1dc4a3cf6e 100644 --- a/internal/cmd/secrettemplatefuncs.go +++ b/internal/cmd/secrettemplatefuncs.go @@ -48,7 +48,7 @@ func (c *Config) secretOutput(args []string) ([]byte, error) { cmd := exec.Command(c.Secret.Command, args...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } diff --git a/internal/cmd/statuscmd.go b/internal/cmd/statuscmd.go index a399a57ce27..67d993413e4 100644 --- a/internal/cmd/statuscmd.go +++ b/internal/cmd/statuscmd.go @@ -4,11 +4,13 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "strings" "github.com/spf13/cobra" "github.com/twpayne/chezmoi/v2/internal/chezmoi" + "github.com/twpayne/chezmoi/v2/internal/chezmoilog" ) type statusCmdConfig struct { @@ -55,12 +57,12 @@ func (c *Config) newStatusCmd() *cobra.Command { func (c *Config) runStatusCmd(cmd *cobra.Command, args []string) error { builder := strings.Builder{} preApplyFunc := func(targetRelPath chezmoi.RelPath, targetEntryState, lastWrittenEntryState, actualEntryState *chezmoi.EntryState) error { - c.logger.Info(). - Stringer("targetRelPath", targetRelPath). - Object("targetEntryState", targetEntryState). - Object("lastWrittenEntryState", lastWrittenEntryState). - Object("actualEntryState", actualEntryState). - Msg("statusPreApplyFunc") + c.logger.Info("statusPreApplyFunc", + chezmoilog.Stringer("targetRelPath", targetRelPath), + slog.Any("targetEntryState", targetEntryState), + slog.Any("lastWrittenEntryState", lastWrittenEntryState), + slog.Any("actualEntryState", actualEntryState), + ) var ( x = ' ' diff --git a/internal/cmd/templatefuncs.go b/internal/cmd/templatefuncs.go index 8a889d0aa6e..0e930da5eba 100644 --- a/internal/cmd/templatefuncs.go +++ b/internal/cmd/templatefuncs.go @@ -304,7 +304,7 @@ func (c *Config) ioregTemplateFunc() map[string]any { args := []string{"-a", "-l"} cmd := exec.Command(command, args...) cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { panic(newCmdOutputError(cmd, output, err)) } @@ -391,7 +391,7 @@ func (c *Config) mozillaInstallHashTemplateFunc(path string) string { func (c *Config) outputTemplateFunc(name string, args ...string) string { cmd := exec.Command(name, args...) cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { panic(newCmdOutputError(cmd, output, err)) } diff --git a/internal/cmd/textconv.go b/internal/cmd/textconv.go index 11e691d3009..0f307ebe11e 100644 --- a/internal/cmd/textconv.go +++ b/internal/cmd/textconv.go @@ -2,6 +2,7 @@ package cmd import ( "bytes" + "log/slog" "os" "os/exec" @@ -39,5 +40,5 @@ func (t textConv) convert(path string, data []byte) ([]byte, error) { cmd := exec.Command(longestPatternElement.Command, longestPatternElement.Args...) //nolint:gosec cmd.Stdin = bytes.NewReader(data) cmd.Stderr = os.Stderr - return chezmoilog.LogCmdOutput(cmd) + return chezmoilog.LogCmdOutput(slog.Default(), cmd) } diff --git a/internal/cmd/upgradecmd.go b/internal/cmd/upgradecmd.go index 42fba62e2f1..f5340e98178 100644 --- a/internal/cmd/upgradecmd.go +++ b/internal/cmd/upgradecmd.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/http" "os" "os/exec" @@ -121,10 +122,7 @@ func (c *Config) runUpgradeCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("%s/%s: cannot determine upgrade method for %s", runtime.GOOS, runtime.GOARCH, executableAbsPath) } } - c.logger.Info(). - Str("executable", c.upgrade.executable). - Str("method", method). - Msg("upgradeMethod") + c.logger.Info("upgradeMethod", slog.String("executable", c.upgrade.executable), slog.String("method", method)) // Replace the executable with the updated version. switch method { @@ -173,7 +171,7 @@ func (c *Config) runUpgradeCmd(cmd *cobra.Command, args []string) error { chezmoiVersionCmd.Stdin = os.Stdin chezmoiVersionCmd.Stdout = os.Stdout chezmoiVersionCmd.Stderr = os.Stderr - return chezmoilog.LogCmdRun(chezmoiVersionCmd) + return chezmoilog.LogCmdRun(c.logger, chezmoiVersionCmd) } func (c *Config) getChecksums(ctx context.Context, rr *github.RepositoryRelease) (map[string][]byte, error) { diff --git a/internal/cmd/upgradecmd_unix.go b/internal/cmd/upgradecmd_unix.go index a0d281a1ca2..4ae0e1e66e2 100644 --- a/internal/cmd/upgradecmd_unix.go +++ b/internal/cmd/upgradecmd_unix.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "os" "os/exec" "regexp" @@ -175,7 +176,7 @@ func getLibc() (string, error) { // writes to stdout and exits with code 0. On musl libc systems it writes to // stderr and exits with code 1. lddCmd := exec.Command("ldd", "--version") - switch output, _ := chezmoilog.LogCmdCombinedOutput(lddCmd); { + switch output, _ := chezmoilog.LogCmdCombinedOutput(slog.Default(), lddCmd); { case libcTypeGlibcRx.Match(output): return libcTypeGlibc, nil case libcTypeMuslRx.Match(output): @@ -184,7 +185,7 @@ func getLibc() (string, error) { // Second, try getconf GNU_LIBC_VERSION. getconfCmd := exec.Command("getconf", "GNU_LIBC_VERSION") - if output, _ := chezmoilog.LogCmdCombinedOutput(getconfCmd); libcTypeGlibcRx.Match(output) { + if output, _ := chezmoilog.LogCmdCombinedOutput(slog.Default(), getconfCmd); libcTypeGlibcRx.Match(output) { return libcTypeGlibc, nil } diff --git a/internal/cmd/vaulttemplatefuncs.go b/internal/cmd/vaulttemplatefuncs.go index 0863f762926..f298be0688f 100644 --- a/internal/cmd/vaulttemplatefuncs.go +++ b/internal/cmd/vaulttemplatefuncs.go @@ -22,7 +22,7 @@ func (c *Config) vaultTemplateFunc(key string) any { cmd := exec.Command(c.Vault.Command, args...) //nolint:gosec cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr - output, err := chezmoilog.LogCmdOutput(cmd) + output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { panic(newCmdOutputError(cmd, output, err)) } From 5686d6f83a57ab16814eede59dcc5012ef9284cf Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 11 Feb 2024 03:34:03 +0100 Subject: [PATCH 2/2] chore: Improve context handling --- .golangci.yml | 2 +- internal/chezmoi/sourcestate.go | 2 +- internal/chezmoilog/chezmoilog.go | 18 +++++++++++------- internal/cmd/azurekeyvaulttemplatefuncs.go | 2 +- internal/cmd/upgradecmd.go | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index dea30d89e87..65df25e4ab5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,6 @@ linters: - bidichk - bodyclose - containedctx - - contextcheck - decorder - dogsled - dupword @@ -78,6 +77,7 @@ linters: - zerologlint disable: - asasalint + - contextcheck - cyclop - depguard - dupl diff --git a/internal/chezmoi/sourcestate.go b/internal/chezmoi/sourcestate.go index 552cbd7c2bc..e31ff6d7491 100644 --- a/internal/chezmoi/sourcestate.go +++ b/internal/chezmoi/sourcestate.go @@ -1531,7 +1531,7 @@ func (s *SourceState) getExternalDataRaw( if err != nil { return nil, err } - resp, err := chezmoilog.LogHTTPRequest(s.logger, s.httpClient, req) + resp, err := chezmoilog.LogHTTPRequest(ctx, s.logger, s.httpClient, req) if err != nil { return nil, err } diff --git a/internal/chezmoilog/chezmoilog.go b/internal/chezmoilog/chezmoilog.go index 4c4b56e1132..a4076c5b3ce 100644 --- a/internal/chezmoilog/chezmoilog.go +++ b/internal/chezmoilog/chezmoilog.go @@ -119,7 +119,7 @@ func FirstFewBytes(key string, data []byte) slog.Attr { // LogHTTPRequest calls httpClient.Do, logs the result to logger, and returns // the result. -func LogHTTPRequest(logger *slog.Logger, client *http.Client, req *http.Request) (*http.Response, error) { +func LogHTTPRequest(ctx context.Context, logger *slog.Logger, client *http.Client, req *http.Request) (*http.Response, error) { start := time.Now() resp, err := client.Do(req) attrs := []slog.Attr{ @@ -134,7 +134,7 @@ func LogHTTPRequest(logger *slog.Logger, client *http.Client, req *http.Request) slog.Int("contentLength", int(resp.ContentLength)), ) } - InfoOrError(logger, "HTTPRequest", err, attrs...) + InfoOrErrorContext(ctx, logger, "HTTPRequest", err, attrs...) return resp, err } @@ -149,7 +149,7 @@ func LogCmdCombinedOutput(logger *slog.Logger, cmd *exec.Cmd) ([]byte, error) { slog.Any("combinedOutput", firstFewBytes(combinedOutput)), } attrs = AppendExitErrorAttrs(attrs, err) - InfoOrError(logger, "Output", err, attrs...) + InfoOrErrorContext(context.Background(), logger, "Output", err, attrs...) return combinedOutput, err } @@ -164,7 +164,7 @@ func LogCmdOutput(logger *slog.Logger, cmd *exec.Cmd) ([]byte, error) { slog.Any("output", firstFewBytes(output)), } attrs = AppendExitErrorAttrs(attrs, err) - InfoOrError(logger, "Output", err, attrs...) + InfoOrErrorContext(context.Background(), logger, "Output", err, attrs...) return output, err } @@ -177,7 +177,7 @@ func LogCmdRun(logger *slog.Logger, cmd *exec.Cmd) error { slog.Duration("duration", time.Since(start)), } attrs = AppendExitErrorAttrs(attrs, err) - InfoOrError(logger, "Run", err, attrs...) + InfoOrErrorContext(context.Background(), logger, "Run", err, attrs...) return err } @@ -190,7 +190,7 @@ func LogCmdStart(logger *slog.Logger, cmd *exec.Cmd) error { slog.Time("start", start), } attrs = AppendExitErrorAttrs(attrs, err) - InfoOrError(logger, "Start", err, attrs...) + InfoOrErrorContext(context.Background(), logger, "Start", err, attrs...) return err } @@ -208,6 +208,10 @@ func LogCmdWait(logger *slog.Logger, cmd *exec.Cmd) error { } func InfoOrError(logger *slog.Logger, msg string, err error, attrs ...slog.Attr) { + InfoOrErrorContext(context.Background(), logger, msg, err, attrs...) +} + +func InfoOrErrorContext(ctx context.Context, logger *slog.Logger, msg string, err error, attrs ...slog.Attr) { if logger == nil { return } @@ -222,7 +226,7 @@ func InfoOrError(logger *slog.Logger, msg string, err error, attrs ...slog.Attr) if err != nil { level = slog.LevelError } - logger.Log(context.TODO(), level, msg, args...) + logger.Log(ctx, level, msg, args...) } // Stringer returns an slog.Attr with value. diff --git a/internal/cmd/azurekeyvaulttemplatefuncs.go b/internal/cmd/azurekeyvaulttemplatefuncs.go index 57a80fbd7d2..31f6a52a273 100644 --- a/internal/cmd/azurekeyvaulttemplatefuncs.go +++ b/internal/cmd/azurekeyvaulttemplatefuncs.go @@ -53,7 +53,7 @@ func (a *azureKeyVaultConfig) GetSecret(secretName, vaultName string) string { } } - resp, err := a.vaults[vaultName].client.GetSecret(context.TODO(), secretName, "", nil) + resp, err := a.vaults[vaultName].client.GetSecret(context.Background(), secretName, "", nil) if err != nil { panic(err) } diff --git a/internal/cmd/upgradecmd.go b/internal/cmd/upgradecmd.go index f5340e98178..2cf0f4a4c82 100644 --- a/internal/cmd/upgradecmd.go +++ b/internal/cmd/upgradecmd.go @@ -207,7 +207,7 @@ func (c *Config) downloadURL(ctx context.Context, url string) ([]byte, error) { if err != nil { return nil, err } - resp, err := chezmoilog.LogHTTPRequest(c.logger, httpClient, req) + resp, err := chezmoilog.LogHTTPRequest(ctx, c.logger, httpClient, req) if err != nil { return nil, err }