Skip to content
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
83525f3
Update go.mod to go1.26
aarongable Apr 23, 2026
8b9b50d
Clean up 'non-constant format string' errors in log package
aarongable Apr 23, 2026
6063069
Create new //blog/ package with very familiar API
aarongable Apr 18, 2026
829f5ce
Update all imports to new package
aarongable Apr 18, 2026
948718f
Update cmd/shell glue to set up new blog-based logger
aarongable Apr 18, 2026
ed3a1df
Update //bdns
aarongable Apr 18, 2026
db50514
Update //grpc
aarongable Apr 18, 2026
5ba7b45
Update //ca
aarongable Apr 18, 2026
ffdf71d
Update //crl
aarongable Apr 18, 2026
ff48c6a
Update //ctpolicy
aarongable Apr 18, 2026
8c14058
Update //observer
aarongable Apr 18, 2026
688c1e1
Update //policy
aarongable Apr 18, 2026
3cd8050
Update //publisher
aarongable Apr 18, 2026
5ac99df
Update //ra
aarongable Apr 18, 2026
ee20b12
Update //ratelimits
aarongable Apr 18, 2026
fe4f565
Update //redis
aarongable Apr 18, 2026
8daf0e4
Update //sa
aarongable Apr 18, 2026
25a7daa
Update //salesforce
aarongable Apr 18, 2026
8df93a3
Update //sfe
aarongable Apr 18, 2026
97dfc5a
Update //test
aarongable Apr 18, 2026
3850d92
Update //va
aarongable Apr 18, 2026
1f0eb22
Update //web
aarongable Apr 22, 2026
0d99f14
Update //wfe2
aarongable Apr 22, 2026
f1998f0
Update //cmd/admin
aarongable Apr 22, 2026
9737805
Update //cmd/bad-key-revoker
aarongable Apr 22, 2026
ce51552
Update //cmd/nonce-service
aarongable Apr 22, 2026
5401c95
Update //cmd/ standalone tools
aarongable Apr 22, 2026
0412683
Update //log/validator
aarongable Apr 23, 2026
e871d95
Enable text output in integration tests
aarongable Apr 23, 2026
2c0f0f2
Remove old log package
aarongable Apr 24, 2026
6a85606
Merge branch main into slog
aarongable Apr 27, 2026
46991b0
Review comments
aarongable Apr 27, 2026
f54b72c
Fix remaining unit tests
aarongable Apr 27, 2026
323dfb8
Remove blog/adapters.go; that's back in cmd/shell.go
aarongable Apr 27, 2026
22548ff
Clean up dependency on x/term
aarongable Apr 27, 2026
080c2e6
More review feedback
aarongable Apr 28, 2026
cc7c122
More review comments; update documentation
aarongable Apr 29, 2026
7ad1d02
Merge branch main into slog
aarongable Apr 29, 2026
22e4e90
Add unit tests (with help from claude)
aarongable Apr 29, 2026
1347282
Fix log-validator checksum mismatch
aarongable Apr 29, 2026
5f28365
Use bytes.TrimSuffix for checksums
aarongable May 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions bdns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"strconv"
Expand All @@ -17,7 +18,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"

blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/blog"
"github.com/letsencrypt/boulder/metrics"
)

Expand Down Expand Up @@ -226,7 +227,12 @@ func (c *impl) exchangeOne(ctx context.Context, hostname string, qtype uint16) (
}).Observe(rtt.Seconds())

if err != nil {
c.log.Infof("logDNSError chosenServer=[%s] hostname=[%s] queryType=[%s] err=[%s]", chosenServer, hostname, qtypeStr, err)
c.log.Info(ctx, "logDNSError",
slog.String("chosenServer", chosenServer),
slog.String("hostname", hostname),
slog.String("qtype", qtypeStr),
blog.Error(err),
)

// Check if the error is a network timeout, rather than a local context
// timeout. If it is, retry instead of giving up.
Expand Down
30 changes: 15 additions & 15 deletions bdns/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"

blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/blog"
"github.com/letsencrypt/boulder/metrics"
"github.com/letsencrypt/boulder/test"
)
Expand Down Expand Up @@ -283,7 +283,7 @@ func TestDNSNoServers(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Hour, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Hour, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)

_, resolver, err := obj.LookupA(context.Background(), "letsencrypt.org")
test.AssertEquals(t, resolver, "")
Expand All @@ -306,7 +306,7 @@ func TestDNSOneServer(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)

_, resolver, err := obj.LookupA(context.Background(), "letsencrypt.org")
test.AssertNotError(t, err, "No message")
Expand All @@ -317,7 +317,7 @@ func TestDNSDuplicateServers(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr, dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)

_, resolver, err := obj.LookupA(context.Background(), "letsencrypt.org")
test.AssertNotError(t, err, "No message")
Expand All @@ -328,7 +328,7 @@ func TestDNSServFail(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)
bad := "servfail.com"

_, _, err = obj.LookupTXT(context.Background(), "servfail.com")
Expand All @@ -348,7 +348,7 @@ func TestDNSLookupTXT(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)

_, _, err = obj.LookupTXT(context.Background(), "letsencrypt.org")
test.AssertNotError(t, err, "No message")
Expand All @@ -363,7 +363,7 @@ func TestDNSLookupA(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)

for _, tc := range []struct {
name string
Expand Down Expand Up @@ -448,7 +448,7 @@ func TestDNSLookupAAAA(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)

for _, tc := range []struct {
name string
Expand Down Expand Up @@ -533,7 +533,7 @@ func TestDNSNXDOMAIN(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)
hostname := "nxdomain.letsencrypt.org"

_, _, err = obj.LookupA(context.Background(), hostname)
Expand All @@ -551,7 +551,7 @@ func TestDNSLookupCAA(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.UseMock(), tlsConfig)
obj := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 1, "", blog.NewMock(), tlsConfig)
removeIDExp := regexp.MustCompile(" id: [[:digit:]]+")

caas, resolver, err := obj.LookupCAA(context.Background(), "bracewel.net")
Expand Down Expand Up @@ -759,7 +759,7 @@ func TestRetry(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

testClient := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), tc.maxTries, "", blog.UseMock(), tlsConfig)
testClient := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), tc.maxTries, "", blog.NewMock(), tlsConfig)
dr := testClient.(*impl)
dr.exchanger = tc.te
_, _, err = dr.LookupTXT(context.Background(), "example.com")
Expand Down Expand Up @@ -796,7 +796,7 @@ func TestRetryMetrics(t *testing.T) {
// context itself being cancelled. It should never see the error in the
// testExchanger, because the fake exchanger (like the real http package)
// checks for cancellation before doing any work.
testClient := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 3, "", blog.UseMock(), tlsConfig)
testClient := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 3, "", blog.NewMock(), tlsConfig)
dr := testClient.(*impl)
dr.exchanger = &testExchanger{errs: []error{errors.New("oops")}}
ctx, cancel := context.WithCancel(t.Context())
Expand All @@ -815,7 +815,7 @@ func TestRetryMetrics(t *testing.T) {

// Same as above, except rather than cancelling the context ourselves, we
// let the go runtime cancel it as a result of a deadline in the past.
testClient = New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 3, "", blog.UseMock(), tlsConfig)
testClient = New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 3, "", blog.NewMock(), tlsConfig)
dr = testClient.(*impl)
dr.exchanger = &testExchanger{errs: []error{errors.New("oops")}}
ctx, cancel = context.WithTimeout(t.Context(), -10*time.Hour)
Expand Down Expand Up @@ -883,7 +883,7 @@ func TestRotateServerOnErr(t *testing.T) {
test.AssertNotError(t, err, "Got error creating StaticProvider")

maxTries := 5
client := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), maxTries, "", blog.UseMock(), tlsConfig)
client := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), maxTries, "", blog.NewMock(), tlsConfig)

// Configure a mock exchanger that will always return a retryable error for
// servers A and B. This will force server "[2606:4700:4700::1111]:53" to do
Expand Down Expand Up @@ -948,7 +948,7 @@ func TestDOHMetric(t *testing.T) {
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
test.AssertNotError(t, err, "Got error creating StaticProvider")

testClient := New(time.Second*11, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 0, "", blog.UseMock(), tlsConfig)
testClient := New(time.Second*11, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 0, "", blog.NewMock(), tlsConfig)
resolver := testClient.(*impl)
resolver.exchanger = &dohAlwaysRetryExchanger{err: &url.Error{Op: "read", Err: testTimeoutError(true)}}

Expand Down
134 changes: 134 additions & 0 deletions blog/adapters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package blog

// This file contains adapters which can be used
Comment thread
aarongable marked this conversation as resolved.
Outdated

import (
"context"
"errors"
"fmt"
"log"
"os"
"strings"

"github.com/go-logr/stdr"
"github.com/go-sql-driver/mysql"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"google.golang.org/grpc/grpclog"
)

func InitAdapters(l Logger) {
_ = mysql.SetLogger(mysqlLogger{l})
grpclog.SetLoggerV2(grpcLogger{l})
log.SetOutput(logWriter{l})
redis.SetLogger(redisLogger{l})
otel.SetLogger(stdr.New(logOutput{l}))
}

// mysqlLogger implements the mysql.Logger interface.
type mysqlLogger struct {
Logger
}

func (l mysqlLogger) Print(v ...any) {
// The mysql package only uses the logger to output errors.
l.Error(context.Background(), "mysql error", errors.New(fmt.Sprint(v...)))
}

// grpcLogger implements the grpclog.LoggerV2 interface.
type grpcLogger struct {
Logger
}

// Ensure that fatal logs exit, because we use neither the gRPC default logger
// nor the stdlib default logger, both of which would call os.Exit(1) for us.
func (l grpcLogger) Fatal(args ...any) {
l.Error(args...)
os.Exit(1)
}
func (l grpcLogger) Fatalf(format string, args ...any) {
l.Errorf(format, args...)
os.Exit(1)
}
func (l grpcLogger) Fatalln(args ...any) {
l.Errorln(args...)
os.Exit(1)
}

// Pass through all Error level logs.
func (l grpcLogger) Error(args ...any) {
l.Logger.Error(context.Background(), "grpc error", errors.New(fmt.Sprint(args...)))
}
func (l grpcLogger) Errorf(format string, args ...any) {
l.Logger.Error(context.Background(), "grpc error", fmt.Errorf(format, args...))
}
func (l grpcLogger) Errorln(args ...any) {
l.Logger.Error(context.Background(), "grpc error", errors.New(fmt.Sprintln(args...)))
}

// Pass through most Warnings, but filter out a few noisy ones.
func (l grpcLogger) Warning(args ...any) {
l.Logger.Warn(context.Background(), fmt.Sprint(args...))
}
func (l grpcLogger) Warningf(format string, args ...any) {
l.Logger.Warn(context.Background(), fmt.Sprintf(format, args...))
}
func (l grpcLogger) Warningln(args ...any) {
msg := fmt.Sprintln(args...)
// See https://github.com/letsencrypt/boulder/issues/4628
if strings.Contains(msg, `ccResolverWrapper: error parsing service config: no JSON service config provided`) {
return
}
// See https://github.com/letsencrypt/boulder/issues/4379
if strings.Contains(msg, `Server.processUnaryRPC failed to write status: connection error: desc = "transport is closing"`) {
return
}
// Since we've already formatted the message, just pass through to .Warning()
l.Logger.Warn(context.Background(), msg)
}

// Don't log any INFO-level gRPC stuff. In practice this is all noise, like
// failed TXT lookups for service discovery (we only use A records).
func (l grpcLogger) Info(args ...any) {}
func (l grpcLogger) Infof(format string, args ...any) {}
func (l grpcLogger) Infoln(args ...any) {}

// V returns true if the verbosity level is less than the verbosity we want to
// log at.
func (l grpcLogger) V(_ int) bool {
// We always return false. This causes gRPC to not log some things which are
// only logged conditionally if the logLevel is set below a certain value.
// TODO: Use the wrapped log.Logger.stdoutLevel and log.Logger.syslogLevel
// to determine a correct return value here.
return false
}

// redisLogger implements the redis internal.Logging interface.
type redisLogger struct {
Logger
}

func (rl redisLogger) Printf(ctx context.Context, format string, v ...any) {
rl.Info(ctx, fmt.Sprintf(format, v...))
}

// logWriter implements the io.Writer interface.
type logWriter struct {
Logger
}

func (lw logWriter) Write(p []byte) (int, error) {
// Lines received by logWriter will always have a trailing newline.
lw.Logger.Info(context.Background(), strings.TrimSuffix(string(p), "\n"))
return len(p), nil
}

// logOutput implements the log.Logger interface's Output method for use with logr
type logOutput struct {
Logger
}

func (l logOutput) Output(calldepth int, logline string) error {
l.Logger.Info(context.Background(), logline)
return nil
}
Loading
Loading