chore: use slog Attr (#2809)

This commit is contained in:
Ludovic Fernandez 2026-01-21 18:00:54 +01:00 committed by GitHub
commit e26d5cee67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 357 additions and 156 deletions

View file

@ -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,

View file

@ -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))
}
}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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))
}
}()

View file

@ -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

View file

@ -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))
}
}
}

View file

@ -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
}

View file

@ -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))
}
}()

View file

@ -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))
}
}()

View file

@ -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),
)
}
}

View file

@ -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

View file

@ -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) == "" {

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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))
}
}

View file

@ -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 ""
}

View file

@ -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{

18
log/attrs.go Normal file
View file

@ -0,0 +1,18 @@
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, ", "))
}

View file

@ -23,29 +23,29 @@ func (l LazyMessage) String() string {
}
// Debugf calls [Logger.Debug] on the default logger.
func Debugf(msg LazyMessage, args ...any) {
func Debugf(msg LazyMessage, args ...slog.Attr) {
logLazy(slog.LevelDebug, msg, args...)
}
// Infof calls [Logger.Info] on the default logger.
func Infof(msg LazyMessage, args ...any) {
func Infof(msg LazyMessage, args ...slog.Attr) {
logLazy(slog.LevelInfo, msg, args...)
}
// Warnf calls [Logger.Warn] on the default logger.
func Warnf(msg LazyMessage, args ...any) {
func Warnf(msg LazyMessage, args ...slog.Attr) {
logLazy(slog.LevelWarn, msg, args...)
}
// Errorf calls [Logger.Error] on the default logger.
func Errorf(msg LazyMessage, args ...any) {
func Errorf(msg LazyMessage, args ...slog.Attr) {
logLazy(slog.LevelError, msg, args...)
}
func logLazy(level slog.Level, msg LazyMessage, args ...any) {
func logLazy(level slog.Level, msg LazyMessage, args ...slog.Attr) {
ctx := context.Background()
if Default().Enabled(ctx, level) {
Default().Log(ctx, level, msg.String(), args...)
Default().LogAttrs(ctx, level, msg.String(), args...)
}
}

View file

@ -1,6 +1,7 @@
package log
import (
"context"
"log/slog"
"os"
"sync/atomic"
@ -22,27 +23,27 @@ func SetDefault(l *slog.Logger) {
}
// Fatal calls [Logger.Error] on the default logger and exit with code 1.
func Fatal(msg string, args ...any) {
Default().Error(msg, args...)
func Fatal(msg string, args ...slog.Attr) {
Error(msg, args...)
os.Exit(1)
}
// Debug calls [Logger.Debug] on the default logger.
func Debug(msg string, args ...any) {
Default().Debug(msg, args...)
func Debug(msg string, args ...slog.Attr) {
Default().LogAttrs(context.Background(), slog.LevelDebug, msg, args...)
}
// Info calls [Logger.Info] on the default logger.
func Info(msg string, args ...any) {
Default().Info(msg, args...)
func Info(msg string, args ...slog.Attr) {
Default().LogAttrs(context.Background(), slog.LevelInfo, msg, args...)
}
// Warn calls [Logger.Warn] on the default logger.
func Warn(msg string, args ...any) {
Default().Warn(msg, args...)
func Warn(msg string, args ...slog.Attr) {
Default().LogAttrs(context.Background(), slog.LevelWarn, msg, args...)
}
// Error calls [Logger.Error] on the default logger.
func Error(msg string, args ...any) {
Default().Error(msg, args...)
func Error(msg string, args ...slog.Attr) {
Default().LogAttrs(context.Background(), slog.LevelError, msg, args...)
}

View file

@ -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 ""
}

View file

@ -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

View file

@ -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{

View file

@ -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)

View file

@ -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
}

View file

@ -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()

View file

@ -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()

View file

@ -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)

View file

@ -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)
}

View file

@ -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))
}
}()

View file

@ -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
}

View file

@ -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))
}
}

View file

@ -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)
}