Skip to content

Commit 6f38eff

Browse files
authored
feat: Use a global logger (#19)
Use a global logger for all library and application logs. Users can set this log to customise log output.
1 parent b1152bb commit 6f38eff

File tree

14 files changed

+113
-68
lines changed

14 files changed

+113
-68
lines changed

asgard/heimdallr.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ package asgard
1313
import (
1414
"context"
1515
"encoding/pem"
16-
"log/slog"
1716
"net/http"
1817
"net/url"
1918

@@ -51,14 +50,14 @@ func Heimdallr(h HeaderName, ns uuid.UUID) func(http.Handler) http.Handler {
5150

5251
certHeader := r.Header.Get(h.String())
5352
if certHeader == "" {
54-
slog.ErrorContext(ctx, "missing authorization header")
53+
bifrost.Logger().ErrorContext(ctx, "missing authorization header")
5554
http.Error(w, errBadAuthHeader, http.StatusServiceUnavailable)
5655
return
5756
}
5857

5958
certPEM, err := url.PathUnescape(certHeader)
6059
if err != nil {
61-
slog.ErrorContext(
60+
bifrost.Logger().ErrorContext(
6261
ctx, "error decoding header",
6362
"headerName", h.String(),
6463
"headerValue", certHeader,
@@ -69,7 +68,7 @@ func Heimdallr(h HeaderName, ns uuid.UUID) func(http.Handler) http.Handler {
6968

7069
block, _ := pem.Decode([]byte(certPEM))
7170
if block == nil {
72-
slog.ErrorContext(
71+
bifrost.Logger().ErrorContext(
7372
ctx, "no PEM data found in authorization header",
7473
"headerName", h.String(),
7574
"headerValue", certPEM,
@@ -80,13 +79,13 @@ func Heimdallr(h HeaderName, ns uuid.UUID) func(http.Handler) http.Handler {
8079

8180
cert, err := bifrost.ParseCertificate(block.Bytes)
8281
if err != nil {
83-
slog.ErrorContext(ctx, "error parsing client certificate", "error", err)
82+
bifrost.Logger().ErrorContext(ctx, "error parsing client certificate", "error", err)
8483
http.Error(w, errBadAuthHeader, http.StatusServiceUnavailable)
8584
return
8685
}
8786

8887
if cert.Namespace != ns {
89-
slog.ErrorContext(
88+
bifrost.Logger().ErrorContext(
9089
ctx, "client certificate namespace mismatch",
9190
"expected", ns,
9291
"actual", cert.Namespace,

asgard/hofund.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package asgard
22

33
import (
44
"encoding/pem"
5-
"log/slog"
65
"net/http"
76
"net/url"
87

@@ -29,13 +28,14 @@ func Hofund(h HeaderName, ns uuid.UUID) func(http.Handler) http.Handler {
2928

3029
cert, err := bifrost.NewCertificate(r.TLS.PeerCertificates[0])
3130
if err != nil {
32-
slog.ErrorContext(ctx, "error validating client certificate", "error", err)
31+
bifrost.Logger().
32+
ErrorContext(ctx, "error validating client certificate", "error", err)
3333
http.Error(w, "invalid client certificate", http.StatusUnauthorized)
3434
return
3535
}
3636

3737
if cert.Namespace != ns {
38-
slog.ErrorContext(
38+
bifrost.Logger().ErrorContext(
3939
ctx, "client certificate namespace mismatch",
4040
"expected", ns,
4141
"actual", cert.Namespace,

bifrost.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Package bifrost contains an API client for the Bifrost CA service.
2+
package bifrost
3+
4+
import (
5+
"context"
6+
"log/slog"
7+
"sync/atomic"
8+
)
9+
10+
var (
11+
// LogLevel is the log level used by the bifrost logger.
12+
LogLevel = new(slog.LevelVar)
13+
14+
logger atomic.Pointer[slog.Logger]
15+
)
16+
17+
// Logger returns the global Bifrost logger.
18+
func Logger() *slog.Logger {
19+
return logger.Load()
20+
}
21+
22+
// SetLogger sets the [*slog.Logger] used by bifrost.
23+
// The default handler disables logging.
24+
func SetLogger(l *slog.Logger) {
25+
logger.Store(l)
26+
}
27+
28+
func init() {
29+
SetLogger(slog.New(discardHandler{}))
30+
}
31+
32+
// discardHandler is an [slog.Handler] which is always disabled and therefore logs nothing.
33+
type discardHandler struct{}
34+
35+
func (discardHandler) Enabled(context.Context, slog.Level) bool { return false }
36+
func (discardHandler) Handle(context.Context, slog.Record) error { return nil }
37+
func (d discardHandler) WithAttrs([]slog.Attr) slog.Handler { return d }
38+
func (d discardHandler) WithGroup(string) slog.Handler { return d }

client.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ func (cr *certRefresher) GetClientCertificate(
5454
}
5555

5656
// If the certificate is nil or is going to expire soon, request a new one.
57-
if cert := cr.cert.Load(); cert == nil || cert.NotAfter.Before(time.Now().Add(-time.Minute*10)) {
57+
if cert := cr.cert.Load(); cert == nil ||
58+
cert.NotAfter.Before(time.Now().Add(-time.Minute*10)) {
59+
Logger().DebugContext(ctx, "refreshing client certificate")
60+
5861
cert, err := RequestCertificate(ctx, cr.url, cr.privkey)
5962
if err != nil {
6063
return nil, err
@@ -66,6 +69,7 @@ func (cr *certRefresher) GetClientCertificate(
6669
break
6770
}
6871
}
72+
Logger().InfoContext(ctx, "got new client certificate")
6973
}
7074

7175
tlsCert := X509ToTLSCertificate(cr.cert.Load().Certificate, cr.privkey.PrivateKey)

cmd/bf/ca.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import (
88
"encoding/pem"
99
"errors"
1010
"fmt"
11-
"log/slog"
1211
"net/http"
1312
"os"
1413
"os/signal"
1514
"time"
1615

16+
"github.com/RealImage/bifrost"
1717
"github.com/RealImage/bifrost/cafiles"
1818
"github.com/RealImage/bifrost/internal/webapp"
1919
"github.com/RealImage/bifrost/tinyca"
@@ -82,10 +82,10 @@ var caServeCmd = &cli.Command{
8282
Action: func(ctx context.Context, _ *cli.Command) error {
8383
cert, key, err := cafiles.GetCertKey(ctx, caCertUri, caPrivKeyUri)
8484
if err != nil {
85-
slog.ErrorContext(ctx, "error reading cert/key", "error", err)
85+
bifrost.Logger().ErrorContext(ctx, "error reading cert/key", "error", err)
8686
return cli.Exit("Error reading cert/key", 1)
8787
}
88-
slog.DebugContext(
88+
bifrost.Logger().DebugContext(
8989
ctx, "loaded CA certificate and private key",
9090
"subject", cert.Subject,
9191
"notBefore", cert.NotBefore,
@@ -94,13 +94,13 @@ var caServeCmd = &cli.Command{
9494

9595
gauntlet, err := tinyca.LoadGauntlet(gauntletPlugin)
9696
if err != nil {
97-
slog.ErrorContext(ctx, "error loading interceptor plugin", "error", err)
97+
bifrost.Logger().ErrorContext(ctx, "error loading interceptor plugin", "error", err)
9898
return cli.Exit("Error loading interceptor plugin", 1)
9999
}
100100

101101
ca, err := tinyca.New(cert, key, gauntlet)
102102
if err != nil {
103-
slog.ErrorContext(ctx, "error creating CA", "error", err)
103+
bifrost.Logger().ErrorContext(ctx, "error creating CA", "error", err)
104104
return cli.Exit("Error creating CA", 1)
105105
}
106106
defer ca.Close()
@@ -115,13 +115,14 @@ var caServeCmd = &cli.Command{
115115
}
116116

117117
addr := fmt.Sprintf("%s:%d", caHost, caPort)
118-
slog.InfoContext(ctx, "starting server", "address", addr, "namespace", cert.Namespace)
118+
bifrost.Logger().
119+
InfoContext(ctx, "starting server", "address", addr, "namespace", cert.Namespace)
119120

120121
server := http.Server{Addr: addr, Handler: hdlr}
121122

122123
go func() {
123124
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
124-
slog.ErrorContext(ctx, "error starting server", "error", err)
125+
bifrost.Logger().ErrorContext(ctx, "error starting server", "error", err)
125126
os.Exit(1)
126127
}
127128
}()
@@ -134,11 +135,11 @@ var caServeCmd = &cli.Command{
134135
ctx, cancel = context.WithTimeout(context.Background(), serverShutdownTimeout)
135136
defer cancel()
136137

137-
slog.DebugContext(ctx, "shutting down server")
138+
bifrost.Logger().DebugContext(ctx, "shutting down server")
138139
if err := server.Shutdown(ctx); err != nil {
139140
return err
140141
}
141-
slog.InfoContext(ctx, "server shut down")
142+
bifrost.Logger().InfoContext(ctx, "server shut down")
142143

143144
return nil
144145
},
@@ -174,20 +175,20 @@ var caIssueCmd = &cli.Command{
174175
Action: func(ctx context.Context, _ *cli.Command) error {
175176
caCert, caKey, err := cafiles.GetCertKey(ctx, caCertUri, caPrivKeyUri)
176177
if err != nil {
177-
slog.ErrorContext(ctx, "error reading cert/key", "error", err)
178+
bifrost.Logger().ErrorContext(ctx, "error reading cert/key", "error", err)
178179
return cli.Exit("Error reading cert/key", 1)
179180
}
180181

181182
ca, err := tinyca.New(caCert, caKey, nil)
182183
if err != nil {
183-
slog.ErrorContext(ctx, "error creating CA", "error", err)
184+
bifrost.Logger().ErrorContext(ctx, "error creating CA", "error", err)
184185
return cli.Exit("Error creating CA", 1)
185186
}
186187
defer ca.Close()
187188

188189
clientKey, err := cafiles.GetPrivateKey(ctx, clientPrivKeyUri)
189190
if err != nil {
190-
slog.ErrorContext(ctx, "error reading client key", "error", err)
191+
bifrost.Logger().ErrorContext(ctx, "error reading client key", "error", err)
191192
return cli.Exit("Error reading client key", 1)
192193
}
193194

@@ -198,30 +199,30 @@ var caIssueCmd = &cli.Command{
198199
},
199200
}, clientKey)
200201
if err != nil {
201-
slog.ErrorContext(ctx, "error creating certificate request", "error", err)
202+
bifrost.Logger().ErrorContext(ctx, "error creating certificate request", "error", err)
202203
return cli.Exit("Error creating certificate request", 1)
203204
}
204205

205206
notBefore, notAfter, err := tinyca.ParseValidity(notBeforeTime, notAfterTime)
206207
if err != nil {
207-
slog.ErrorContext(ctx, "error parsing validity", "error", err)
208+
bifrost.Logger().ErrorContext(ctx, "error parsing validity", "error", err)
208209
return cli.Exit("Error parsing validity", 1)
209210
}
210211

211212
cert, err := ca.IssueCertificate(csr, notBefore, notAfter)
212213
if err != nil {
213-
slog.ErrorContext(ctx, "error issuing certificate", "error", err)
214+
bifrost.Logger().ErrorContext(ctx, "error issuing certificate", "error", err)
214215
return cli.Exit("Error issuing certificate", 1)
215216
}
216217

217218
out, cls, err := getOutputWriter()
218219
if err != nil {
219-
slog.ErrorContext(ctx, "error getting output writer", "error", err)
220+
bifrost.Logger().ErrorContext(ctx, "error getting output writer", "error", err)
220221
return cli.Exit("Error getting output writer", 1)
221222
}
222223
defer func() {
223224
if err := cls(); err != nil {
224-
slog.ErrorContext(ctx, "error closing output writer", "error", err)
225+
bifrost.Logger().ErrorContext(ctx, "error closing output writer", "error", err)
225226
}
226227
}()
227228

cmd/bf/id.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"io"
7-
"log/slog"
87
"os"
98

109
"github.com/RealImage/bifrost"
@@ -36,7 +35,7 @@ var idCmd = &cli.Command{
3635

3736
id, err := bifrost.ParseIdentity(data)
3837
if err != nil {
39-
slog.ErrorContext(ctx, "error parsing id file", "error", err)
38+
bifrost.Logger().ErrorContext(ctx, "error parsing id file", "error", err)
4039
return cli.Exit("Error parsing file", 1)
4140
}
4241

@@ -46,15 +45,16 @@ var idCmd = &cli.Command{
4645

4746
// Either we got a namespace from the file or the namespace flag is set
4847
if id.Namespace != uuid.Nil && namespace != uuid.Nil && id.Namespace != namespace {
49-
slog.ErrorContext(ctx, "namespace mismatch", "file", id.Namespace, "flag", namespace)
48+
bifrost.Logger().
49+
ErrorContext(ctx, "namespace mismatch", "file", id.Namespace, "flag", namespace)
5050
return cli.Exit("Namespace mismatch", 1)
5151
}
5252

5353
if namespace != uuid.Nil {
5454
id.Namespace = namespace
5555
}
5656

57-
slog.Debug("using", "namespace", id.Namespace)
57+
bifrost.Logger().Debug("using", "namespace", id.Namespace)
5858
fmt.Println(id.UUID())
5959

6060
return nil

cmd/bf/main.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@ import (
55
"log/slog"
66
"os"
77

8+
"github.com/RealImage/bifrost"
89
"github.com/urfave/cli/v3"
910
)
1011

1112
var version = "devel"
1213

1314
func main() {
14-
logLevel := new(slog.LevelVar)
15-
hdlr := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: logLevel})
16-
slog.SetDefault(slog.New(hdlr))
15+
logger := slog.New(
16+
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: bifrost.LogLevel}),
17+
)
18+
19+
slog.SetDefault(logger)
20+
bifrost.SetLogger(logger)
1721

1822
cli := &cli.Command{
1923
Name: "bifrost",
@@ -27,7 +31,7 @@ func main() {
2731
Sources: cli.EnvVars("LOG_LEVEL"),
2832
Value: slog.LevelInfo.String(),
2933
Action: func(_ context.Context, _ *cli.Command, level string) error {
30-
return logLevel.UnmarshalText([]byte(level))
34+
return bifrost.LogLevel.UnmarshalText([]byte(level))
3135
},
3236
},
3337
},

cmd/bf/new.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"crypto/x509/pkix"
88
"encoding/pem"
99
"fmt"
10-
"log/slog"
1110

1211
"github.com/RealImage/bifrost"
1312
"github.com/RealImage/bifrost/cafiles"
@@ -35,7 +34,8 @@ var newCmd = &cli.Command{
3534
}
3635
defer func() {
3736
if err := cls(); err != nil {
38-
slog.ErrorContext(ctx, "error closing output writer", "error", err)
37+
bifrost.Logger().
38+
ErrorContext(ctx, "error closing output writer", "error", err)
3939
}
4040
}()
4141

@@ -67,7 +67,8 @@ var newCmd = &cli.Command{
6767
}
6868
defer func() {
6969
if err := cls(); err != nil {
70-
slog.ErrorContext(ctx, "error closing output writer", "error", err)
70+
bifrost.Logger().
71+
ErrorContext(ctx, "error closing output writer", "error", err)
7172
}
7273
}()
7374

@@ -110,7 +111,8 @@ var newCmd = &cli.Command{
110111
}
111112
defer func() {
112113
if err := cls(); err != nil {
113-
slog.ErrorContext(ctx, "error closing output writer", "error", err)
114+
bifrost.Logger().
115+
ErrorContext(ctx, "error closing output writer", "error", err)
114116
}
115117
}()
116118

@@ -173,7 +175,8 @@ var newCmd = &cli.Command{
173175
}
174176
defer func() {
175177
if err := cls(); err != nil {
176-
slog.ErrorContext(ctx, "error closing output writer", "error", err)
178+
bifrost.Logger().
179+
ErrorContext(ctx, "error closing output writer", "error", err)
177180
}
178181
}()
179182

0 commit comments

Comments
 (0)