From 218ec2c138e14204048410f93050d21ff8fa07ac Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 19 Jan 2026 18:06:42 +0100 Subject: [PATCH] feat: new logger (slog) (#2800) --- .golangci.yml | 5 +- acme/api/api.go | 2 +- certificate/authorization.go | 10 +-- certificate/certificates.go | 22 +++--- challenge/dns01/dns_challenge.go | 14 ++-- challenge/http01/http_challenge.go | 6 +- challenge/http01/http_challenge_server.go | 10 ++- challenge/resolver/prober.go | 6 +- challenge/resolver/solver_manager.go | 8 +-- challenge/tlsalpn01/tls_alpn_challenge.go | 6 +- .../tlsalpn01/tls_alpn_challenge_server.go | 2 +- cmd/accounts_storage.go | 22 +++--- cmd/certs_storage.go | 26 +++---- cmd/cmd_before.go | 8 ++- cmd/cmd_renew.go | 60 ++++++++--------- cmd/cmd_revoke.go | 12 ++-- cmd/cmd_run.go | 12 ++-- cmd/lego/main.go | 2 +- cmd/setup.go | 10 +-- cmd/setup_challenges.go | 30 +++++---- log/lazy.go | 51 ++++++++++++++ log/logger.go | 67 ++++++++----------- platform/config/env/env.go | 2 +- platform/wait/wait.go | 2 +- providers/dns/bluecat/bluecat.go | 2 +- providers/dns/cloudflare/cloudflare.go | 5 +- providers/dns/cloudns/cloudns.go | 2 +- providers/dns/desec/desec.go | 2 +- providers/dns/designate/designate.go | 6 +- providers/dns/dynu/internal/client.go | 2 +- providers/dns/edgedns/edgedns.go | 3 - providers/dns/exec/exec.go | 2 +- providers/dns/exec/exec_test.go | 25 +++---- providers/dns/exec/log_mock_test.go | 39 ++++++----- providers/dns/gandiv5/gandiv5.go | 2 +- providers/dns/gandiv5/internal/client.go | 9 --- providers/dns/gcloud/googlecloud.go | 5 +- providers/dns/hetzner/hetzner.go | 4 +- providers/dns/hurricane/internal/client.go | 4 +- providers/dns/infomaniak/internal/client.go | 18 +---- providers/dns/inwx/inwx.go | 8 +-- providers/dns/joker/internal/dmapi/client.go | 6 -- providers/dns/joker/internal/svc/client.go | 2 + providers/dns/joker/joker.go | 3 - providers/dns/joker/provider_dmapi.go | 15 +---- providers/dns/joker/provider_svc.go | 14 +++- providers/dns/liara/liara.go | 2 +- providers/dns/namecheap/internal/ip.go | 10 ++- providers/dns/namecheap/namecheap.go | 11 +-- providers/dns/netcup/netcup.go | 6 +- providers/dns/ns1/ns1.go | 15 ++--- providers/dns/octenium/octenium.go | 2 +- .../dns/oraclecloud/configurationprovider.go | 2 +- providers/dns/pdns/pdns.go | 2 +- providers/dns/stackpath/stackpath.go | 16 +++-- providers/dns/variomedia/variomedia.go | 3 - registration/registar.go | 12 ++-- 57 files changed, 330 insertions(+), 324 deletions(-) create mode 100644 log/lazy.go diff --git a/.golangci.yml b/.golangci.yml index 68622529d..557263f8f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -101,9 +101,9 @@ linters: - Print - Printf - Warn - - Warnf - Fatal - Fatalf + - LazySprintf misspell: locale: US ignore-rules: @@ -280,6 +280,9 @@ linters: text: 'SA1019: linodego\.(DomainsPagedResponse|DomainRecordsPagedResponse) is deprecated' linters: - staticcheck + - path: log/logger.go + linters: + - gochecknoinits issues: max-issues-per-linter: 0 diff --git a/acme/api/api.go b/acme/api/api.go index c7cb896c9..961bd79bc 100644 --- a/acme/api/api.go +++ b/acme/api/api.go @@ -99,7 +99,7 @@ func (a *Core) retrievablePost(ctx context.Context, uri string, content []byte, } notify := func(err error, duration time.Duration) { - log.Infof("retry due to: %v", err) + log.Warn("Retry.", "error", err) } return backoff.Retry(ctx, operation, diff --git a/certificate/authorization.go b/certificate/authorization.go index 8ed377413..be0828ab1 100644 --- a/certificate/authorization.go +++ b/certificate/authorization.go @@ -41,7 +41,7 @@ func (c *Certifier) getAuthorizations(ctx context.Context, order acme.ExtendedOr } for i, auth := range order.Authorizations { - log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) + log.Info("Authorization", "url", order.Identifiers[i].Value, "authz", auth) } close(resc) @@ -54,19 +54,19 @@ func (c *Certifier) deactivateAuthorizations(ctx context.Context, order acme.Ext for _, authzURL := range order.Authorizations { auth, err := c.core.Authorizations.Get(ctx, authzURL) if err != nil { - log.Infof("Unable to get the authorization for %s: %v", authzURL, err) + log.Info("Unable to get the authorization.", "url", authzURL, "error", err) continue } if auth.Status == acme.StatusValid && !force { - log.Infof("Skipping deactivating of valid auth: %s", authzURL) + log.Info("Skipping deactivating of valid authorization.", "url", authzURL) continue } - log.Infof("Deactivating auth: %s", authzURL) + log.Info("Deactivating authorization.", "url", authzURL) if c.core.Authorizations.Deactivate(ctx, authzURL) != nil { - log.Infof("Unable to deactivate the authorization: %s", authzURL) + log.Info("Unable to deactivate the authorization.", "url", authzURL) } } } diff --git a/certificate/certificates.go b/certificate/certificates.go index dcb0577a9..433ef90b3 100644 --- a/certificate/certificates.go +++ b/certificate/certificates.go @@ -165,9 +165,9 @@ func (c *Certifier) Obtain(ctx context.Context, request ObtainRequest) (*Resourc domains := sanitizeDomain(request.Domains) if request.Bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) + log.Info("acme: Obtaining bundled SAN certificate.", "domains", strings.Join(domains, ", ")) } else { - log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) + log.Info("acme: Obtaining SAN certificate.", "domains", strings.Join(domains, ", ")) } orderOpts := &api.OrderOptions{ @@ -196,7 +196,7 @@ func (c *Certifier) Obtain(ctx context.Context, request ObtainRequest) (*Resourc return nil, err } - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + log.Info("acme: Validations succeeded; requesting certificates.", "domains", strings.Join(domains, ", ")) failures := newObtainError() @@ -233,9 +233,9 @@ func (c *Certifier) ObtainForCSR(ctx context.Context, request ObtainForCSRReques domains := certcrypto.ExtractDomainsCSR(request.CSR) if request.Bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) + log.Info("acme: Obtaining bundled SAN certificate given a CSR.", "domains", strings.Join(domains, ", ")) } else { - log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) + log.Info("acme: Obtaining SAN certificate given a CSR.", "domains", strings.Join(domains, ", ")) } orderOpts := &api.OrderOptions{ @@ -264,7 +264,7 @@ func (c *Certifier) ObtainForCSR(ctx context.Context, request ObtainForCSRReques return nil, err } - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + log.Info("acme: Validations succeeded; requesting certificates.", "domains", strings.Join(domains, ", ")) failures := newObtainError() @@ -414,7 +414,7 @@ func (c *Certifier) checkResponse(ctx context.Context, order acme.ExtendedOrder, certRes.CertStableURL = order.Certificate if preferredChain == "" { - log.Infof("[%s] Server responded with a certificate.", certRes.Domain) + log.Info("Server responded with a certificate.", "domain", certRes.Domain) return true, nil } @@ -426,7 +426,7 @@ func (c *Certifier) checkResponse(ctx context.Context, order acme.ExtendedOrder, } if ok { - log.Infof("[%s] Server responded with a certificate for the preferred certificate chains %q.", certRes.Domain, preferredChain) + log.Info("Server responded with a certificate.", "domain", certRes.Domain, "preferredChain", preferredChain) certRes.IssuerCertificate = cert.Issuer certRes.Certificate = cert.Cert @@ -437,7 +437,7 @@ func (c *Certifier) checkResponse(ctx context.Context, order acme.ExtendedOrder, } } - log.Infof("lego has been configured to prefer certificate chains with issuer %q, but no chain from the CA matched this issuer. Using the default certificate chain instead.", preferredChain) + log.Info("lego has been configured to prefer certificate chains with issuer, but no chain from the CA matched this issuer. Using the default certificate chain instead.", "preferredChain", preferredChain) return true, nil } @@ -529,7 +529,7 @@ func (c *Certifier) RenewWithOptions(ctx context.Context, certRes Resource, opti // This is just meant to be informal for the user. timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", certRes.Domain, int(timeLeft.Hours())) + log.Info("acme: Trying renewal.", "domain", certRes.Domain, "hoursRemaining", int(timeLeft.Hours())) // We always need to request a new certificate to renew. // Start by checking to see if the certificate was based off a CSR, @@ -742,7 +742,7 @@ func sanitizeDomain(domains []string) []string { for _, domain := range domains { sanitizedDomain, err := idna.ToASCII(domain) if err != nil { - log.Infof("skip domain %q: unable to sanitize (punnycode): %v", domain, err) + log.Warn("skip domain: unable to sanitize (punnycode).", "domain", domain, "error", err) } else { sanitizedDomains = append(sanitizedDomains, sanitizedDomain) } diff --git a/challenge/dns01/dns_challenge.go b/challenge/dns01/dns_challenge.go index a6f7ad47b..c1212d486 100644 --- a/challenge/dns01/dns_challenge.go +++ b/challenge/dns01/dns_challenge.go @@ -66,7 +66,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov for _, opt := range opts { err := opt(chlg) if err != nil { - log.Infof("challenge option error: %v", err) + log.Warn("Challenge option skipped.", "error", err) } } @@ -77,7 +77,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov // It does not validate record propagation, or do anything at all with the acme server. func (c *Challenge) PreSolve(ctx context.Context, authz acme.Authorization) error { domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Preparing to solve DNS-01", domain) + log.Info("acme: Preparing to solve DNS-01.", "domain", domain) chlng, err := challenge.FindChallenge(challenge.DNS01, authz) if err != nil { @@ -104,7 +104,7 @@ func (c *Challenge) PreSolve(ctx context.Context, authz acme.Authorization) erro func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Trying to solve DNS-01", domain) + log.Info("acme: Trying to solve DNS-01.", "domain", domain) chlng, err := challenge.FindChallenge(challenge.DNS01, authz) if err != nil { @@ -128,14 +128,14 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval } - log.Infof("[%s] acme: Checking DNS record propagation. [nameservers=%s]", domain, strings.Join(recursiveNameservers, ",")) + log.Info("acme: Checking DNS record propagation.", "domain", domain, "nameservers", strings.Join(recursiveNameservers, ",")) time.Sleep(interval) err = wait.For("propagation", timeout, interval, func() (bool, error) { stop, errP := c.preCheck.call(domain, info.EffectiveFQDN, info.Value) if !stop || errP != nil { - log.Infof("[%s] acme: Waiting for DNS record propagation.", domain) + log.Info("acme: Waiting for DNS record propagation.", "domain", domain) } return stop, errP @@ -151,7 +151,7 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { // CleanUp cleans the challenge. func (c *Challenge) CleanUp(authz acme.Authorization) error { - log.Infof("[%s] acme: Cleaning DNS-01 challenge", challenge.GetTargetedDomain(authz)) + log.Info("acme: Cleaning DNS-01 challenge.", "domain", challenge.GetTargetedDomain(authz)) chlng, err := challenge.FindChallenge(challenge.DNS01, authz) if err != nil { @@ -237,7 +237,7 @@ func getChallengeFQDN(domain string, followCNAME bool) string { break } - log.Infof("Found CNAME entry for %q: %q", fqdn, cname) + log.Info("Found CNAME entry.", "fqdn", fqdn, "cname", cname) fqdn = cname } diff --git a/challenge/http01/http_challenge.go b/challenge/http01/http_challenge.go index 577ceeb83..83e494a9f 100644 --- a/challenge/http01/http_challenge.go +++ b/challenge/http01/http_challenge.go @@ -45,7 +45,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov for _, opt := range opts { err := opt(chlg) if err != nil { - log.Infof("challenge option error: %v", err) + log.Warn("Challenge option skipped.", "error", err) } } @@ -58,7 +58,7 @@ func (c *Challenge) SetProvider(provider challenge.Provider) { func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Trying to solve HTTP-01", domain) + log.Info("acme: Trying to solve HTTP-01.", "domain", domain) chlng, err := challenge.FindChallenge(challenge.HTTP01, authz) if err != nil { @@ -79,7 +79,7 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { defer func() { err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth) if err != nil { - log.Warnf("[%s] acme: cleaning up failed: %v", domain, err) + log.Warn("acme: cleaning up failed.", "domain", domain, "error", err) } }() diff --git a/challenge/http01/http_challenge_server.go b/challenge/http01/http_challenge_server.go index 9edb52407..2c589125a 100644 --- a/challenge/http01/http_challenge_server.go +++ b/challenge/http01/http_challenge_server.go @@ -120,12 +120,16 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) { return } - log.Infof("[%s] Served key authentication", domain) + log.Info("Served key authentication.", "domain", domain) return } - log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure you are passing the %s header properly.", r.Host, r.Method, s.matcher.name()) + log.Warn("Received request but the domain did not match any challenge. Please ensure you are passing the header properly.", + "domain", r.Host, + "method", r.Method, + "header", s.matcher.name(), + ) _, err := w.Write([]byte("TEST")) if err != nil { @@ -142,7 +146,7 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) { err := httpServer.Serve(s.listener) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Println(err) + log.Warn("HTTP server serve.", "error", err) } s.done <- true diff --git a/challenge/resolver/prober.go b/challenge/resolver/prober.go index 151ca0976..ea8f9a512 100644 --- a/challenge/resolver/prober.go +++ b/challenge/resolver/prober.go @@ -63,7 +63,7 @@ func (p *Prober) Solve(ctx context.Context, authorizations []acme.Authorization) domain := challenge.GetTargetedDomain(authz) if authz.Status == acme.StatusValid { // Boulder might recycle recent validated authz (see issue #267) - log.Infof("[%s] acme: authorization already valid; skipping challenge", domain) + log.Info("acme: authorization already valid; skipping challenge.", "domain", domain) continue } @@ -130,7 +130,7 @@ func sequentialSolve(ctx context.Context, authSolvers []*selectedAuthSolver, fai if len(authSolvers)-1 > i { solvr := authSolver.solver.(sequential) _, interval := solvr.Sequential() - log.Infof("sequence: wait for %s", interval) + log.Info("sequence: wait.", "interval", interval) time.Sleep(interval) } } @@ -178,7 +178,7 @@ func cleanUp(solvr solver, authz acme.Authorization) { err := solvr.CleanUp(authz) if err != nil { - log.Warnf("[%s] acme: cleaning up failed: %v ", domain, err) + log.Warn("acme: cleaning up failed.", "domain", domain, "error", err) } } } diff --git a/challenge/resolver/solver_manager.go b/challenge/resolver/solver_manager.go index 95764fbd8..e287a9ae4 100644 --- a/challenge/resolver/solver_manager.go +++ b/challenge/resolver/solver_manager.go @@ -68,11 +68,11 @@ func (c *SolverManager) chooseSolver(authz acme.Authorization) solver { domain := challenge.GetTargetedDomain(authz) for _, chlg := range authz.Challenges { if solvr, ok := c.solvers[challenge.Type(chlg.Type)]; ok { - log.Infof("[%s] acme: use %s solver", domain, chlg.Type) + log.Info("acme: use solver.", "domain", domain, "type", chlg.Type) return solvr } - log.Infof("[%s] acme: Could not find solver for: %s", domain, chlg.Type) + log.Info("acme: Could not find the solver.", "domain", domain, "type", chlg.Type) } return nil @@ -90,7 +90,7 @@ func validate(ctx context.Context, core *api.Core, domain string, chlg acme.Chal } if valid { - log.Infof("[%s] The server validated our request", domain) + log.Info("The server validated our request.", "domain", domain) return nil } @@ -123,7 +123,7 @@ func validate(ctx context.Context, core *api.Core, domain string, chlg acme.Chal } if valid { - log.Infof("[%s] The server validated our request", domain) + log.Info("The server validated our request.", "domain", domain) return nil } diff --git a/challenge/tlsalpn01/tls_alpn_challenge.go b/challenge/tlsalpn01/tls_alpn_challenge.go index a48864ea4..2469b5898 100644 --- a/challenge/tlsalpn01/tls_alpn_challenge.go +++ b/challenge/tlsalpn01/tls_alpn_challenge.go @@ -50,7 +50,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov for _, opt := range opts { err := opt(chlg) if err != nil { - log.Infof("challenge option error: %v", err) + log.Warn("Challenge option skipped.", "error", err) } } @@ -64,7 +64,7 @@ func (c *Challenge) SetProvider(provider challenge.Provider) { // Solve manages the provider to validate and solve the challenge. func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { domain := authz.Identifier.Value - log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", challenge.GetTargetedDomain(authz)) + log.Info("acme: Trying to solve TLS-ALPN-01.", "domain", challenge.GetTargetedDomain(authz)) chlng, err := challenge.FindChallenge(challenge.TLSALPN01, authz) if err != nil { @@ -85,7 +85,7 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { defer func() { err := c.provider.CleanUp(domain, chlng.Token, keyAuth) if err != nil { - log.Warnf("[%s] acme: cleaning up failed: %v", challenge.GetTargetedDomain(authz), err) + log.Warn("acme: cleaning up failed.", "domain", challenge.GetTargetedDomain(authz), err) } }() diff --git a/challenge/tlsalpn01/tls_alpn_challenge_server.go b/challenge/tlsalpn01/tls_alpn_challenge_server.go index 4c93864e1..b242e8ca8 100644 --- a/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ b/challenge/tlsalpn01/tls_alpn_challenge_server.go @@ -74,7 +74,7 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error { go func() { err := http.Serve(s.listener, nil) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Println(err) + log.Warn("HTTP server serve.", "error", err) } }() diff --git a/cmd/accounts_storage.go b/cmd/accounts_storage.go index 7f6d801af..2d765b845 100644 --- a/cmd/accounts_storage.go +++ b/cmd/accounts_storage.go @@ -79,7 +79,7 @@ func NewAccountsStorage(ctx *cli.Context) *AccountsStorage { serverURL, err := url.Parse(ctx.String(flgServer)) if err != nil { - log.Fatal(err) + log.Fatal("URL parsing", "flag", flgServer, "serverURL", ctx.String(flgServer), "error", err) } rootPath := filepath.Join(ctx.String(flgPath), baseAccountsRootFolderName) @@ -103,7 +103,7 @@ func (s *AccountsStorage) ExistsAccountFilePath() bool { if _, err := os.Stat(accountFile); os.IsNotExist(err) { return false } else if err != nil { - log.Fatal(err) + log.Fatal("Could not read the account file.", "filepath", accountFile, "error", err) } return true @@ -137,14 +137,14 @@ func (s *AccountsStorage) Save(account *Account) error { func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account { fileBytes, err := os.ReadFile(s.accountFilePath) if err != nil { - log.Fatalf("Could not load file for account %s: %v", s.GetUserID(), err) + log.Fatal("Could not load the account file.", "userID", s.GetUserID(), "error", err) } var account Account err = json.Unmarshal(fileBytes, &account) if err != nil { - log.Fatalf("Could not parse file for account %s: %v", s.GetUserID(), err) + log.Fatal("Could not parse the account file.", "userID", s.GetUserID(), "error", err) } account.key = privateKey @@ -152,14 +152,14 @@ func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account { if account.Registration == nil || account.Registration.Body.Status == "" { reg, err := tryRecoverRegistration(s.ctx, privateKey) if err != nil { - log.Fatalf("Could not load account for %s. Registration is nil: %#v", s.GetUserID(), err) + log.Fatal("Could not load the account file. Registration is nil.", "userID", s.GetUserID(), "error", err) } account.Registration = reg err = s.Save(&account) if err != nil { - log.Fatalf("Could not save account for %s. Registration is nil: %#v", s.GetUserID(), err) + log.Fatal("Could not save the account file. Registration is nil.", "userID", s.GetUserID(), "error", err) } } @@ -170,22 +170,22 @@ func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.Priva accKeyPath := filepath.Join(s.keysPath, s.GetUserID()+".key") if _, err := os.Stat(accKeyPath); os.IsNotExist(err) { - log.Printf("No key found for account %s. Generating a %s key.", s.GetUserID(), keyType) + log.Info("No key found for the account. Generating a new private key.", "userID", s.GetUserID(), "keyType", keyType) s.createKeysFolder() privateKey, err := generatePrivateKey(accKeyPath, keyType) if err != nil { - log.Fatalf("Could not generate RSA private account key for account %s: %v", s.GetUserID(), err) + log.Fatal("Could not generate the RSA private account key.", "userID", s.GetUserID(), "error", err) } - log.Printf("Saved key to %s", accKeyPath) + log.Info("Saved key.", "filepath", accKeyPath) return privateKey } privateKey, err := loadPrivateKey(accKeyPath) if err != nil { - log.Fatalf("Could not load RSA private key from file %s: %v", accKeyPath, err) + log.Fatal("Could not load an RSA private key from the file.", "filepath", accKeyPath, "error", err) } return privateKey @@ -193,7 +193,7 @@ func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.Priva func (s *AccountsStorage) createKeysFolder() { if err := createNonExistingFolder(s.keysPath); err != nil { - log.Fatalf("Could not check/create directory for account %s: %v", s.GetUserID(), err) + log.Fatal("Could not check/create the directory for the account.", "userID", s.GetUserID(), "error", err) } } diff --git a/cmd/certs_storage.go b/cmd/certs_storage.go index 17bb1b035..813ef8d44 100644 --- a/cmd/certs_storage.go +++ b/cmd/certs_storage.go @@ -65,7 +65,7 @@ func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage { switch pfxFormat { case "DES", "RC2", "SHA256": default: - log.Fatalf("Invalid PFX format: %s", pfxFormat) + log.Fatal("Invalid PFX format.", "format", pfxFormat) } return &CertificatesStorage{ @@ -82,14 +82,14 @@ func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage { func (s *CertificatesStorage) CreateRootFolder() { err := createNonExistingFolder(s.rootPath) if err != nil { - log.Fatalf("Could not check/create path: %v", err) + log.Fatal("Could not check/create the root folder", "filepath", s.rootPath, "error", err) } } func (s *CertificatesStorage) CreateArchiveFolder() { err := createNonExistingFolder(s.archivePath) if err != nil { - log.Fatalf("Could not check/create path: %v", err) + log.Fatal("Could not check/create the archive folder.", "filepath", s.archivePath, "error", err) } } @@ -104,13 +104,13 @@ func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) { // as web servers would not be able to work with a combined file. err := s.WriteFile(domain, certExt, certRes.Certificate) if err != nil { - log.Fatalf("Unable to save Certificate for domain %s\n\t%v", domain, err) + log.Fatal("Unable to save Certificate.", "domain", domain, "error", err) } if certRes.IssuerCertificate != nil { err = s.WriteFile(domain, issuerExt, certRes.IssuerCertificate) if err != nil { - log.Fatalf("Unable to save IssuerCertificate for domain %s\n\t%v", domain, err) + log.Fatal("Unable to save IssuerCertificate.", "domain", domain, "error", err) } } @@ -118,33 +118,33 @@ func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) { if certRes.PrivateKey != nil { err = s.WriteCertificateFiles(domain, certRes) if err != nil { - log.Fatalf("Unable to save PrivateKey for domain %s\n\t%v", domain, err) + log.Fatal("Unable to save PrivateKey.", "domain", domain, "error", err) } } else if s.pem || s.pfx { // we don't have the private key; can't write the .pem or .pfx file - log.Fatalf("Unable to save PEM or PFX without private key for domain %s. Are you using a CSR?", domain) + log.Fatal("Unable to save PEM or PFX without the private key. Are you using a CSR?", "domain", domain) } jsonBytes, err := json.MarshalIndent(certRes, "", "\t") if err != nil { - log.Fatalf("Unable to marshal CertResource for domain %s\n\t%v", domain, err) + log.Fatal("Unable to marshal CertResource.", "domain", domain, "error", err) } err = s.WriteFile(domain, resourceExt, jsonBytes) if err != nil { - log.Fatalf("Unable to save CertResource for domain %s\n\t%v", domain, err) + log.Fatal("Unable to save CertResource.", "domain", domain, "error", err) } } func (s *CertificatesStorage) ReadResource(domain string) certificate.Resource { raw, err := s.ReadFile(domain, resourceExt) if err != nil { - log.Fatalf("Error while loading the meta data for domain %s\n\t%v", domain, err) + log.Fatal("Error while loading the metadata.", "domain", domain, "error", err) } var resource certificate.Resource if err = json.Unmarshal(raw, &resource); err != nil { - log.Fatalf("Error while marshaling the meta data for domain %s\n\t%v", domain, err) + log.Fatal("Error while marshaling the metadata.", "domain", domain, "error", err) } return resource @@ -156,7 +156,7 @@ func (s *CertificatesStorage) ExistsFile(domain, extension string) bool { if _, err := os.Stat(filePath); os.IsNotExist(err) { return false } else if err != nil { - log.Fatal(err) + log.Fatal("File stat", "filepath", filePath, "error", err) } return true @@ -319,7 +319,7 @@ func getPFXEncoder(pfxFormat string) (*pkcs12.Encoder, error) { func sanitizedDomain(domain string) string { safe, err := idna.ToASCII(strings.NewReplacer(":", "-", "*", "_").Replace(domain)) if err != nil { - log.Fatal(err) + log.Fatal("Could not sanitize the domain.", "domain", domain, "error", err) } return safe diff --git a/cmd/cmd_before.go b/cmd/cmd_before.go index 645ce4324..37fece5a9 100644 --- a/cmd/cmd_before.go +++ b/cmd/cmd_before.go @@ -1,22 +1,24 @@ package cmd import ( + "fmt" + "github.com/go-acme/lego/v5/log" "github.com/urfave/cli/v2" ) func Before(ctx *cli.Context) error { if ctx.String(flgPath) == "" { - log.Fatalf("Could not determine current working directory. Please pass --%s.", flgPath) + log.Fatal(fmt.Sprintf("Could not determine the current working directory. Please pass --%s.", flgPath)) } err := createNonExistingFolder(ctx.String(flgPath)) if err != nil { - log.Fatalf("Could not check/create path: %v", err) + log.Fatal("Could not check/create the path.", "flag", flgPath, "filepath", ctx.String(flgPath), "error", err) } if ctx.String(flgServer) == "" { - log.Fatalf("Could not determine current working server. Please pass --%s.", flgServer) + log.Fatal(fmt.Sprintf("Could not determine the current working server. Please pass --%s.", flgServer)) } return nil diff --git a/cmd/cmd_renew.go b/cmd/cmd_renew.go index b2ab9186f..aff22372d 100644 --- a/cmd/cmd_renew.go +++ b/cmd/cmd_renew.go @@ -4,6 +4,7 @@ import ( "crypto" "crypto/x509" "errors" + "fmt" "math/rand" "os" "slices" @@ -42,15 +43,15 @@ func createRenew() *cli.Command { hasCsr := ctx.String(flgCSR) != "" if hasDomains && hasCsr { - log.Fatalf("Please specify either --%s/-d or --%s/-c, but not both", flgDomains, flgCSR) + log.Fatal(fmt.Sprintf("Please specify either --%s/-d or --%s/-c, but not both", flgDomains, flgCSR)) } if !hasDomains && !hasCsr { - log.Fatalf("Please specify --%s/-d (or --%s/-c if you already have a CSR)", flgDomains, flgCSR) + log.Fatal(fmt.Sprintf("Please specify --%s/-d (or --%s/-c if you already have a CSR)", flgDomains, flgCSR)) } if ctx.Bool(flgForceCertDomains) && hasCsr { - log.Fatalf("--%s only works with --%s/-d, --%s/-c doesn't support this option.", flgForceCertDomains, flgDomains, flgCSR) + log.Fatal(fmt.Sprintf("--%s only works with --%s/-d, --%s/-c doesn't support this option.", flgForceCertDomains, flgDomains, flgCSR)) } return nil @@ -137,7 +138,7 @@ func renew(cliCtx *cli.Context) error { account, keyType := setupAccount(cliCtx, NewAccountsStorage(cliCtx)) if account.Registration == nil { - log.Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email) + log.Fatal("The account is not registered. Use 'run' to register a new account.", "email", account.Email) } certsStorage := NewCertificatesStorage(cliCtx) @@ -166,7 +167,7 @@ func renewForDomains(cliCtx *cli.Context, account *Account, keyType certcrypto.K // as web servers would not be able to work with a combined file. certificates, err := certsStorage.ReadCertificate(domain, certExt) if err != nil { - log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err) + log.Fatal("Error while loading the certificate.", "domain", domain, "error", err) } cert := certificates[0] @@ -187,14 +188,14 @@ func renewForDomains(cliCtx *cli.Context, account *Account, keyType certcrypto.K // Figure out if we need to sleep before renewing. if ariRenewalTime.After(now) { - log.Infof("[%s] Sleeping %s until renewal time %s", domain, ariRenewalTime.Sub(now), ariRenewalTime) + log.Info("Sleeping until renewal time", "domain", domain, "sleep", ariRenewalTime.Sub(now), "renewalTime", ariRenewalTime) time.Sleep(ariRenewalTime.Sub(now)) } } replacesCertID, err = certificate.MakeARICertID(cert) if err != nil { - log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err) + log.Fatal("Error while construction the ARI CertID.", "domain", domain, "error", err) } } @@ -213,14 +214,14 @@ func renewForDomains(cliCtx *cli.Context, account *Account, keyType certcrypto.K // This is just meant to be informal for the user. timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours())) + log.Info("acme: Trying renewal.", "domain", domain, "hoursRemaining", int(timeLeft.Hours())) var privateKey crypto.PrivateKey if cliCtx.Bool(flgReuseKey) { keyBytes, errR := certsStorage.ReadFile(domain, keyExt) if errR != nil { - log.Fatalf("Error while loading the private key for domain %s\n\t%v", domain, errR) + log.Fatal("Error while loading the private key.", "domain", domain, "error", errR) } privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes) @@ -238,7 +239,7 @@ func renewForDomains(cliCtx *cli.Context, account *Account, keyType certcrypto.K rnd := rand.New(rand.NewSource(time.Now().UnixNano())) sleepTime := time.Duration(rnd.Int63n(int64(jitter))) - log.Infof("renewal: random delay of %s", sleepTime) + log.Info("renewal: random delay.", "sleep", sleepTime) time.Sleep(sleepTime) } @@ -265,7 +266,7 @@ func renewForDomains(cliCtx *cli.Context, account *Account, keyType certcrypto.K certRes, err := client.Certificate.Obtain(cliCtx.Context, request) if err != nil { - log.Fatal(err) + log.Fatal("Could not obtain the certificate.", "error", err) } certRes.Domain = domain @@ -280,12 +281,12 @@ func renewForDomains(cliCtx *cli.Context, account *Account, keyType certcrypto.K func renewForCSR(cliCtx *cli.Context, account *Account, keyType certcrypto.KeyType, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error { csr, err := readCSRFile(cliCtx.String(flgCSR)) if err != nil { - log.Fatal(err) + log.Fatal("Could not read CSR file.", "flag", flgCSR, "filepath", cliCtx.String(flgCSR), "error", err) } domain, err := certcrypto.GetCSRMainDomain(csr) if err != nil { - log.Fatalf("Error: %v", err) + log.Fatal("Could not get CSR main domain.", "error", err) } // load the cert resource from files. @@ -293,7 +294,7 @@ func renewForCSR(cliCtx *cli.Context, account *Account, keyType certcrypto.KeyTy // as web servers would not be able to work with a combined file. certificates, err := certsStorage.ReadCertificate(domain, certExt) if err != nil { - log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err) + log.Fatal("Error while loading the certificate.", "domain", domain, "error", err) } cert := certificates[0] @@ -314,14 +315,14 @@ func renewForCSR(cliCtx *cli.Context, account *Account, keyType certcrypto.KeyTy // Figure out if we need to sleep before renewing. if ariRenewalTime.After(now) { - log.Infof("[%s] Sleeping %s until renewal time %s", domain, ariRenewalTime.Sub(now), ariRenewalTime) + log.Info("Sleeping until renewal time", "domain", domain, "sleep", ariRenewalTime.Sub(now), "renewalTime", ariRenewalTime) time.Sleep(ariRenewalTime.Sub(now)) } } replacesCertID, err = certificate.MakeARICertID(cert) if err != nil { - log.Fatalf("Error while construction the ARI CertID for domain %s\n\t%v", domain, err) + log.Fatal("Error while construction the ARI CertID.", "domain", domain, "error", err) } } @@ -335,7 +336,7 @@ func renewForCSR(cliCtx *cli.Context, account *Account, keyType certcrypto.KeyTy // This is just meant to be informal for the user. timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours())) + log.Info("acme: Trying renewal.", "domain", domain, "hoursRemaining", int(timeLeft.Hours())) request := certificate.ObtainForCSRRequest{ CSR: csr, @@ -353,7 +354,7 @@ func renewForCSR(cliCtx *cli.Context, account *Account, keyType certcrypto.KeyTy certRes, err := client.Certificate.ObtainForCSR(cliCtx.Context, request) if err != nil { - log.Fatal(err) + log.Fatal("Could not obtain the certificate fro CSR.", "error", err) } certsStorage.SaveResource(certRes) @@ -365,7 +366,7 @@ func renewForCSR(cliCtx *cli.Context, account *Account, keyType certcrypto.KeyTy func needRenewal(x509Cert *x509.Certificate, domain string, days int, dynamic bool) bool { if x509Cert.IsCA { - log.Fatalf("[%s] Certificate bundle starts with a CA certificate", domain) + log.Fatal("Certificate bundle starts with a CA certificate.", "domain", domain) } if dynamic { @@ -381,8 +382,8 @@ func needRenewal(x509Cert *x509.Certificate, domain string, days int, dynamic bo return true } - log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.", - domain, notAfter, days) + log.Infof(log.LazySprintf("Skip renewal: the certificate expires in %d days, the number of days defined to perform the renewal is %d.", + notAfter, days), "domain", domain) return false } @@ -401,8 +402,8 @@ func needRenewalDynamic(x509Cert *x509.Certificate, domain string, now time.Time return true } - log.Infof("[%s] The certificate expires at %s, the renewal can be performed in %s: no renewal.", - domain, x509Cert.NotAfter.Format(time.RFC3339), dueDate.Sub(now)) + log.Infof(log.LazySprintf("Skip renewal: The certificate expires at %s, the renewal can be performed in %s.", + x509Cert.NotAfter.Format(time.RFC3339), dueDate.Sub(now)), "domain", domain) return false } @@ -410,18 +411,17 @@ func needRenewalDynamic(x509Cert *x509.Certificate, domain string, now time.Time // getARIRenewalTime checks if the certificate needs to be renewed using the renewalInfo endpoint. func getARIRenewalTime(cliCtx *cli.Context, cert *x509.Certificate, domain string, client *lego.Client) *time.Time { if cert.IsCA { - log.Fatalf("[%s] Certificate bundle starts with a CA certificate", domain) + log.Fatal("Certificate bundle starts with a CA certificate.", "domain", domain) } renewalInfo, err := client.Certificate.GetRenewalInfo(cliCtx.Context, certificate.RenewalInfoRequest{Cert: cert}) if err != nil { if errors.Is(err, api.ErrNoARI) { - // The server does not advertise a renewal info endpoint. - log.Warnf("[%s] acme: %v", domain, err) + log.Warn("acme: the server does not advertise a renewal info endpoint.", "domain", domain, "errorr", err) return nil } - log.Warnf("[%s] acme: calling renewal info endpoint: %v", domain, err) + log.Warn("acme: calling renewal info endpoint", "domain", domain, "error", err) return nil } @@ -430,14 +430,14 @@ func getARIRenewalTime(cliCtx *cli.Context, cert *x509.Certificate, domain strin renewalTime := renewalInfo.ShouldRenewAt(now, cliCtx.Duration(flgARIWaitToRenewDuration)) if renewalTime == nil { - log.Infof("[%s] acme: renewalInfo endpoint indicates that renewal is not needed", domain) + log.Info("acme: renewalInfo endpoint indicates that renewal is not needed.", "domain", domain) return nil } - log.Infof("[%s] acme: renewalInfo endpoint indicates that renewal is needed", domain) + log.Info("acme: renewalInfo endpoint indicates that renewal is needed.", "domain", domain) if renewalInfo.ExplanationURL != "" { - log.Infof("[%s] acme: renewalInfo endpoint provided an explanation: %s", domain, renewalInfo.ExplanationURL) + log.Info("acme: renewalInfo endpoint provided an explanation.", "domain", domain, "explanationURL", renewalInfo.ExplanationURL) } return renewalTime diff --git a/cmd/cmd_revoke.go b/cmd/cmd_revoke.go index c0b486d3b..9a48c13f9 100644 --- a/cmd/cmd_revoke.go +++ b/cmd/cmd_revoke.go @@ -41,7 +41,7 @@ func revoke(cliCtx *cli.Context) error { account, keyType := setupAccount(cliCtx, NewAccountsStorage(cliCtx)) if account.Registration == nil { - log.Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email) + log.Fatal("Account is not registered. Use 'run' to register a new account.", "email", account.Email) } client := newClient(cliCtx, account, keyType) @@ -50,21 +50,21 @@ func revoke(cliCtx *cli.Context) error { certsStorage.CreateRootFolder() for _, domain := range cliCtx.StringSlice(flgDomains) { - log.Printf("Trying to revoke certificate for domain %s", domain) + log.Info("Trying to revoke the certificate.", "domain", domain) certBytes, err := certsStorage.ReadFile(domain, certExt) if err != nil { - log.Fatalf("Error while revoking the certificate for domain %s\n\t%v", domain, err) + log.Fatal("Error while revoking the certificate.", "domain", domain, "error", err) } reason := cliCtx.Uint(flgReason) err = client.Certificate.RevokeWithReason(cliCtx.Context, certBytes, &reason) if err != nil { - log.Fatalf("Error while revoking the certificate for domain %s\n\t%v", domain, err) + log.Fatal("Error while revoking the certificate.", "domain", domain, "error", err) } - log.Println("Certificate was revoked.") + log.Info("Certificate was revoked.", "domain", domain) if cliCtx.Bool(flgKeep) { return nil @@ -77,7 +77,7 @@ func revoke(cliCtx *cli.Context) error { return err } - log.Println("Certificate was archived for domain:", domain) + log.Info("Certificate was archived", "domain", domain) } return nil diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go index e4a626d56..1c42213c3 100644 --- a/cmd/cmd_run.go +++ b/cmd/cmd_run.go @@ -119,12 +119,12 @@ func run(cliCtx *cli.Context) error { if account.Registration == nil { reg, err := register(cliCtx, client) if err != nil { - log.Fatalf("Could not complete registration\n\t%v", err) + log.Fatal("Could not complete registration.", "error", err) } account.Registration = reg if err = accountsStorage.Save(account); err != nil { - log.Fatal(err) + log.Fatal("Could not save the account file.", "error", err) } fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath()) @@ -137,7 +137,7 @@ func run(cliCtx *cli.Context) error { if err != nil { // Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error. // Due to us not returning partial certificate we can just exit here instead of at the end. - log.Fatalf("Could not obtain certificates:\n\t%v", err) + log.Fatal("Could not obtain certificates", "error", err) } certsStorage.SaveResource(cert) @@ -159,14 +159,14 @@ func handleTOS(ctx *cli.Context, client *lego.Client) bool { reader := bufio.NewReader(os.Stdin) - log.Printf("Please review the TOS at %s", client.GetToSURL()) + log.Warn("Please review the TOS", "url", client.GetToSURL()) for { fmt.Println("Do you accept the TOS? Y/n") text, err := reader.ReadString('\n') if err != nil { - log.Fatalf("Could not read from console: %v", err) + log.Fatal("Could not read from the console", "error", err) } text = strings.Trim(text, "\r\n") @@ -192,7 +192,7 @@ func register(cliCtx *cli.Context, client *lego.Client) (*registration.Resource, hmacEncoded := cliCtx.String(flgHMAC) if kid == "" || hmacEncoded == "" { - log.Fatalf("Requires arguments --%s and --%s.", flgKID, flgHMAC) + log.Fatal(fmt.Sprintf("Requires arguments --%s and --%s.", flgKID, flgHMAC)) } return client.Registration.RegisterWithExternalAccountBinding(cliCtx.Context, registration.RegisterEABOptions{ diff --git a/cmd/lego/main.go b/cmd/lego/main.go index 7fc46dec3..1d03d3896 100644 --- a/cmd/lego/main.go +++ b/cmd/lego/main.go @@ -40,6 +40,6 @@ func main() { err = app.Run(os.Args) if err != nil { - log.Fatal(err) + log.Fatal("Error", "error", err) } } diff --git a/cmd/setup.go b/cmd/setup.go index 2f8660a84..9ff5804b6 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -78,18 +78,18 @@ func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyTy retryClient.Logger = nil if _, v := os.LookupEnv("LEGO_DEBUG_ACME_HTTP_CLIENT"); v { - retryClient.Logger = log.Logger + retryClient.Logger = log.Default() } config.HTTPClient = retryClient.StandardClient() client, err := lego.NewClient(config) if err != nil { - log.Fatalf("Could not create client: %v", err) + log.Fatal("Could not create client.", "error", err) } if client.GetExternalAccountRequired() && !ctx.IsSet(flgEAB) { - log.Fatalf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC) + log.Fatal(fmt.Sprintf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC)) } return client @@ -113,7 +113,7 @@ func getKeyType(ctx *cli.Context) certcrypto.KeyType { return certcrypto.EC384 } - log.Fatalf("Unsupported KeyType: %s", keyType) + log.Fatal("Unsupported KeyType.", "keyType", keyType) return "" } @@ -202,7 +202,7 @@ func checkRetry(ctx context.Context, resp *http.Response, err error) (bool, erro } default: - log.Warnf("retry: %v", errorDetails) + log.Warnf(log.LazySprintf("retry: %v", errorDetails)) return rt, errorDetails } diff --git a/cmd/setup_challenges.go b/cmd/setup_challenges.go index 3c8845eb7..6f7fce708 100644 --- a/cmd/setup_challenges.go +++ b/cmd/setup_challenges.go @@ -21,27 +21,27 @@ import ( func setupChallenges(ctx *cli.Context, client *lego.Client) { if !ctx.Bool(flgHTTP) && !ctx.Bool(flgTLS) && !ctx.IsSet(flgDNS) { - log.Fatalf("No challenge selected. You must specify at least one challenge: `--%s`, `--%s`, `--%s`.", flgHTTP, flgTLS, flgDNS) + log.Fatal(fmt.Sprintf("No challenge selected. You must specify at least one challenge: `--%s`, `--%s`, `--%s`.", flgHTTP, flgTLS, flgDNS)) } if ctx.Bool(flgHTTP) { err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx), http01.SetDelay(ctx.Duration(flgHTTPDelay))) if err != nil { - log.Fatal(err) + log.Fatal("Could not set HTTP challenge provider.", "error", err) } } if ctx.Bool(flgTLS) { err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx), tlsalpn01.SetDelay(ctx.Duration(flgTLSDelay))) if err != nil { - log.Fatal(err) + log.Fatal("Could not set TLS challenge provider.", "error", err) } } if ctx.IsSet(flgDNS) { err := setupDNS(ctx, client) if err != nil { - log.Fatal(err) + log.Fatal("Could not set DNS challenge provider.", "error", err) } } } @@ -52,33 +52,39 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider { case ctx.IsSet(flgHTTPWebroot): ps, err := webroot.NewHTTPProvider(ctx.String(flgHTTPWebroot)) if err != nil { - log.Fatal(err) + log.Fatal("Could not create the webroot provider.", + "flag", flgHTTPWebroot, "webRoot", ctx.String(flgHTTPWebroot), "error", err) } return ps case ctx.IsSet(flgHTTPMemcachedHost): ps, err := memcached.NewMemcachedProvider(ctx.StringSlice(flgHTTPMemcachedHost)) if err != nil { - log.Fatal(err) + log.Fatal("Could not create the memcached provider.", + "flag", flgHTTPMemcachedHost, "memcachedHosts", strings.Join(ctx.StringSlice(flgHTTPMemcachedHost), ", "), "error", err) } return ps case ctx.IsSet(flgHTTPS3Bucket): ps, err := s3.NewHTTPProvider(ctx.String(flgHTTPS3Bucket)) if err != nil { - log.Fatal(err) + log.Fatal("Could not create the S3 provider.", + "flag", flgHTTPS3Bucket, "bucket", ctx.String(flgHTTPS3Bucket), "error", err) } return ps case ctx.IsSet(flgHTTPPort): iface := ctx.String(flgHTTPPort) if !strings.Contains(iface, ":") { - log.Fatalf("The --%s switch only accepts interface:port or :port for its argument.", flgHTTPPort) + log.Fatal( + fmt.Sprintf("The --%s switch only accepts interface:port or :port for its argument.", flgHTTPPort), + "flag", flgHTTPPort, "port", ctx.String(flgHTTPPort), + ) } host, port, err := net.SplitHostPort(iface) if err != nil { - log.Fatal(err) + log.Fatal("Could not split host and port.", "iface", iface, "error", err) } srv := http01.NewProviderServer(host, port) @@ -105,12 +111,12 @@ func setupTLSProvider(ctx *cli.Context) challenge.Provider { case ctx.IsSet(flgTLSPort): iface := ctx.String(flgTLSPort) if !strings.Contains(iface, ":") { - log.Fatalf("The --%s switch only accepts interface:port or :port for its argument.", flgTLSPort) + log.Fatal(fmt.Sprintf("The --%s switch only accepts interface:port or :port for its argument.", flgTLSPort)) } host, port, err := net.SplitHostPort(iface) if err != nil { - log.Fatal(err) + log.Fatal("Could not split host and port.", "iface", iface, "error", err) } return tlsalpn01.NewProviderServer(host, port) @@ -164,7 +170,7 @@ func setupDNS(ctx *cli.Context, client *lego.Client) error { func checkPropagationExclusiveOptions(ctx *cli.Context) error { if ctx.IsSet(flgDNSDisableCP) { - log.Printf("The flag '%s' is deprecated use '%s' instead.", flgDNSDisableCP, flgDNSPropagationDisableANS) + log.Warnf(log.LazySprintf("The flag '%s' is deprecated use '%s' instead.", flgDNSDisableCP, flgDNSPropagationDisableANS)) } if (isSetBool(ctx, flgDNSDisableCP) || isSetBool(ctx, flgDNSPropagationDisableANS)) && ctx.IsSet(flgDNSPropagationWait) { diff --git a/log/lazy.go b/log/lazy.go new file mode 100644 index 000000000..037340784 --- /dev/null +++ b/log/lazy.go @@ -0,0 +1,51 @@ +package log + +import ( + "context" + "fmt" + "log/slog" +) + +type LazyMessage struct { + msg string + args []any +} + +func LazySprintf(msg string, args ...any) LazyMessage { + return LazyMessage{ + msg: msg, + args: args, + } +} + +func (l LazyMessage) String() string { + return fmt.Sprintf(l.msg, l.args...) +} + +// Debugf calls [Logger.Debug] on the default logger. +func Debugf(msg LazyMessage, args ...any) { + logLazy(slog.LevelDebug, msg, args...) +} + +// Infof calls [Logger.Info] on the default logger. +func Infof(msg LazyMessage, args ...any) { + logLazy(slog.LevelInfo, msg, args...) +} + +// Warnf calls [Logger.Warn] on the default logger. +func Warnf(msg LazyMessage, args ...any) { + logLazy(slog.LevelWarn, msg, args...) +} + +// Errorf calls [Logger.Error] on the default logger. +func Errorf(msg LazyMessage, args ...any) { + logLazy(slog.LevelError, msg, args...) +} + +func logLazy(level slog.Level, msg LazyMessage, args ...any) { + ctx := context.Background() + + if Default().Enabled(ctx, level) { + Default().Log(ctx, level, msg.String(), args...) + } +} diff --git a/log/logger.go b/log/logger.go index 2f700a359..e3552702f 100644 --- a/log/logger.go +++ b/log/logger.go @@ -1,59 +1,48 @@ package log import ( - "log" + "log/slog" "os" + "sync/atomic" ) -// Logger is an optional custom logger. -var Logger StdLogger = log.New(os.Stderr, "", log.LstdFlags) +var defaultLogger atomic.Pointer[slog.Logger] -// StdLogger interface for Standard Logger. -type StdLogger interface { - Fatal(args ...any) - Fatalln(args ...any) - Fatalf(format string, args ...any) - Print(args ...any) - Println(args ...any) - Printf(format string, args ...any) +func init() { + defaultLogger.Store(slog.New(slog.NewTextHandler(os.Stdout, nil))) } -// Fatal writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Fatal(args ...any) { - Logger.Fatal(args...) +// Default returns the default [Logger]. +func Default() *slog.Logger { return defaultLogger.Load() } + +// SetDefault makes l the default [Logger], which is used by +// the top-level functions [Info], [Debug] and so on. +func SetDefault(l *slog.Logger) { + defaultLogger.Store(l) } -// Fatalf writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Fatalf(format string, args ...any) { - Logger.Fatalf(format, args...) +// Fatal calls [Logger.Error] on the default logger and exit with code 1. +func Fatal(msg string, args ...any) { + Default().Error(msg, args...) + os.Exit(1) } -// Print writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Print(args ...any) { - Logger.Print(args...) +// Debug calls [Logger.Debug] on the default logger. +func Debug(msg string, args ...any) { + Default().Debug(msg, args...) } -// Println writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Println(args ...any) { - Logger.Println(args...) +// Info calls [Logger.Info] on the default logger. +func Info(msg string, args ...any) { + Default().Info(msg, args...) } -// Printf writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Printf(format string, args ...any) { - Logger.Printf(format, args...) +// Warn calls [Logger.Warn] on the default logger. +func Warn(msg string, args ...any) { + Default().Warn(msg, args...) } -// Warnf writes a log entry. -func Warnf(format string, args ...any) { - Printf("[WARN] "+format, args...) -} - -// Infof writes a log entry. -func Infof(format string, args ...any) { - Printf("[INFO] "+format, args...) +// Error calls [Logger.Error] on the default logger. +func Error(msg string, args ...any) { + Default().Error(msg, args...) } diff --git a/platform/config/env/env.go b/platform/config/env/env.go index b107ed4a8..2368bee9f 100644 --- a/platform/config/env/env.go +++ b/platform/config/env/env.go @@ -160,7 +160,7 @@ func GetOrFile(envVar string) string { fileContents, err := os.ReadFile(fileVarValue) if err != nil { - log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, fileVar, err) + log.Warn("Failed to read the file.", "filepath", fileVarValue, "envVar", fileVar, "error", err) return "" } diff --git a/platform/wait/wait.go b/platform/wait/wait.go index 01e32df04..2ae35cb72 100644 --- a/platform/wait/wait.go +++ b/platform/wait/wait.go @@ -11,7 +11,7 @@ import ( // For polls the given function 'f', once every 'interval', up to 'timeout'. func For(msg string, timeout, interval time.Duration, f func() (bool, error)) error { - log.Infof("Wait for %s [timeout: %s, interval: %s]", msg, timeout, interval) + log.Infof(log.LazySprintf("Wait for %s.", msg), "timeout", timeout, "interval", interval) var lastErr error diff --git a/providers/dns/bluecat/bluecat.go b/providers/dns/bluecat/bluecat.go index 823d85489..81f49141a 100644 --- a/providers/dns/bluecat/bluecat.go +++ b/providers/dns/bluecat/bluecat.go @@ -138,7 +138,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } if d.config.Debug { - log.Infof("fqdn: %s; viewID: %d; ZoneID: %d; zone: %s", info.EffectiveFQDN, viewID, parentZoneID, name) + log.Info("bluecat: debug information.", "fqdn", info.EffectiveFQDN, "viewID", viewID, "zoneID", parentZoneID, "zone", name) } txtRecord := internal.Entity{ diff --git a/providers/dns/cloudflare/cloudflare.go b/providers/dns/cloudflare/cloudflare.go index d9190dce2..b248aafd2 100644 --- a/providers/dns/cloudflare/cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -13,7 +13,6 @@ import ( "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/go-acme/lego/v5/providers/dns/cloudflare/internal" ) @@ -185,8 +184,6 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { d.recordIDs[token] = response.ID d.recordIDsMu.Unlock() - log.Infof("cloudflare: new record for %s, ID %s", domain, response.ID) - return nil } @@ -217,7 +214,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { err = d.client.DeleteDNSRecord(ctx, zoneID, recordID) if err != nil { - log.Printf("cloudflare: failed to delete TXT record: %v", err) + return fmt.Errorf("cloudflare: failed to delete TXT record: %w", err) } // Delete record ID from map diff --git a/providers/dns/cloudns/cloudns.go b/providers/dns/cloudns/cloudns.go index 25b4eb831..651c6756f 100644 --- a/providers/dns/cloudns/cloudns.go +++ b/providers/dns/cloudns/cloudns.go @@ -176,7 +176,7 @@ func (d *DNSProvider) waitNameservers(ctx context.Context, domain string, zone * return fmt.Errorf("nameserver sync on %s: %w", domain, err) } - log.Infof("[%s] Sync %d/%d complete", domain, syncProgress.Updated, syncProgress.Total) + log.Infof(log.LazySprintf("Sync %d/%d complete", syncProgress.Updated, syncProgress.Total), "domain", domain) if !syncProgress.Complete { return fmt.Errorf("nameserver sync on %s not complete", domain) diff --git a/providers/dns/desec/desec.go b/providers/dns/desec/desec.go index 266bbac94..c582be3b3 100644 --- a/providers/dns/desec/desec.go +++ b/providers/dns/desec/desec.go @@ -5,12 +5,12 @@ import ( "context" "errors" "fmt" - "log" "net/http" "time" "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" + "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/go-acme/lego/v5/providers/dns/internal/clientdebug" "github.com/nrdcg/desec" diff --git a/providers/dns/designate/designate.go b/providers/dns/designate/designate.go index 40752cce1..53e9facd5 100644 --- a/providers/dns/designate/designate.go +++ b/providers/dns/designate/designate.go @@ -4,7 +4,6 @@ package designate import ( "errors" "fmt" - "log" "os" "slices" "sync" @@ -12,6 +11,7 @@ import ( "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" + "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" @@ -155,7 +155,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { if existingRecord != nil { if slices.Contains(existingRecord.Records, info.Value) { - log.Printf("designate: the record already exists: %s", info.Value) + log.Debug("designate: the record already exists.", "value", info.Value) return nil } @@ -229,7 +229,7 @@ func (d *DNSProvider) createRecord(zoneID, fqdn, value string) error { func (d *DNSProvider) updateRecord(record *recordsets.RecordSet, value string) error { if slices.Contains(record.Records, value) { - log.Printf("skip: the record already exists: %s", value) + log.Debug("skip: the record already exists.", "value", value) return nil } diff --git a/providers/dns/dynu/internal/client.go b/providers/dns/dynu/internal/client.go index 79224911e..8c8544ce9 100644 --- a/providers/dns/dynu/internal/client.go +++ b/providers/dns/dynu/internal/client.go @@ -122,7 +122,7 @@ func (c *Client) doRetry(ctx context.Context, method, uri string, body []byte, r } notify := func(err error, duration time.Duration) { - log.Printf("client retries because of %v", err) + log.Warn("client retries.", "error", err) } bo := backoff.NewExponentialBackOff() diff --git a/providers/dns/edgedns/edgedns.go b/providers/dns/edgedns/edgedns.go index 934dbb713..18af1b582 100644 --- a/providers/dns/edgedns/edgedns.go +++ b/providers/dns/edgedns/edgedns.go @@ -15,7 +15,6 @@ import ( "github.com/akamai/AkamaiOPEN-edgegrid-golang/v11/pkg/session" "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" ) @@ -158,8 +157,6 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } if record != nil { - log.Infof("TXT record already exists. Updating target") - if containsValue(record.Target, info.Value) { // have a record and have entry already return nil diff --git a/providers/dns/exec/exec.go b/providers/dns/exec/exec.go index 3533dcb25..a6fef30c9 100644 --- a/providers/dns/exec/exec.go +++ b/providers/dns/exec/exec.go @@ -135,7 +135,7 @@ func (d *DNSProvider) run(ctx context.Context, command, domain, token, keyAuth s scanner := bufio.NewScanner(stdout) for scanner.Scan() { - log.Println(scanner.Text()) + log.Info(scanner.Text()) } err = cmd.Wait() diff --git a/providers/dns/exec/exec_test.go b/providers/dns/exec/exec_test.go index 3fef6c339..12f12b3e9 100644 --- a/providers/dns/exec/exec_test.go +++ b/providers/dns/exec/exec_test.go @@ -2,6 +2,7 @@ package exec import ( "fmt" + "log/slog" "os" "strings" "testing" @@ -13,14 +14,14 @@ import ( ) func TestDNSProvider_Present(t *testing.T) { - backupLogger := log.Logger + backupLogger := log.Default() defer func() { - log.Logger = backupLogger + log.SetDefault(backupLogger) }() - logRecorder := &LogRecorder{} - log.Logger = logRecorder + logHandler := &LogHandler{} + log.SetDefault(slog.New(logHandler)) type expected struct { args string @@ -64,8 +65,8 @@ func TestDNSProvider_Present(t *testing.T) { var message string - logRecorder.On("Println", mock.Anything).Run(func(args mock.Arguments) { - message = args.String(0) + logHandler.On("Handle", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + message = args.Get(1).(slog.Record).Message fmt.Fprintln(os.Stdout, "XXX", message) }) @@ -88,14 +89,14 @@ func TestDNSProvider_Present(t *testing.T) { } func TestDNSProvider_CleanUp(t *testing.T) { - backupLogger := log.Logger + backupLogger := log.Default() defer func() { - log.Logger = backupLogger + log.SetDefault(backupLogger) }() - logRecorder := &LogRecorder{} - log.Logger = logRecorder + logHandler := &LogHandler{} + log.SetDefault(slog.New(logHandler)) type expected struct { args string @@ -139,8 +140,8 @@ func TestDNSProvider_CleanUp(t *testing.T) { var message string - logRecorder.On("Println", mock.Anything).Run(func(args mock.Arguments) { - message = args.String(0) + logHandler.On("Handle", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + message = args.Get(1).(slog.Record).Message fmt.Fprintln(os.Stdout, "XXX", message) }) diff --git a/providers/dns/exec/log_mock_test.go b/providers/dns/exec/log_mock_test.go index 65753dcf8..ce768a9a3 100644 --- a/providers/dns/exec/log_mock_test.go +++ b/providers/dns/exec/log_mock_test.go @@ -1,31 +1,30 @@ package exec -import "github.com/stretchr/testify/mock" +import ( + "context" + "log/slog" -type LogRecorder struct { + "github.com/stretchr/testify/mock" +) + +type LogHandler struct { mock.Mock } -func (*LogRecorder) Fatal(args ...any) { +func (l *LogHandler) Enabled(ctx context.Context, level slog.Level) bool { + return true +} + +func (l *LogHandler) Handle(ctx context.Context, record slog.Record) error { + l.Called(ctx, record) + + return nil +} + +func (l *LogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { panic("implement me") } -func (*LogRecorder) Fatalln(args ...any) { - panic("implement me") -} - -func (*LogRecorder) Fatalf(format string, args ...any) { - panic("implement me") -} - -func (*LogRecorder) Print(args ...any) { - panic("implement me") -} - -func (l *LogRecorder) Println(args ...any) { - l.Called(args...) -} - -func (*LogRecorder) Printf(format string, args ...any) { +func (l *LogHandler) WithGroup(name string) slog.Handler { panic("implement me") } diff --git a/providers/dns/gandiv5/gandiv5.go b/providers/dns/gandiv5/gandiv5.go index 1851eae06..9e736f2ad 100644 --- a/providers/dns/gandiv5/gandiv5.go +++ b/providers/dns/gandiv5/gandiv5.go @@ -96,7 +96,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { } if config.APIKey != "" { - log.Print("gandiv5: API Key is deprecated, use Personal Access Token instead") + log.Warn("gandiv5: API Key is deprecated, use Personal Access Token instead") } if config.APIKey == "" && config.PersonalAccessToken == "" { diff --git a/providers/dns/gandiv5/internal/client.go b/providers/dns/gandiv5/internal/client.go index d5aa81b1b..e9f6d892e 100644 --- a/providers/dns/gandiv5/internal/client.go +++ b/providers/dns/gandiv5/internal/client.go @@ -10,7 +10,6 @@ import ( "net/url" "time" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/providers/dns/internal/errutils" ) @@ -99,10 +98,6 @@ func (c *Client) addTXTRecord(ctx context.Context, domain, name string, newRecor return fmt.Errorf("unable to create TXT record for domain %s and name %s: %w", domain, name, err) } - if message.Message != "" { - log.Infof("API response: %s", message.Message) - } - return nil } @@ -121,10 +116,6 @@ func (c *Client) DeleteTXTRecord(ctx context.Context, domain, name string) error return fmt.Errorf("unable to delete TXT record for domain %s and name %s: %w", domain, name, err) } - if message.Message != "" { - log.Infof("API response: %s", message.Message) - } - return nil } diff --git a/providers/dns/gcloud/googlecloud.go b/providers/dns/gcloud/googlecloud.go index 9b1c36dfa..74405616f 100644 --- a/providers/dns/gcloud/googlecloud.go +++ b/providers/dns/gcloud/googlecloud.go @@ -212,7 +212,6 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { rrd = append(rrd, data) if data == info.Value { - log.Printf("skip: the record already exists: %s", info.Value) return nil } } @@ -257,7 +256,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { func (d *DNSProvider) applyChanges(ctx context.Context, zone string, change *gdns.Change) error { if d.config.Debug { data, _ := json.Marshal(change) - log.Printf("change (Create): %s", string(data)) + log.Info("change (Create)", "data", string(data)) } chg, err := d.client.Changes.Create(d.config.Project, zone, change).Do() @@ -283,7 +282,7 @@ func (d *DNSProvider) applyChanges(ctx context.Context, zone string, change *gdn func() error { if d.config.Debug { data, _ := json.Marshal(change) - log.Printf("change (Get): %s", string(data)) + log.Info("change (Get)", "data", string(data)) } chg, err = d.client.Changes.Get(d.config.Project, zone, chgID).Do() diff --git a/providers/dns/hetzner/hetzner.go b/providers/dns/hetzner/hetzner.go index ef21f0ee6..c3410c1c9 100644 --- a/providers/dns/hetzner/hetzner.go +++ b/providers/dns/hetzner/hetzner.go @@ -75,7 +75,7 @@ func NewDNSProvider() (*DNSProvider, error) { return &DNSProvider{provider: provider}, nil case foundAPIKey: - log.Warnf("APIKey (legacy Hetzner DNS API) is deprecated, please use APIToken (Hetzner Cloud API) instead.") + log.Warn("APIKey (legacy Hetzner DNS API) is deprecated, please use APIToken (Hetzner Cloud API) instead.") provider, err := legacy.NewDNSProvider() if err != nil { @@ -118,7 +118,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{provider: provider}, nil case config.APIKey != "": - log.Warnf("%s (legacy Hetzner DNS API) is deprecated, please use %s (Hetzner Cloud API) instead.", EnvAPIKey, EnvAPIToken) + log.Warnf(log.LazySprintf("%s (legacy Hetzner DNS API) is deprecated, please use %s (Hetzner Cloud API) instead.", EnvAPIKey, EnvAPIToken)) cfg := &legacy.Config{ APIKey: config.APIKey, diff --git a/providers/dns/hurricane/internal/client.go b/providers/dns/hurricane/internal/client.go index 0cb546530..e7d476f52 100644 --- a/providers/dns/hurricane/internal/client.go +++ b/providers/dns/hurricane/internal/client.go @@ -5,13 +5,13 @@ import ( "context" "fmt" "io" - "log" "net/http" "net/url" "strings" "sync" "time" + "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/providers/dns/internal/errutils" "golang.org/x/time/rate" ) @@ -108,7 +108,7 @@ func evaluateBody(body, hostname string) error { case codeGood: return nil case codeNoChg: - log.Printf("%s: unchanged content written to TXT record %s", body, hostname) + log.Debug("unchanged content written to TXT record.", "hostname", hostname, "body", body) return nil case codeAbuse: return fmt.Errorf("%s: blocked hostname for abuse: %s", body, hostname) diff --git a/providers/dns/infomaniak/internal/client.go b/providers/dns/infomaniak/internal/client.go index 978f2bb7c..58eaa0889 100644 --- a/providers/dns/infomaniak/internal/client.go +++ b/providers/dns/infomaniak/internal/client.go @@ -9,11 +9,9 @@ import ( "net/http" "net/url" "strconv" - "strings" "time" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/providers/dns/internal/errutils" "golang.org/x/oauth2" ) @@ -72,17 +70,9 @@ func (c *Client) DeleteDNSRecord(ctx context.Context, domainID uint64, recordID // GetDomainByName gets a Domain object from its name. func (c *Client) GetDomainByName(ctx context.Context, name string) (*DNSDomain, error) { - name = dns01.UnFqdn(name) - // Try to find the most specific domain - // starts with the FQDN, then remove each left label until we have a match - for { - i := strings.Index(name, ".") - if i == -1 { - break - } - - domain, err := c.getDomainByName(ctx, name) + for n := range dns01.UnFqdnDomainsSeq(dns01.UnFqdn(name)) { + domain, err := c.getDomainByName(ctx, n) if err != nil { return nil, err } @@ -90,10 +80,6 @@ func (c *Client) GetDomainByName(ctx context.Context, name string) (*DNSDomain, if domain != nil { return domain, nil } - - log.Infof("domain %q not found, trying with %q", name, name[i+1:]) - - name = name[i+1:] } return nil, fmt.Errorf("domain not found %s", name) diff --git a/providers/dns/inwx/inwx.go b/providers/dns/inwx/inwx.go index 30e14d624..6c9973427 100644 --- a/providers/dns/inwx/inwx.go +++ b/providers/dns/inwx/inwx.go @@ -87,7 +87,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { } if config.Sandbox { - log.Infof("inwx: sandbox mode is enabled") + log.Info("inwx: sandbox mode is enabled.") } client := goinwx.NewClient(config.Username, config.Password, &goinwx.ClientOptions{Sandbox: config.Sandbox}) @@ -112,7 +112,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer func() { errL := d.client.Account.Logout() if errL != nil { - log.Infof("inwx: failed to log out: %v", errL) + log.Warn("inwx: failed to log out.", "error", errL) } }() @@ -159,7 +159,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { defer func() { errL := d.client.Account.Logout() if errL != nil { - log.Infof("inwx: failed to log out: %v", errL) + log.Warn("inwx: failed to log out.", "error", errL) } }() @@ -220,7 +220,7 @@ func (d *DNSProvider) twoFactorAuth(info *goinwx.LoginResponse) error { // To avoid using the same TAN twice, we wait until the next TOTP period. sleep := d.computeSleep(time.Now()) if sleep != 0 { - log.Infof("inwx: waiting %s for next TOTP token", sleep) + log.Info("inwx: waiting for the next TOTP token", "sleep", sleep) time.Sleep(sleep) } diff --git a/providers/dns/joker/internal/dmapi/client.go b/providers/dns/joker/internal/dmapi/client.go index 26ee62db7..37e281715 100644 --- a/providers/dns/joker/internal/dmapi/client.go +++ b/providers/dns/joker/internal/dmapi/client.go @@ -15,7 +15,6 @@ import ( "time" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/providers/dns/internal/errutils" ) @@ -45,7 +44,6 @@ type Client struct { token *Token muToken sync.Mutex - Debug bool BaseURL string HTTPClient *http.Client } @@ -90,10 +88,6 @@ func (c *Client) postRequest(ctx context.Context, cmd string, data url.Values) ( data.Set("auth-sid", getSessionID(ctx)) } - if c.Debug { - log.Infof("postRequest:\n\tURL: %q\n\tData: %v", endpoint, data) - } - req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, strings.NewReader(data.Encode())) if err != nil { return nil, fmt.Errorf("unable to create request: %w", err) diff --git a/providers/dns/joker/internal/svc/client.go b/providers/dns/joker/internal/svc/client.go index f3c14b8f7..935caefea 100644 --- a/providers/dns/joker/internal/svc/client.go +++ b/providers/dns/joker/internal/svc/client.go @@ -69,6 +69,8 @@ func (c *Client) SendRequest(ctx context.Context, zone, label, value string) err return errutils.NewHTTPDoError(req, err) } + defer func() { _ = resp.Body.Close() }() + raw, err := io.ReadAll(resp.Body) if err != nil { return errutils.NewReadResponseError(req, resp.StatusCode, err) diff --git a/providers/dns/joker/joker.go b/providers/dns/joker/joker.go index ab6f41198..77488777c 100644 --- a/providers/dns/joker/joker.go +++ b/providers/dns/joker/joker.go @@ -18,7 +18,6 @@ const ( EnvAPIKey = envNamespace + "API_KEY" EnvUsername = envNamespace + "USERNAME" EnvPassword = envNamespace + "PASSWORD" - EnvDebug = envNamespace + "DEBUG" EnvMode = envNamespace + "API_MODE" EnvTTL = envNamespace + "TTL" @@ -35,7 +34,6 @@ const ( // Config is used to configure the creation of the DNSProvider. type Config struct { - Debug bool APIKey string Username string Password string @@ -51,7 +49,6 @@ type Config struct { func NewDefaultConfig() *Config { return &Config{ APIMode: env.GetOrDefaultString(EnvMode, modeDMAPI), - Debug: env.GetOrDefaultBool(EnvDebug, false), TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), diff --git a/providers/dns/joker/provider_dmapi.go b/providers/dns/joker/provider_dmapi.go index 2b136e7f6..68bba5fc6 100644 --- a/providers/dns/joker/provider_dmapi.go +++ b/providers/dns/joker/provider_dmapi.go @@ -8,7 +8,6 @@ import ( "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/go-acme/lego/v5/providers/dns/internal/clientdebug" "github.com/go-acme/lego/v5/providers/dns/joker/internal/dmapi" @@ -62,8 +61,6 @@ func newDmapiProviderConfig(config *Config) (*dmapiProvider, error) { Password: config.Password, }) - client.Debug = config.Debug - if config.HTTPClient != nil { client.HTTPClient = config.HTTPClient } @@ -93,13 +90,9 @@ func (d *dmapiProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("joker: %w", err) } - if d.config.Debug { - log.Infof("[%s] joker: adding TXT record %q to zone %q with value %q", domain, subDomain, zone, info.Value) - } - ctx, err := d.client.CreateAuthenticatedContext(context.Background()) if err != nil { - return err + return fmt.Errorf("joker: create authenticated context: %w", err) } response, err := d.client.GetZone(ctx, zone) @@ -131,13 +124,9 @@ func (d *dmapiProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("joker: %w", err) } - if d.config.Debug { - log.Infof("[%s] joker: removing entry %q from zone %q", domain, subDomain, zone) - } - ctx, err := d.client.CreateAuthenticatedContext(context.Background()) if err != nil { - return err + return fmt.Errorf("joker: create authenticated context: %w", err) } defer func() { diff --git a/providers/dns/joker/provider_svc.go b/providers/dns/joker/provider_svc.go index 0da8f139a..35e1cb7ed 100644 --- a/providers/dns/joker/provider_svc.go +++ b/providers/dns/joker/provider_svc.go @@ -73,7 +73,12 @@ func (d *svcProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("joker: %w", err) } - return d.client.SendRequest(context.Background(), dns01.UnFqdn(zone), subDomain, info.Value) + err = d.client.SendRequest(context.Background(), dns01.UnFqdn(zone), subDomain, info.Value) + if err != nil { + return fmt.Errorf("joker: send request: %w", err) + } + + return nil } // CleanUp removes the TXT record matching the specified parameters. @@ -90,7 +95,12 @@ func (d *svcProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("joker: %w", err) } - return d.client.SendRequest(context.Background(), dns01.UnFqdn(zone), subDomain, "") + err = d.client.SendRequest(context.Background(), dns01.UnFqdn(zone), subDomain, "") + if err != nil { + return fmt.Errorf("joker: send request: %w", err) + } + + return nil } // Sequential All DNS challenges for this provider will be resolved sequentially. diff --git a/providers/dns/liara/liara.go b/providers/dns/liara/liara.go index b055f71d3..3664a5332 100644 --- a/providers/dns/liara/liara.go +++ b/providers/dns/liara/liara.go @@ -106,7 +106,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { retryClient.HTTPClient = config.HTTPClient } - retryClient.Logger = log.Logger + retryClient.Logger = log.Default() client := internal.NewClient( clientdebug.Wrap( diff --git a/providers/dns/namecheap/internal/ip.go b/providers/dns/namecheap/internal/ip.go index ac53b74f7..b1338ea2e 100644 --- a/providers/dns/namecheap/internal/ip.go +++ b/providers/dns/namecheap/internal/ip.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "github.com/go-acme/lego/v5/log" + "github.com/go-acme/lego/v5/providers/dns/internal/clientdebug" "github.com/go-acme/lego/v5/providers/dns/internal/errutils" ) @@ -15,11 +15,13 @@ const getIPURL = "https://dynamicdns.park-your-domain.com/getip" // GetClientIP returns the client's public IP address. // It uses namecheap's IP discovery service to perform the lookup. -func GetClientIP(ctx context.Context, client *http.Client, debug bool) (addr string, err error) { +func GetClientIP(ctx context.Context, client *http.Client) (addr string, err error) { if client == nil { client = &http.Client{Timeout: 5 * time.Second} } + client = clientdebug.Wrap(client) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, getIPURL, http.NoBody) if err != nil { return "", fmt.Errorf("unable to create request: %w", err) @@ -37,9 +39,5 @@ func GetClientIP(ctx context.Context, client *http.Client, debug bool) (addr str return "", errutils.NewReadResponseError(req, resp.StatusCode, err) } - if debug { - log.Println("Client IP:", string(clientIP)) - } - return string(clientIP), nil } diff --git a/providers/dns/namecheap/namecheap.go b/providers/dns/namecheap/namecheap.go index 26c723dd3..8abedaa49 100644 --- a/providers/dns/namecheap/namecheap.go +++ b/providers/dns/namecheap/namecheap.go @@ -39,7 +39,6 @@ const ( EnvAPIKey = envNamespace + "API_KEY" EnvSandbox = envNamespace + "SANDBOX" - EnvDebug = envNamespace + "DEBUG" EnvTTL = envNamespace + "TTL" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" @@ -51,7 +50,6 @@ var _ challenge.ProviderTimeout = (*DNSProvider)(nil) // Config is used to configure the creation of the DNSProvider. type Config struct { - Debug bool BaseURL string APIUser string APIKey string @@ -71,7 +69,6 @@ func NewDefaultConfig() *Config { return &Config{ BaseURL: baseURL, - Debug: env.GetOrDefaultBool(EnvDebug, false), TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, time.Hour), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 15*time.Second), @@ -115,7 +112,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { } if config.ClientIP == "" { - clientIP, err := internal.GetClientIP(context.Background(), config.HTTPClient, config.Debug) + clientIP, err := internal.GetClientIP(context.Background(), config.HTTPClient) if err != nil { return nil, fmt.Errorf("namecheap: %w", err) } @@ -166,10 +163,8 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { records = append(records, record) - if d.config.Debug { - for _, h := range records { - log.Printf("%-5.5s %-30.30s %-6s %-70.70s", h.Type, h.Name, h.TTL, h.Address) - } + for _, h := range records { + log.Debugf(log.LazySprintf("%-5.5s %-30.30s %-6s %-70.70s", h.Type, h.Name, h.TTL, h.Address)) } err = d.client.SetHosts(ctx, pr.sld, pr.tld, records) diff --git a/providers/dns/netcup/netcup.go b/providers/dns/netcup/netcup.go index bcaea28eb..6e40d0e2e 100644 --- a/providers/dns/netcup/netcup.go +++ b/providers/dns/netcup/netcup.go @@ -119,7 +119,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer func() { err = d.client.Logout(ctx) if err != nil { - log.Printf("netcup: %v", err) + log.Warn("netcup: failed to logout.", "error", err) } }() @@ -135,7 +135,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { records, err := d.client.GetDNSRecords(ctx, zone) if err != nil { // skip no existing records - log.Infof("no existing records, error ignored: %v", err) + log.Info("No existing records, error ignored.", "zone", zone, "error", err) } records = append(records, record) @@ -165,7 +165,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { defer func() { err = d.client.Logout(ctx) if err != nil { - log.Printf("netcup: %v", err) + log.Warn("netcup: failed to logout.", "error", err) } }() diff --git a/providers/dns/ns1/ns1.go b/providers/dns/ns1/ns1.go index e6b01376a..f12f6b5df 100644 --- a/providers/dns/ns1/ns1.go +++ b/providers/dns/ns1/ns1.go @@ -9,7 +9,6 @@ import ( "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/go-acme/lego/v5/providers/dns/internal/clientdebug" "gopkg.in/ns1/ns1-go.v2/rest" @@ -104,8 +103,6 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // Create a new record if errors.Is(err, rest.ErrRecordMissing) || record == nil { - log.Infof("Create a new record for [zone: %s, fqdn: %s, domain: %s]", zone.Zone, info.EffectiveFQDN, domain) - // Work through a bug in the NS1 API library that causes 400 Input validation failed (Value None for field '.filters' is not of type ...) // So the `tags` and `blockedTags` parameters should be initialized to empty. record = dns.NewRecord(zone.Zone, dns01.UnFqdn(info.EffectiveFQDN), "TXT", make(map[string]string), make([]string, 0)) @@ -114,24 +111,22 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { _, err = d.client.Records.Create(record) if err != nil { - return fmt.Errorf("ns1: failed to create record [zone: %q, fqdn: %q]: %w", zone.Zone, info.EffectiveFQDN, err) + return fmt.Errorf("ns1: create record [zone: %q, fqdn: %q]: %w", zone.Zone, info.EffectiveFQDN, err) } return nil } if err != nil { - return fmt.Errorf("ns1: failed to get the existing record: %w", err) + return fmt.Errorf("ns1: get the existing record: %w", err) } // Update the existing records record.Answers = append(record.Answers, &dns.Answer{Rdata: []string{info.Value}}) - log.Infof("Update an existing record for [zone: %s, fqdn: %s, domain: %s]", zone.Zone, info.EffectiveFQDN, domain) - _, err = d.client.Records.Update(record) if err != nil { - return fmt.Errorf("ns1: failed to update record [zone: %q, fqdn: %q]: %w", zone.Zone, info.EffectiveFQDN, err) + return fmt.Errorf("ns1: update record [zone: %q, fqdn: %q]: %w", zone.Zone, info.EffectiveFQDN, err) } return nil @@ -150,7 +145,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { _, err = d.client.Records.Delete(zone.Zone, name, "TXT") if err != nil { - return fmt.Errorf("ns1: failed to delete record [zone: %q, domain: %q]: %w", zone.Zone, name, err) + return fmt.Errorf("ns1: delete record [zone: %q, domain: %q]: %w", zone.Zone, name, err) } return nil @@ -172,7 +167,7 @@ func (d *DNSProvider) getHostedZone(fqdn string) (*dns.Zone, error) { zone, _, err := d.client.Zones.Get(authZone, false) if err != nil { - return nil, fmt.Errorf("failed to get zone [authZone: %q, fqdn: %q]: %w", authZone, fqdn, err) + return nil, fmt.Errorf("get zone [authZone: %q, fqdn: %q]: %w", authZone, fqdn, err) } return zone, nil diff --git a/providers/dns/octenium/octenium.go b/providers/dns/octenium/octenium.go index 775a4e6c7..9bf1ebf83 100644 --- a/providers/dns/octenium/octenium.go +++ b/providers/dns/octenium/octenium.go @@ -91,7 +91,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { retryClient := retryablehttp.NewClient() retryClient.RetryMax = 5 retryClient.HTTPClient = client.HTTPClient - retryClient.Logger = log.Logger + retryClient.Logger = log.Default() client.HTTPClient = clientdebug.Wrap(retryClient.StandardClient()) diff --git a/providers/dns/oraclecloud/configurationprovider.go b/providers/dns/oraclecloud/configurationprovider.go index d1dd29a76..7acee8cec 100644 --- a/providers/dns/oraclecloud/configurationprovider.go +++ b/providers/dns/oraclecloud/configurationprovider.go @@ -133,7 +133,7 @@ func getEnvFileWithStrictFallback(keys ...string) []byte { fileContents, err := os.ReadFile(fileVarValue) if err != nil { - log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, key, err) + log.Debug("Failed to read the file.", "filepath", fileVarValue, "envVar", key, "error", err) return nil } diff --git a/providers/dns/pdns/pdns.go b/providers/dns/pdns/pdns.go index e69f65b1a..08608956a 100644 --- a/providers/dns/pdns/pdns.go +++ b/providers/dns/pdns/pdns.go @@ -113,7 +113,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config.APIVersion <= 0 { err := client.SetAPIVersion(context.Background()) if err != nil { - log.Warnf("pdns: failed to get API version %v", err) + log.Warn("pdns: failed to get API version.", "error", err) } } diff --git a/providers/dns/stackpath/stackpath.go b/providers/dns/stackpath/stackpath.go index bd6c09878..34db07b87 100644 --- a/providers/dns/stackpath/stackpath.go +++ b/providers/dns/stackpath/stackpath.go @@ -10,7 +10,6 @@ import ( "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/go-acme/lego/v5/providers/dns/internal/clientdebug" "github.com/go-acme/lego/v5/providers/dns/stackpath/internal" @@ -105,7 +104,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { zone, err := d.client.GetZones(ctx, info.EffectiveFQDN) if err != nil { - return fmt.Errorf("stackpath: %w", err) + return fmt.Errorf("stackpath: get zones: %w", err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.Domain) @@ -120,7 +119,12 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Data: info.Value, } - return d.client.CreateZoneRecord(ctx, zone, record) + err = d.client.CreateZoneRecord(ctx, zone, record) + if err != nil { + return fmt.Errorf("stackpath: create zone record: %w", err) + } + + return nil } // CleanUp removes the TXT record matching the specified parameters. @@ -131,7 +135,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { zone, err := d.client.GetZones(ctx, info.EffectiveFQDN) if err != nil { - return fmt.Errorf("stackpath: %w", err) + return fmt.Errorf("stackpath: get zones: %w", err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.Domain) @@ -141,13 +145,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { records, err := d.client.GetZoneRecords(ctx, subDomain, zone) if err != nil { - return err + return fmt.Errorf("stackpath: get zone records: %w", err) } for _, record := range records { err = d.client.DeleteZoneRecord(ctx, zone, record) if err != nil { - log.Printf("stackpath: failed to delete TXT record: %v", err) + return fmt.Errorf("stackpath: delete zone record: %w", err) } } diff --git a/providers/dns/variomedia/variomedia.go b/providers/dns/variomedia/variomedia.go index 1325972a0..df9e2da52 100644 --- a/providers/dns/variomedia/variomedia.go +++ b/providers/dns/variomedia/variomedia.go @@ -13,7 +13,6 @@ import ( "github.com/cenkalti/backoff/v5" "github.com/go-acme/lego/v5/challenge" "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/log" "github.com/go-acme/lego/v5/platform/config/env" "github.com/go-acme/lego/v5/platform/wait" "github.com/go-acme/lego/v5/providers/dns/internal/clientdebug" @@ -195,8 +194,6 @@ func (d *DNSProvider) waitJob(ctx context.Context, domain, id string) error { return fmt.Errorf("apply change on %s: %w", domain, err) } - log.Infof("variomedia: [%s] %s: %s %s", domain, result.Data.ID, result.Data.Attributes.JobType, result.Data.Attributes.Status) - if result.Data.Attributes.Status != "done" { return fmt.Errorf("apply change on %s: status: %s", domain, result.Data.Attributes.Status) } diff --git a/registration/registar.go b/registration/registar.go index 87c5e2759..2b091e2dc 100644 --- a/registration/registar.go +++ b/registration/registar.go @@ -54,7 +54,7 @@ func (r *Registrar) Register(ctx context.Context, options RegisterOptions) (*Res } if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) + log.Info("acme: Registering the account.", "email", r.user.GetEmail()) accMsg.Contact = []string{mailTo + r.user.GetEmail()} } @@ -78,7 +78,7 @@ func (r *Registrar) RegisterWithExternalAccountBinding(ctx context.Context, opti } if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) + log.Info("acme: Registering the account.", "email", r.user.GetEmail()) accMsg.Contact = []string{mailTo + r.user.GetEmail()} } @@ -104,7 +104,7 @@ func (r *Registrar) QueryRegistration(ctx context.Context) (*Resource, error) { } // Log the URL here instead of the email as the email may not be set - log.Infof("acme: Querying account for %s", r.user.GetRegistration().URI) + log.Info("acme: Querying the account.", "registrationURI", r.user.GetRegistration().URI) account, err := r.core.Accounts.Get(ctx, r.user.GetRegistration().URI) if err != nil { @@ -130,7 +130,7 @@ func (r *Registrar) UpdateRegistration(ctx context.Context, options RegisterOpti } if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) + log.Info("acme: Registering the account.", "email", r.user.GetEmail()) accMsg.Contact = []string{mailTo + r.user.GetEmail()} } @@ -150,7 +150,7 @@ func (r *Registrar) DeleteRegistration(ctx context.Context) error { return errors.New("acme: cannot unregister a nil client or user") } - log.Infof("acme: Deleting account for %s", r.user.GetEmail()) + log.Info("acme: Deleting the account.", "email", r.user.GetEmail()) return r.core.Accounts.Deactivate(ctx, r.user.GetRegistration().URI) } @@ -158,7 +158,7 @@ func (r *Registrar) DeleteRegistration(ctx context.Context) error { // ResolveAccountByKey will attempt to look up an account using the given account key // and return its registration resource. func (r *Registrar) ResolveAccountByKey(ctx context.Context) (*Resource, error) { - log.Infof("acme: Trying to resolve account by key") + log.Info("acme: Trying to resolve the account by key") accMsg := acme.Account{OnlyReturnExisting: true}