diff --git a/acme/api/api.go b/acme/api/api.go index 961bd79bc..0f256fc34 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.Warn("Retry.", "error", err) + log.Warn("Retry.", log.ErrorAttr(err)) } return backoff.Retry(ctx, operation, diff --git a/certificate/authorization.go b/certificate/authorization.go index be0828ab1..deec89db8 100644 --- a/certificate/authorization.go +++ b/certificate/authorization.go @@ -2,6 +2,7 @@ package certificate import ( "context" + "log/slog" "time" "github.com/go-acme/lego/v5/acme" @@ -41,7 +42,10 @@ func (c *Certifier) getAuthorizations(ctx context.Context, order acme.ExtendedOr } for i, auth := range order.Authorizations { - log.Info("Authorization", "url", order.Identifiers[i].Value, "authz", auth) + log.Info("Authorization", + slog.String("url", order.Identifiers[i].Value), + slog.String("authz", auth), + ) } close(resc) @@ -54,19 +58,24 @@ 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.Info("Unable to get the authorization.", "url", authzURL, "error", err) + log.Info("Unable to get the authorization.", + slog.String("url", authzURL), + log.ErrorAttr(err), + ) + continue } if auth.Status == acme.StatusValid && !force { - log.Info("Skipping deactivating of valid authorization.", "url", authzURL) + log.Info("Skipping deactivating of valid authorization.", slog.String("url", authzURL)) + continue } - log.Info("Deactivating authorization.", "url", authzURL) + log.Info("Deactivating authorization.", slog.String("url", authzURL)) if c.core.Authorizations.Deactivate(ctx, authzURL) != nil { - log.Info("Unable to deactivate the authorization.", "url", authzURL) + log.Info("Unable to deactivate the authorization.", slog.String("url", authzURL)) } } } diff --git a/certificate/certificates.go b/certificate/certificates.go index b6283a87a..270cb4803 100644 --- a/certificate/certificates.go +++ b/certificate/certificates.go @@ -9,8 +9,8 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" - "strings" "time" "github.com/go-acme/lego/v5/acme" @@ -165,9 +165,9 @@ func (c *Certifier) Obtain(ctx context.Context, request ObtainRequest) (*Resourc domains := sanitizeDomain(request.Domains) if request.Bundle { - log.Info("acme: Obtaining bundled SAN certificate.", "domains", strings.Join(domains, ", ")) + log.Info("acme: Obtaining bundled SAN certificate.", log.DomainsAttr(domains)) } else { - log.Info("acme: Obtaining SAN certificate.", "domains", strings.Join(domains, ", ")) + log.Info("acme: Obtaining SAN certificate.", log.DomainsAttr(domains)) } orderOpts := &api.OrderOptions{ @@ -196,7 +196,7 @@ func (c *Certifier) Obtain(ctx context.Context, request ObtainRequest) (*Resourc return nil, err } - log.Info("acme: Validations succeeded; requesting certificates.", "domains", strings.Join(domains, ", ")) + log.Info("acme: Validations succeeded; requesting certificates.", log.DomainsAttr(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.Info("acme: Obtaining bundled SAN certificate given a CSR.", "domains", strings.Join(domains, ", ")) + log.Info("acme: Obtaining bundled SAN certificate given a CSR.", log.DomainsAttr(domains)) } else { - log.Info("acme: Obtaining SAN certificate given a CSR.", "domains", strings.Join(domains, ", ")) + log.Info("acme: Obtaining SAN certificate given a CSR.", log.DomainsAttr(domains)) } orderOpts := &api.OrderOptions{ @@ -264,7 +264,7 @@ func (c *Certifier) ObtainForCSR(ctx context.Context, request ObtainForCSRReques return nil, err } - log.Info("acme: Validations succeeded; requesting certificates.", "domains", strings.Join(domains, ", ")) + log.Info("acme: Validations succeeded; requesting certificates.", log.DomainsAttr(domains)) failures := newObtainError() @@ -414,7 +414,7 @@ func (c *Certifier) checkResponse(ctx context.Context, order acme.ExtendedOrder, certRes.CertStableURL = order.Certificate if preferredChain == "" { - log.Info("Server responded with a certificate.", "domain", certRes.Domain) + log.Info("Server responded with a certificate.", log.DomainAttr(certRes.Domain)) return true, nil } @@ -426,7 +426,10 @@ func (c *Certifier) checkResponse(ctx context.Context, order acme.ExtendedOrder, } if ok { - log.Info("Server responded with a certificate.", "domain", certRes.Domain, "preferredChain", preferredChain) + log.Info("Server responded with a certificate.", + log.DomainAttr(certRes.Domain), + slog.String("preferredChain", preferredChain), + ) certRes.IssuerCertificate = cert.Issuer certRes.Certificate = cert.Cert @@ -437,7 +440,9 @@ func (c *Certifier) checkResponse(ctx context.Context, order acme.ExtendedOrder, } } - 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) + 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.", + slog.String("preferredChain", preferredChain), + ) return true, nil } @@ -529,7 +534,10 @@ 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.Info("acme: Trying renewal.", "domain", certRes.Domain, "hoursRemaining", int(timeLeft.Hours())) + log.Info("acme: Trying renewal.", + log.DomainAttr(certRes.Domain), + slog.Int("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 +750,10 @@ func sanitizeDomain(domains []string) []string { for _, domain := range domains { sanitizedDomain, err := idna.ToASCII(domain) if err != nil { - log.Warn("skip domain: unable to sanitize (punnycode).", "domain", domain, "error", err) + log.Warn("skip domain: unable to sanitize (punnycode).", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } else { sanitizedDomains = append(sanitizedDomains, sanitizedDomain) } diff --git a/challenge/dns01/client_cname.go b/challenge/dns01/client_cname.go index 743264e8a..d7115d091 100644 --- a/challenge/dns01/client_cname.go +++ b/challenge/dns01/client_cname.go @@ -2,6 +2,7 @@ package dns01 import ( "context" + "log/slog" "slices" "strings" @@ -27,7 +28,10 @@ func (c *Client) lookupCNAME(ctx context.Context, fqdn string) string { break } - log.Info("Found CNAME entry.", "fqdn", fqdn, "cname", cname) + log.Info("Found CNAME entry.", + slog.String("fqdn", fqdn), + slog.String("cname", cname), + ) fqdn = cname } diff --git a/challenge/dns01/dns_challenge.go b/challenge/dns01/dns_challenge.go index 9956562f0..f7533a322 100644 --- a/challenge/dns01/dns_challenge.go +++ b/challenge/dns01/dns_challenge.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/base64" "fmt" + "log/slog" "os" "strconv" "strings" @@ -49,7 +50,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov for _, opt := range opts { err := opt(chlg) if err != nil { - log.Warn("Challenge option skipped.", "error", err) + log.Warn("Challenge option skipped.", log.ErrorAttr(err)) } } @@ -60,7 +61,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.Info("acme: Preparing to solve DNS-01.", "domain", domain) + log.Info("acme: Preparing to solve DNS-01.", log.DomainAttr(domain)) chlng, err := challenge.FindChallenge(challenge.DNS01, authz) if err != nil { @@ -87,7 +88,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.Info("acme: Trying to solve DNS-01.", "domain", domain) + log.Info("acme: Trying to solve DNS-01.", log.DomainAttr(domain)) chlng, err := challenge.FindChallenge(challenge.DNS01, authz) if err != nil { @@ -112,14 +113,16 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { } log.Info("acme: Checking DNS record propagation.", - "domain", domain, "nameservers", strings.Join(DefaultClient().recursiveNameservers, ",")) + log.DomainAttr(domain), + slog.String("nameservers", strings.Join(DefaultClient().recursiveNameservers, ",")), + ) time.Sleep(interval) err = wait.For("propagation", timeout, interval, func() (bool, error) { stop, errP := c.preCheck.call(ctx, domain, info.EffectiveFQDN, info.Value) if !stop || errP != nil { - log.Info("acme: Waiting for DNS record propagation.", "domain", domain) + log.Info("acme: Waiting for DNS record propagation.", log.DomainAttr(domain)) } return stop, errP @@ -135,7 +138,7 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { // CleanUp cleans the challenge. func (c *Challenge) CleanUp(ctx context.Context, authz acme.Authorization) error { - log.Info("acme: Cleaning DNS-01 challenge.", "domain", challenge.GetTargetedDomain(authz)) + log.Info("acme: Cleaning DNS-01 challenge.", log.DomainAttr(challenge.GetTargetedDomain(authz))) chlng, err := challenge.FindChallenge(challenge.DNS01, authz) if err != nil { diff --git a/challenge/http01/http_challenge.go b/challenge/http01/http_challenge.go index c27ae12b5..c8610136c 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.Warn("Challenge option skipped.", "error", err) + log.Warn("Challenge option skipped.", log.ErrorAttr(err)) } } @@ -54,7 +54,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { domain := challenge.GetTargetedDomain(authz) - log.Info("acme: Trying to solve HTTP-01.", "domain", domain) + log.Info("acme: Trying to solve HTTP-01.", log.DomainAttr(domain)) chlng, err := challenge.FindChallenge(challenge.HTTP01, authz) if err != nil { @@ -75,7 +75,7 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { defer func() { err := c.provider.CleanUp(ctx, authz.Identifier.Value, chlng.Token, keyAuth) if err != nil { - log.Warn("acme: cleaning up failed.", "domain", domain, "error", err) + log.Warn("acme: cleaning up failed.", log.DomainAttr(domain), log.ErrorAttr(err)) } }() diff --git a/challenge/http01/http_challenge_server.go b/challenge/http01/http_challenge_server.go index 7350f6213..79b2a79d5 100644 --- a/challenge/http01/http_challenge_server.go +++ b/challenge/http01/http_challenge_server.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/fs" + "log/slog" "net" "net/http" "net/textproto" @@ -154,15 +155,15 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) { return } - log.Info("Served key authentication.", "domain", domain) + log.Info("Served key authentication.", log.DomainAttr(domain)) return } 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(), + log.DomainAttr(r.Host), + slog.String("method", r.Method), + slog.String("header", s.matcher.name()), ) _, err := w.Write([]byte("TEST")) @@ -180,7 +181,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.Warn("HTTP server serve.", "error", err) + log.Warn("HTTP server serve.", log.ErrorAttr(err)) } s.done <- true diff --git a/challenge/resolver/prober.go b/challenge/resolver/prober.go index f36f8e1f8..ee69f5a80 100644 --- a/challenge/resolver/prober.go +++ b/challenge/resolver/prober.go @@ -3,6 +3,7 @@ package resolver import ( "context" "fmt" + "log/slog" "time" "github.com/go-acme/lego/v5/acme" @@ -63,7 +64,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.Info("acme: authorization already valid; skipping challenge.", "domain", domain) + log.Info("acme: authorization already valid; skipping challenge.", log.DomainAttr(domain)) continue } @@ -130,7 +131,7 @@ func sequentialSolve(ctx context.Context, authSolvers []*selectedAuthSolver, fai if len(authSolvers)-1 > i { solvr := authSolver.solver.(sequential) _, interval := solvr.Sequential() - log.Info("sequence: wait.", "interval", interval) + log.Info("sequence: wait.", slog.Duration("interval", interval)) time.Sleep(interval) } } @@ -178,7 +179,7 @@ func cleanUp(ctx context.Context, solvr solver, authz acme.Authorization) { err := solvr.CleanUp(ctx, authz) if err != nil { - log.Warn("acme: cleaning up failed.", "domain", domain, "error", err) + log.Warn("acme: cleaning up failed.", log.DomainAttr(domain), log.ErrorAttr(err)) } } } diff --git a/challenge/resolver/solver_manager.go b/challenge/resolver/solver_manager.go index e287a9ae4..6d026bd16 100644 --- a/challenge/resolver/solver_manager.go +++ b/challenge/resolver/solver_manager.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "sort" "strconv" "time" @@ -68,11 +69,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.Info("acme: use solver.", "domain", domain, "type", chlg.Type) + log.Info("acme: use solver.", log.DomainAttr(domain), slog.String("type", chlg.Type)) return solvr } - log.Info("acme: Could not find the solver.", "domain", domain, "type", chlg.Type) + log.Info("acme: Could not find the solver.", log.DomainAttr(domain), slog.String("type", chlg.Type)) } return nil @@ -90,7 +91,7 @@ func validate(ctx context.Context, core *api.Core, domain string, chlg acme.Chal } if valid { - log.Info("The server validated our request.", "domain", domain) + log.Info("The server validated our request.", log.DomainAttr(domain)) return nil } @@ -123,7 +124,7 @@ func validate(ctx context.Context, core *api.Core, domain string, chlg acme.Chal } if valid { - log.Info("The server validated our request.", "domain", domain) + log.Info("The server validated our request.", log.DomainAttr(domain)) return nil } diff --git a/challenge/tlsalpn01/tls_alpn_challenge.go b/challenge/tlsalpn01/tls_alpn_challenge.go index 5c214567c..00aa23100 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.Warn("Challenge option skipped.", "error", err) + log.Warn("Challenge option skipped.", log.ErrorAttr(err)) } } @@ -60,7 +60,7 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov // 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.Info("acme: Trying to solve TLS-ALPN-01.", "domain", challenge.GetTargetedDomain(authz)) + log.Info("acme: Trying to solve TLS-ALPN-01.", log.DomainAttr(challenge.GetTargetedDomain(authz))) chlng, err := challenge.FindChallenge(challenge.TLSALPN01, authz) if err != nil { @@ -81,7 +81,7 @@ func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error { defer func() { err := c.provider.CleanUp(ctx, domain, chlng.Token, keyAuth) if err != nil { - log.Warn("acme: cleaning up failed.", "domain", challenge.GetTargetedDomain(authz), err) + log.Warn("acme: cleaning up failed.", log.DomainAttr(challenge.GetTargetedDomain(authz)), log.ErrorAttr(err)) } }() diff --git a/challenge/tlsalpn01/tls_alpn_challenge_server.go b/challenge/tlsalpn01/tls_alpn_challenge_server.go index 583c977ee..46d774dcf 100644 --- a/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ b/challenge/tlsalpn01/tls_alpn_challenge_server.go @@ -94,7 +94,7 @@ func (s *ProviderServer) Present(ctx context.Context, domain, token, keyAuth str go func() { err := http.Serve(s.listener, nil) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Warn("HTTP server serve.", "error", err) + log.Warn("HTTP server serve.", log.ErrorAttr(err)) } }() diff --git a/cmd/accounts_storage.go b/cmd/accounts_storage.go index 5ce6828de..1e4a31a19 100644 --- a/cmd/accounts_storage.go +++ b/cmd/accounts_storage.go @@ -5,6 +5,7 @@ import ( "crypto" "encoding/json" "encoding/pem" + "log/slog" "net/url" "os" "path/filepath" @@ -80,7 +81,11 @@ func NewAccountsStorage(cmd *cli.Command) *AccountsStorage { serverURL, err := url.Parse(cmd.String(flgServer)) if err != nil { - log.Fatal("URL parsing", "flag", flgServer, "serverURL", cmd.String(flgServer), "error", err) + log.Fatal("URL parsing", + slog.String("flag", flgServer), + slog.String("serverURL", cmd.String(flgServer)), + log.ErrorAttr(err), + ) } rootPath := filepath.Join(cmd.String(flgPath), baseAccountsRootFolderName) @@ -104,7 +109,10 @@ func (s *AccountsStorage) ExistsAccountFilePath() bool { if _, err := os.Stat(accountFile); os.IsNotExist(err) { return false } else if err != nil { - log.Fatal("Could not read the account file.", "filepath", accountFile, "error", err) + log.Fatal("Could not read the account file.", + slog.String("filepath", accountFile), + log.ErrorAttr(err), + ) } return true @@ -138,14 +146,20 @@ func (s *AccountsStorage) Save(account *Account) error { func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.PrivateKey) *Account { fileBytes, err := os.ReadFile(s.accountFilePath) if err != nil { - log.Fatal("Could not load the account file.", "userID", s.GetUserID(), "error", err) + log.Fatal("Could not load the account file.", + slog.String("userID", s.GetUserID()), + log.ErrorAttr(err), + ) } var account Account err = json.Unmarshal(fileBytes, &account) if err != nil { - log.Fatal("Could not parse the account file.", "userID", s.GetUserID(), "error", err) + log.Fatal("Could not parse the account file.", + slog.String("userID", s.GetUserID()), + log.ErrorAttr(err), + ) } account.key = privateKey @@ -153,14 +167,20 @@ func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.Pri if account.Registration == nil || account.Registration.Body.Status == "" { reg, err := tryRecoverRegistration(ctx, s.cmd, privateKey) if err != nil { - log.Fatal("Could not load the account file. Registration is nil.", "userID", s.GetUserID(), "error", err) + log.Fatal("Could not load the account file. Registration is nil.", + slog.String("userID", s.GetUserID()), + log.ErrorAttr(err), + ) } account.Registration = reg err = s.Save(&account) if err != nil { - log.Fatal("Could not save the account file. Registration is nil.", "userID", s.GetUserID(), "error", err) + log.Fatal("Could not save the account file. Registration is nil.", + slog.String("userID", s.GetUserID()), + log.ErrorAttr(err), + ) } } @@ -171,22 +191,31 @@ 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.Info("No key found for the account. Generating a new private key.", "userID", s.GetUserID(), "keyType", keyType) + log.Info("No key found for the account. Generating a new private key.", + slog.String("userID", s.GetUserID()), + slog.Any("keyType", keyType), + ) s.createKeysFolder() privateKey, err := generatePrivateKey(accKeyPath, keyType) if err != nil { - log.Fatal("Could not generate the RSA private account key.", "userID", s.GetUserID(), "error", err) + log.Fatal("Could not generate the RSA private account key.", + slog.String("userID", s.GetUserID()), + log.ErrorAttr(err), + ) } - log.Info("Saved key.", "filepath", accKeyPath) + log.Info("Saved key.", slog.String("filepath", accKeyPath)) return privateKey } privateKey, err := loadPrivateKey(accKeyPath) if err != nil { - log.Fatal("Could not load an RSA private key from the file.", "filepath", accKeyPath, "error", err) + log.Fatal("Could not load an RSA private key from the file.", + slog.String("filepath", accKeyPath), + log.ErrorAttr(err), + ) } return privateKey @@ -194,7 +223,10 @@ func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.Priva func (s *AccountsStorage) createKeysFolder() { if err := createNonExistingFolder(s.keysPath); err != nil { - log.Fatal("Could not check/create the directory for the account.", "userID", s.GetUserID(), "error", err) + log.Fatal("Could not check/create the directory for the account.", + slog.String("userID", s.GetUserID()), + log.ErrorAttr(err), + ) } } diff --git a/cmd/certs_storage.go b/cmd/certs_storage.go index 1411a2c84..48a08d93f 100644 --- a/cmd/certs_storage.go +++ b/cmd/certs_storage.go @@ -7,6 +7,7 @@ import ( "encoding/pem" "errors" "fmt" + "log/slog" "os" "path/filepath" "strconv" @@ -65,7 +66,7 @@ func NewCertificatesStorage(cmd *cli.Command) *CertificatesStorage { switch pfxFormat { case "DES", "RC2", "SHA256": default: - log.Fatal("Invalid PFX format.", "format", pfxFormat) + log.Fatal("Invalid PFX format.", slog.String("format", pfxFormat)) } return &CertificatesStorage{ @@ -82,14 +83,20 @@ func NewCertificatesStorage(cmd *cli.Command) *CertificatesStorage { func (s *CertificatesStorage) CreateRootFolder() { err := createNonExistingFolder(s.rootPath) if err != nil { - log.Fatal("Could not check/create the root folder", "filepath", s.rootPath, "error", err) + log.Fatal("Could not check/create the root folder", + slog.String("filepath", s.rootPath), + log.ErrorAttr(err), + ) } } func (s *CertificatesStorage) CreateArchiveFolder() { err := createNonExistingFolder(s.archivePath) if err != nil { - log.Fatal("Could not check/create the archive folder.", "filepath", s.archivePath, "error", err) + log.Fatal("Could not check/create the archive folder.", + slog.String("filepath", s.archivePath), + log.ErrorAttr(err), + ) } } @@ -104,13 +111,19 @@ 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.Fatal("Unable to save Certificate.", "domain", domain, "error", err) + log.Fatal("Unable to save Certificate.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } if certRes.IssuerCertificate != nil { err = s.WriteFile(domain, issuerExt, certRes.IssuerCertificate) if err != nil { - log.Fatal("Unable to save IssuerCertificate.", "domain", domain, "error", err) + log.Fatal("Unable to save IssuerCertificate.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } } @@ -118,33 +131,45 @@ func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) { if certRes.PrivateKey != nil { err = s.WriteCertificateFiles(domain, certRes) if err != nil { - log.Fatal("Unable to save PrivateKey.", "domain", domain, "error", err) + log.Fatal("Unable to save PrivateKey.", log.DomainAttr(domain), log.ErrorAttr(err)) } } else if s.pem || s.pfx { // we don't have the private key; can't write the .pem or .pfx file - log.Fatal("Unable to save PEM or PFX without the private key. Are you using a CSR?", "domain", domain) + log.Fatal("Unable to save PEM or PFX without the private key. Are you using a CSR?", log.DomainAttr(domain)) } jsonBytes, err := json.MarshalIndent(certRes, "", "\t") if err != nil { - log.Fatal("Unable to marshal CertResource.", "domain", domain, "error", err) + log.Fatal("Unable to marshal CertResource.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } err = s.WriteFile(domain, resourceExt, jsonBytes) if err != nil { - log.Fatal("Unable to save CertResource.", "domain", domain, "error", err) + log.Fatal("Unable to save CertResource.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } } func (s *CertificatesStorage) ReadResource(domain string) certificate.Resource { raw, err := s.ReadFile(domain, resourceExt) if err != nil { - log.Fatal("Error while loading the metadata.", "domain", domain, "error", err) + log.Fatal("Error while loading the metadata.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } var resource certificate.Resource if err = json.Unmarshal(raw, &resource); err != nil { - log.Fatal("Error while marshaling the metadata.", "domain", domain, "error", err) + log.Fatal("Error while marshaling the metadata.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } return resource @@ -156,7 +181,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("File stat", "filepath", filePath, "error", err) + log.Fatal("File stat", slog.String("filepath", filePath), log.ErrorAttr(err)) } return true @@ -319,7 +344,10 @@ 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("Could not sanitize the domain.", "domain", domain, "error", err) + log.Fatal("Could not sanitize the domain.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } return safe diff --git a/cmd/cmd_before.go b/cmd/cmd_before.go index 34995964b..dde95f184 100644 --- a/cmd/cmd_before.go +++ b/cmd/cmd_before.go @@ -3,6 +3,7 @@ package cmd import ( "context" "fmt" + "log/slog" "github.com/go-acme/lego/v5/log" "github.com/urfave/cli/v3" @@ -15,7 +16,11 @@ func Before(ctx context.Context, cmd *cli.Command) (context.Context, error) { err := createNonExistingFolder(cmd.String(flgPath)) if err != nil { - log.Fatal("Could not check/create the path.", "flag", flgPath, "filepath", cmd.String(flgPath), "error", err) + log.Fatal("Could not check/create the path.", + slog.String("flag", flgPath), + slog.String("filepath", cmd.String(flgPath)), + log.ErrorAttr(err), + ) } if cmd.String(flgServer) == "" { diff --git a/cmd/cmd_renew.go b/cmd/cmd_renew.go index a5a022a4b..10ea3d283 100644 --- a/cmd/cmd_renew.go +++ b/cmd/cmd_renew.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "errors" "fmt" + "log/slog" "math/rand" "os" "slices" @@ -143,7 +144,7 @@ func renew(ctx context.Context, cmd *cli.Command) error { account, keyType := setupAccount(ctx, cmd, NewAccountsStorage(cmd)) if account.Registration == nil { - log.Fatal("The account is not registered. Use 'run' to register a new account.", "email", account.Email) + log.Fatal("The account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email)) } certsStorage := NewCertificatesStorage(cmd) @@ -172,7 +173,7 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *Account, ke // as web servers would not be able to work with a combined file. certificates, err := certsStorage.ReadCertificate(domain, certExt) if err != nil { - log.Fatal("Error while loading the certificate.", "domain", domain, "error", err) + log.Fatal("Error while loading the certificate.", log.DomainAttr(domain), log.ErrorAttr(err)) } cert := certificates[0] @@ -193,14 +194,18 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *Account, ke // Figure out if we need to sleep before renewing. if ariRenewalTime.After(now) { - log.Info("Sleeping until renewal time", "domain", domain, "sleep", ariRenewalTime.Sub(now), "renewalTime", ariRenewalTime) + log.Info("Sleeping until renewal time", + log.DomainAttr(domain), + slog.Duration("sleep", ariRenewalTime.Sub(now)), + slog.Time("renewalTime", *ariRenewalTime), + ) time.Sleep(ariRenewalTime.Sub(now)) } } replacesCertID, err = certificate.MakeARICertID(cert) if err != nil { - log.Fatal("Error while construction the ARI CertID.", "domain", domain, "error", err) + log.Fatal("Error while construction the ARI CertID.", log.DomainAttr(domain), log.ErrorAttr(err)) } } @@ -219,14 +224,20 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *Account, ke // This is just meant to be informal for the user. timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - log.Info("acme: Trying renewal.", "domain", domain, "hoursRemaining", int(timeLeft.Hours())) + log.Info("acme: Trying renewal.", + log.DomainAttr(domain), + slog.Int("hoursRemaining", int(timeLeft.Hours())), + ) var privateKey crypto.PrivateKey if cmd.Bool(flgReuseKey) { keyBytes, errR := certsStorage.ReadFile(domain, keyExt) if errR != nil { - log.Fatal("Error while loading the private key.", "domain", domain, "error", errR) + log.Fatal("Error while loading the private key.", + log.DomainAttr(domain), + log.ErrorAttr(errR), + ) } privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes) @@ -244,7 +255,7 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *Account, ke rnd := rand.New(rand.NewSource(time.Now().UnixNano())) sleepTime := time.Duration(rnd.Int63n(int64(jitter))) - log.Info("renewal: random delay.", "sleep", sleepTime) + log.Info("renewal: random delay.", slog.Duration("sleep", sleepTime)) time.Sleep(sleepTime) } @@ -271,7 +282,7 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *Account, ke certRes, err := client.Certificate.Obtain(ctx, request) if err != nil { - log.Fatal("Could not obtain the certificate.", "error", err) + log.Fatal("Could not obtain the certificate.", log.ErrorAttr(err)) } certRes.Domain = domain @@ -286,12 +297,16 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *Account, ke func renewForCSR(ctx context.Context, cmd *cli.Command, account *Account, keyType certcrypto.KeyType, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error { csr, err := readCSRFile(cmd.String(flgCSR)) if err != nil { - log.Fatal("Could not read CSR file.", "flag", flgCSR, "filepath", cmd.String(flgCSR), "error", err) + log.Fatal("Could not read CSR file.", + slog.String("flag", flgCSR), + slog.String("filepath", cmd.String(flgCSR)), + log.ErrorAttr(err), + ) } domain, err := certcrypto.GetCSRMainDomain(csr) if err != nil { - log.Fatal("Could not get CSR main domain.", "error", err) + log.Fatal("Could not get CSR main domain.", log.ErrorAttr(err)) } // load the cert resource from files. @@ -299,7 +314,10 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *Account, keyTyp // as web servers would not be able to work with a combined file. certificates, err := certsStorage.ReadCertificate(domain, certExt) if err != nil { - log.Fatal("Error while loading the certificate.", "domain", domain, "error", err) + log.Fatal("Error while loading the certificate.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) } cert := certificates[0] @@ -320,14 +338,17 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *Account, keyTyp // Figure out if we need to sleep before renewing. if ariRenewalTime.After(now) { - log.Info("Sleeping until renewal time", "domain", domain, "sleep", ariRenewalTime.Sub(now), "renewalTime", ariRenewalTime) + log.Info("Sleeping until renewal time", + log.DomainAttr(domain), + slog.Duration("sleep", ariRenewalTime.Sub(now)), + slog.Time("renewalTime", *ariRenewalTime)) time.Sleep(ariRenewalTime.Sub(now)) } } replacesCertID, err = certificate.MakeARICertID(cert) if err != nil { - log.Fatal("Error while construction the ARI CertID.", "domain", domain, "error", err) + log.Fatal("Error while construction the ARI CertID.", log.DomainAttr(domain), log.ErrorAttr(err)) } } @@ -341,7 +362,10 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *Account, keyTyp // This is just meant to be informal for the user. timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - log.Info("acme: Trying renewal.", "domain", domain, "hoursRemaining", int(timeLeft.Hours())) + log.Info("acme: Trying renewal.", + log.DomainAttr(domain), + slog.Int("hoursRemaining", int(timeLeft.Hours())), + ) request := certificate.ObtainForCSRRequest{ CSR: csr, @@ -359,7 +383,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *Account, keyTyp certRes, err := client.Certificate.ObtainForCSR(ctx, request) if err != nil { - log.Fatal("Could not obtain the certificate fro CSR.", "error", err) + log.Fatal("Could not obtain the certificate for CSR.", log.ErrorAttr(err)) } certsStorage.SaveResource(certRes) @@ -371,7 +395,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *Account, keyTyp func needRenewal(x509Cert *x509.Certificate, domain string, days int, dynamic bool) bool { if x509Cert.IsCA { - log.Fatal("Certificate bundle starts with a CA certificate.", "domain", domain) + log.Fatal("Certificate bundle starts with a CA certificate.", log.DomainAttr(domain)) } if dynamic { @@ -387,8 +411,11 @@ func needRenewal(x509Cert *x509.Certificate, domain string, days int, dynamic bo return true } - 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) + 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), + log.DomainAttr(domain), + ) return false } @@ -408,7 +435,7 @@ func needRenewalDynamic(x509Cert *x509.Certificate, domain string, now time.Time } 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) + x509Cert.NotAfter.Format(time.RFC3339), dueDate.Sub(now)), log.DomainAttr(domain)) return false } @@ -416,17 +443,24 @@ 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(ctx context.Context, cmd *cli.Command, cert *x509.Certificate, domain string, client *lego.Client) *time.Time { if cert.IsCA { - log.Fatal("Certificate bundle starts with a CA certificate.", "domain", domain) + log.Fatal("Certificate bundle starts with a CA certificate.", log.DomainAttr(domain)) } renewalInfo, err := client.Certificate.GetRenewalInfo(ctx, certificate.RenewalInfoRequest{Cert: cert}) if err != nil { if errors.Is(err, api.ErrNoARI) { - log.Warn("acme: the server does not advertise a renewal info endpoint.", "domain", domain, "errorr", err) + log.Warn("acme: the server does not advertise a renewal info endpoint.", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) + return nil } - log.Warn("acme: calling renewal info endpoint", "domain", domain, "error", err) + log.Warn("acme: calling renewal info endpoint", + log.DomainAttr(domain), + log.ErrorAttr(err), + ) return nil } @@ -435,14 +469,17 @@ func getARIRenewalTime(ctx context.Context, cmd *cli.Command, cert *x509.Certifi renewalTime := renewalInfo.ShouldRenewAt(now, cmd.Duration(flgARIWaitToRenewDuration)) if renewalTime == nil { - log.Info("acme: renewalInfo endpoint indicates that renewal is not needed.", "domain", domain) + log.Info("acme: renewalInfo endpoint indicates that renewal is not needed.", log.DomainAttr(domain)) return nil } - log.Info("acme: renewalInfo endpoint indicates that renewal is needed.", "domain", domain) + log.Info("acme: renewalInfo endpoint indicates that renewal is needed.", log.DomainAttr(domain)) if renewalInfo.ExplanationURL != "" { - log.Info("acme: renewalInfo endpoint provided an explanation.", "domain", domain, "explanationURL", renewalInfo.ExplanationURL) + log.Info("acme: renewalInfo endpoint provided an explanation.", + log.DomainAttr(domain), + slog.String("explanationURL", renewalInfo.ExplanationURL), + ) } return renewalTime diff --git a/cmd/cmd_revoke.go b/cmd/cmd_revoke.go index 9d458ff63..13eff52d4 100644 --- a/cmd/cmd_revoke.go +++ b/cmd/cmd_revoke.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "log/slog" "github.com/go-acme/lego/v5/acme" "github.com/go-acme/lego/v5/log" @@ -43,7 +44,7 @@ func revoke(ctx context.Context, cmd *cli.Command) error { account, keyType := setupAccount(ctx, cmd, NewAccountsStorage(cmd)) if account.Registration == nil { - log.Fatal("Account is not registered. Use 'run' to register a new account.", "email", account.Email) + log.Fatal("Account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email)) } client := newClient(cmd, account, keyType) @@ -52,21 +53,21 @@ func revoke(ctx context.Context, cmd *cli.Command) error { certsStorage.CreateRootFolder() for _, domain := range cmd.StringSlice(flgDomains) { - log.Info("Trying to revoke the certificate.", "domain", domain) + log.Info("Trying to revoke the certificate.", log.DomainAttr(domain)) certBytes, err := certsStorage.ReadFile(domain, certExt) if err != nil { - log.Fatal("Error while revoking the certificate.", "domain", domain, "error", err) + log.Fatal("Error while revoking the certificate.", log.DomainAttr(domain), log.ErrorAttr(err)) } reason := cmd.Uint(flgReason) err = client.Certificate.RevokeWithReason(ctx, certBytes, &reason) if err != nil { - log.Fatal("Error while revoking the certificate.", "domain", domain, "error", err) + log.Fatal("Error while revoking the certificate.", log.DomainAttr(domain), log.ErrorAttr(err)) } - log.Info("Certificate was revoked.", "domain", domain) + log.Info("Certificate was revoked.", log.DomainAttr(domain)) if cmd.Bool(flgKeep) { return nil @@ -79,7 +80,7 @@ func revoke(ctx context.Context, cmd *cli.Command) error { return err } - log.Info("Certificate was archived", "domain", domain) + log.Info("Certificate was archived", log.DomainAttr(domain)) } return nil diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go index 7653d8164..4b89fdc9f 100644 --- a/cmd/cmd_run.go +++ b/cmd/cmd_run.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "fmt" + "log/slog" "os" "strings" "time" @@ -124,12 +125,12 @@ func run(ctx context.Context, cmd *cli.Command) error { if account.Registration == nil { reg, err := register(ctx, cmd, client) if err != nil { - log.Fatal("Could not complete registration.", "error", err) + log.Fatal("Could not complete registration.", log.ErrorAttr(err)) } account.Registration = reg if err = accountsStorage.Save(account); err != nil { - log.Fatal("Could not save the account file.", "error", err) + log.Fatal("Could not save the account file.", log.ErrorAttr(err)) } fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath()) @@ -142,7 +143,7 @@ func run(ctx context.Context, cmd *cli.Command) 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.Fatal("Could not obtain certificates", "error", err) + log.Fatal("Could not obtain certificates", log.ErrorAttr(err)) } certsStorage.SaveResource(cert) @@ -164,14 +165,14 @@ func handleTOS(cmd *cli.Command, client *lego.Client) bool { reader := bufio.NewReader(os.Stdin) - log.Warn("Please review the TOS", "url", client.GetToSURL()) + log.Warn("Please review the TOS", slog.String("url", client.GetToSURL())) for { fmt.Println("Do you accept the TOS? Y/n") text, err := reader.ReadString('\n') if err != nil { - log.Fatal("Could not read from the console", "error", err) + log.Fatal("Could not read from the console", log.ErrorAttr(err)) } text = strings.Trim(text, "\r\n") diff --git a/cmd/lego/main.go b/cmd/lego/main.go index 4ebcc2df4..e5bd8b785 100644 --- a/cmd/lego/main.go +++ b/cmd/lego/main.go @@ -32,6 +32,6 @@ func main() { err := app.Run(context.Background(), os.Args) if err != nil { - log.Fatal("Error", "error", err) + log.Fatal("Error", log.ErrorAttr(err)) } } diff --git a/cmd/setup.go b/cmd/setup.go index bec0f2702..c70effdff 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -7,6 +7,7 @@ import ( "encoding/pem" "fmt" "io" + "log/slog" "net/http" "os" "strings" @@ -85,7 +86,7 @@ func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyTy client, err := lego.NewClient(config) if err != nil { - log.Fatal("Could not create client.", "error", err) + log.Fatal("Could not create client.", log.ErrorAttr(err)) } if client.GetExternalAccountRequired() && !cmd.IsSet(flgEAB) { @@ -113,7 +114,7 @@ func getKeyType(cmd *cli.Command) certcrypto.KeyType { return certcrypto.EC384 } - log.Fatal("Unsupported KeyType.", "keyType", keyType) + log.Fatal("Unsupported KeyType.", slog.String("keyType", keyType)) return "" } diff --git a/cmd/setup_challenges.go b/cmd/setup_challenges.go index 77b1a6e75..ea0fe7c69 100644 --- a/cmd/setup_challenges.go +++ b/cmd/setup_challenges.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "log/slog" "net" "strings" "time" @@ -27,21 +28,21 @@ func setupChallenges(cmd *cli.Command, client *lego.Client) { if cmd.Bool(flgHTTP) { err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(cmd), http01.SetDelay(cmd.Duration(flgHTTPDelay))) if err != nil { - log.Fatal("Could not set HTTP challenge provider.", "error", err) + log.Fatal("Could not set HTTP challenge provider.", log.ErrorAttr(err)) } } if cmd.Bool(flgTLS) { err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(cmd), tlsalpn01.SetDelay(cmd.Duration(flgTLSDelay))) if err != nil { - log.Fatal("Could not set TLS challenge provider.", "error", err) + log.Fatal("Could not set TLS challenge provider.", log.ErrorAttr(err)) } } if cmd.IsSet(flgDNS) { err := setupDNS(cmd, client) if err != nil { - log.Fatal("Could not set DNS challenge provider.", "error", err) + log.Fatal("Could not set DNS challenge provider.", log.ErrorAttr(err)) } } } @@ -53,7 +54,10 @@ func setupHTTPProvider(cmd *cli.Command) challenge.Provider { ps, err := webroot.NewHTTPProvider(cmd.String(flgHTTPWebroot)) if err != nil { log.Fatal("Could not create the webroot provider.", - "flag", flgHTTPWebroot, "webRoot", cmd.String(flgHTTPWebroot), "error", err) + slog.String("flag", flgHTTPWebroot), + slog.String("webRoot", cmd.String(flgHTTPWebroot)), + log.ErrorAttr(err), + ) } return ps @@ -62,7 +66,10 @@ func setupHTTPProvider(cmd *cli.Command) challenge.Provider { ps, err := memcached.NewMemcachedProvider(cmd.StringSlice(flgHTTPMemcachedHost)) if err != nil { log.Fatal("Could not create the memcached provider.", - "flag", flgHTTPMemcachedHost, "memcachedHosts", strings.Join(cmd.StringSlice(flgHTTPMemcachedHost), ", "), "error", err) + slog.String("flag", flgHTTPMemcachedHost), + slog.String("memcachedHosts", strings.Join(cmd.StringSlice(flgHTTPMemcachedHost), ", ")), + log.ErrorAttr(err), + ) } return ps @@ -71,7 +78,10 @@ func setupHTTPProvider(cmd *cli.Command) challenge.Provider { ps, err := s3.NewHTTPProvider(cmd.String(flgHTTPS3Bucket)) if err != nil { log.Fatal("Could not create the S3 provider.", - "flag", flgHTTPS3Bucket, "bucket", cmd.String(flgHTTPS3Bucket), "error", err) + slog.String("flag", flgHTTPS3Bucket), + slog.String("bucket", cmd.String(flgHTTPS3Bucket)), + log.ErrorAttr(err), + ) } return ps @@ -82,13 +92,14 @@ func setupHTTPProvider(cmd *cli.Command) challenge.Provider { if !strings.Contains(iface, ":") { log.Fatal( fmt.Sprintf("The --%s switch only accepts interface:port or :port for its argument.", flgHTTPPort), - "flag", flgHTTPPort, "port", cmd.String(flgHTTPPort), + slog.String("flag", flgHTTPPort), + slog.String("port", cmd.String(flgHTTPPort)), ) } host, port, err := net.SplitHostPort(iface) if err != nil { - log.Fatal("Could not split host and port.", "iface", iface, "error", err) + log.Fatal("Could not split host and port.", slog.String("iface", iface), log.ErrorAttr(err)) } srv := http01.NewProviderServerWithOptions(http01.Options{ @@ -132,7 +143,7 @@ func setupTLSProvider(cmd *cli.Command) challenge.Provider { host, port, err := net.SplitHostPort(iface) if err != nil { - log.Fatal("Could not split host and port.", "iface", iface, "error", err) + log.Fatal("Could not split host and port.", slog.String("iface", iface), log.ErrorAttr(err)) } return tlsalpn01.NewProviderServerWithOptions(tlsalpn01.Options{ diff --git a/log/attrs.go b/log/attrs.go index b6b0c380d..9d870fb9b 100644 --- a/log/attrs.go +++ b/log/attrs.go @@ -2,8 +2,17 @@ package log import ( "log/slog" + "strings" ) func ErrorAttr(err error) slog.Attr { return slog.Any("error", err) } + +func DomainAttr(v string) slog.Attr { + return slog.String("domain", v) +} + +func DomainsAttr(v []string) slog.Attr { + return slog.String("domains", strings.Join(v, ", ")) +} diff --git a/platform/config/env/env.go b/platform/config/env/env.go index 2368bee9f..f10940d33 100644 --- a/platform/config/env/env.go +++ b/platform/config/env/env.go @@ -3,6 +3,7 @@ package env import ( "errors" "fmt" + "log/slog" "os" "strconv" "strings" @@ -160,7 +161,12 @@ func GetOrFile(envVar string) string { fileContents, err := os.ReadFile(fileVarValue) if err != nil { - log.Warn("Failed to read the file.", "filepath", fileVarValue, "envVar", fileVar, "error", err) + log.Warn("Failed to read the file.", + slog.String("filepath", fileVarValue), + slog.String("envVar", fileVar), + log.ErrorAttr(err), + ) + return "" } diff --git a/platform/wait/wait.go b/platform/wait/wait.go index 2ae35cb72..3ff729197 100644 --- a/platform/wait/wait.go +++ b/platform/wait/wait.go @@ -3,6 +3,7 @@ package wait import ( "context" "fmt" + "log/slog" "time" "github.com/cenkalti/backoff/v5" @@ -11,7 +12,10 @@ 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(log.LazySprintf("Wait for %s.", msg), "timeout", timeout, "interval", interval) + log.Infof(log.LazySprintf("Wait for %s.", msg), + slog.Duration("timeout", timeout), + slog.Duration("interval", interval), + ) var lastErr error diff --git a/providers/dns/bluecat/bluecat.go b/providers/dns/bluecat/bluecat.go index 6a9af0869..f345f2e60 100644 --- a/providers/dns/bluecat/bluecat.go +++ b/providers/dns/bluecat/bluecat.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "log/slog" "net/http" "time" @@ -138,7 +139,13 @@ func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string } if d.config.Debug { - log.Info("bluecat: debug information.", "fqdn", info.EffectiveFQDN, "viewID", viewID, "zoneID", parentZoneID, "zone", name) + log.Info( + "bluecat: debug information.", + slog.String("fqdn", info.EffectiveFQDN), + slog.Uint64("viewID", uint64(viewID)), + slog.Uint64("zoneID", uint64(parentZoneID)), + slog.String("zone", name), + ) } txtRecord := internal.Entity{ diff --git a/providers/dns/cloudns/cloudns.go b/providers/dns/cloudns/cloudns.go index f603d048d..7d939379d 100644 --- a/providers/dns/cloudns/cloudns.go +++ b/providers/dns/cloudns/cloudns.go @@ -172,7 +172,10 @@ func (d *DNSProvider) waitNameservers(ctx context.Context, domain string, zone * return fmt.Errorf("nameserver sync on %s: %w", domain, err) } - log.Infof(log.LazySprintf("Sync %d/%d complete", syncProgress.Updated, syncProgress.Total), "domain", domain) + log.Infof( + log.LazySprintf("Sync %d/%d complete", syncProgress.Updated, syncProgress.Total), + log.DomainAttr(domain), + ) if !syncProgress.Complete { return fmt.Errorf("nameserver sync on %s not complete", domain) diff --git a/providers/dns/designate/designate.go b/providers/dns/designate/designate.go index e9222fc33..ca5cea6d4 100644 --- a/providers/dns/designate/designate.go +++ b/providers/dns/designate/designate.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "slices" "sync" @@ -156,7 +157,7 @@ func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string if existingRecord != nil { if slices.Contains(existingRecord.Records, info.Value) { - log.Debug("designate: the record already exists.", "value", info.Value) + log.Debug("designate: the record already exists.", slog.String("value", info.Value)) return nil } @@ -230,7 +231,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.Debug("skip: the record already exists.", "value", value) + log.Debug("skip: the record already exists.", slog.String("value", value)) return nil } diff --git a/providers/dns/dynu/internal/client.go b/providers/dns/dynu/internal/client.go index 8c8544ce9..67377ed95 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.Warn("client retries.", "error", err) + log.Warn("client retries.", log.ErrorAttr(err)) } bo := backoff.NewExponentialBackOff() diff --git a/providers/dns/gcloud/googlecloud.go b/providers/dns/gcloud/googlecloud.go index 4aae86d94..ba7eb0972 100644 --- a/providers/dns/gcloud/googlecloud.go +++ b/providers/dns/gcloud/googlecloud.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/http" "os" "strconv" @@ -254,7 +255,7 @@ func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string func (d *DNSProvider) applyChanges(ctx context.Context, zone string, change *gdns.Change) error { if d.config.Debug { data, _ := json.Marshal(change) - log.Info("change (Create)", "data", string(data)) + log.Info("change (Create)", slog.String("data", string(data))) } chg, err := d.client.Changes.Create(d.config.Project, zone, change).Do() @@ -280,7 +281,7 @@ func (d *DNSProvider) applyChanges(ctx context.Context, zone string, change *gdn func() error { if d.config.Debug { data, _ := json.Marshal(change) - log.Info("change (Get)", "data", string(data)) + log.Info("change (Get)", slog.String("data", string(data))) } chg, err = d.client.Changes.Get(d.config.Project, zone, chgID).Do() diff --git a/providers/dns/hurricane/internal/client.go b/providers/dns/hurricane/internal/client.go index e7d476f52..50c0528e0 100644 --- a/providers/dns/hurricane/internal/client.go +++ b/providers/dns/hurricane/internal/client.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "log/slog" "net/http" "net/url" "strings" @@ -108,7 +109,11 @@ func evaluateBody(body, hostname string) error { case codeGood: return nil case codeNoChg: - log.Debug("unchanged content written to TXT record.", "hostname", hostname, "body", body) + log.Debug("unchanged content written to TXT record.", + slog.String("hostname", hostname), + slog.String("body", body), + ) + return nil case codeAbuse: return fmt.Errorf("%s: blocked hostname for abuse: %s", body, hostname) diff --git a/providers/dns/inwx/inwx.go b/providers/dns/inwx/inwx.go index 5906dbeef..468b3410a 100644 --- a/providers/dns/inwx/inwx.go +++ b/providers/dns/inwx/inwx.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "log/slog" "time" "github.com/go-acme/lego/v5/challenge" @@ -113,7 +114,7 @@ func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string defer func() { errL := d.client.Account.Logout() if errL != nil { - log.Warn("inwx: failed to log out.", "error", errL) + log.Warn("inwx: failed to log out.", log.ErrorAttr(errL)) } }() @@ -160,7 +161,7 @@ func (d *DNSProvider) CleanUp(ctx context.Context, domain, token, keyAuth string defer func() { errL := d.client.Account.Logout() if errL != nil { - log.Warn("inwx: failed to log out.", "error", errL) + log.Warn("inwx: failed to log out.", log.ErrorAttr(errL)) } }() @@ -221,7 +222,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.Info("inwx: waiting for the next TOTP token", "sleep", sleep) + log.Info("inwx: waiting for the next TOTP token", slog.Duration("sleep", sleep)) time.Sleep(sleep) } diff --git a/providers/dns/netcup/netcup.go b/providers/dns/netcup/netcup.go index 403d05b2a..382076102 100644 --- a/providers/dns/netcup/netcup.go +++ b/providers/dns/netcup/netcup.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "log/slog" "net/http" "strings" "time" @@ -119,7 +120,7 @@ func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string defer func() { err = d.client.Logout(ctxAuth) if err != nil { - log.Warn("netcup: failed to logout.", "error", err) + log.Warn("netcup: failed to logout.", log.ErrorAttr(err)) } }() @@ -135,7 +136,7 @@ func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string records, err := d.client.GetDNSRecords(ctxAuth, zone) if err != nil { // skip no existing records - log.Info("No existing records, error ignored.", "zone", zone, "error", err) + log.Info("No existing records, error ignored.", slog.String("zone", zone), log.ErrorAttr(err)) } records = append(records, record) @@ -165,7 +166,7 @@ func (d *DNSProvider) CleanUp(ctx context.Context, domain, token, keyAuth string defer func() { err = d.client.Logout(ctxAuth) if err != nil { - log.Warn("netcup: failed to logout.", "error", err) + log.Warn("netcup: failed to logout.", log.ErrorAttr(err)) } }() diff --git a/providers/dns/oraclecloud/configurationprovider.go b/providers/dns/oraclecloud/configurationprovider.go index 7acee8cec..e9dde1a81 100644 --- a/providers/dns/oraclecloud/configurationprovider.go +++ b/providers/dns/oraclecloud/configurationprovider.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "errors" "fmt" + "log/slog" "os" "slices" "strings" @@ -133,7 +134,12 @@ func getEnvFileWithStrictFallback(keys ...string) []byte { fileContents, err := os.ReadFile(fileVarValue) if err != nil { - log.Debug("Failed to read the file.", "filepath", fileVarValue, "envVar", key, "error", err) + log.Debug("Failed to read the file.", + slog.String("filepath", fileVarValue), + slog.String("envVar", key), + log.ErrorAttr(err), + ) + return nil } diff --git a/providers/dns/pdns/pdns.go b/providers/dns/pdns/pdns.go index a5b7f04e5..6e1211a28 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.Warn("pdns: failed to get API version.", "error", err) + log.Warn("pdns: failed to get API version.", log.ErrorAttr(err)) } } diff --git a/registration/registar.go b/registration/registar.go index 2b091e2dc..e80774720 100644 --- a/registration/registar.go +++ b/registration/registar.go @@ -3,6 +3,7 @@ package registration import ( "context" "errors" + "log/slog" "net/http" "github.com/go-acme/lego/v5/acme" @@ -54,7 +55,7 @@ func (r *Registrar) Register(ctx context.Context, options RegisterOptions) (*Res } if r.user.GetEmail() != "" { - log.Info("acme: Registering the account.", "email", r.user.GetEmail()) + log.Info("acme: Registering the account.", slog.String("email", r.user.GetEmail())) accMsg.Contact = []string{mailTo + r.user.GetEmail()} } @@ -78,7 +79,7 @@ func (r *Registrar) RegisterWithExternalAccountBinding(ctx context.Context, opti } if r.user.GetEmail() != "" { - log.Info("acme: Registering the account.", "email", r.user.GetEmail()) + log.Info("acme: Registering the account.", slog.String("email", r.user.GetEmail())) accMsg.Contact = []string{mailTo + r.user.GetEmail()} } @@ -104,7 +105,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.Info("acme: Querying the account.", "registrationURI", r.user.GetRegistration().URI) + log.Info("acme: Querying the account.", slog.String("registrationURI", r.user.GetRegistration().URI)) account, err := r.core.Accounts.Get(ctx, r.user.GetRegistration().URI) if err != nil { @@ -130,7 +131,7 @@ func (r *Registrar) UpdateRegistration(ctx context.Context, options RegisterOpti } if r.user.GetEmail() != "" { - log.Info("acme: Registering the account.", "email", r.user.GetEmail()) + log.Info("acme: Registering the account.", slog.String("email", r.user.GetEmail())) accMsg.Contact = []string{mailTo + r.user.GetEmail()} } @@ -150,7 +151,7 @@ func (r *Registrar) DeleteRegistration(ctx context.Context) error { return errors.New("acme: cannot unregister a nil client or user") } - log.Info("acme: Deleting the account.", "email", r.user.GetEmail()) + log.Info("acme: Deleting the account.", slog.String("email", r.user.GetEmail())) return r.core.Accounts.Deactivate(ctx, r.user.GetRegistration().URI) }