Skip to content

Commit

Permalink
feat: Use a global logger (#19)
Browse files Browse the repository at this point in the history
Use a global logger for all library and application logs.
Users can set this log to customise log output.
  • Loading branch information
ananthb authored Dec 25, 2024
1 parent b1152bb commit 6f38eff
Show file tree
Hide file tree
Showing 14 changed files with 113 additions and 68 deletions.
11 changes: 5 additions & 6 deletions asgard/heimdallr.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ package asgard
import (
"context"
"encoding/pem"
"log/slog"
"net/http"
"net/url"

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

certHeader := r.Header.Get(h.String())
if certHeader == "" {
slog.ErrorContext(ctx, "missing authorization header")
bifrost.Logger().ErrorContext(ctx, "missing authorization header")
http.Error(w, errBadAuthHeader, http.StatusServiceUnavailable)
return
}

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

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

cert, err := bifrost.ParseCertificate(block.Bytes)
if err != nil {
slog.ErrorContext(ctx, "error parsing client certificate", "error", err)
bifrost.Logger().ErrorContext(ctx, "error parsing client certificate", "error", err)
http.Error(w, errBadAuthHeader, http.StatusServiceUnavailable)
return
}

if cert.Namespace != ns {
slog.ErrorContext(
bifrost.Logger().ErrorContext(
ctx, "client certificate namespace mismatch",
"expected", ns,
"actual", cert.Namespace,
Expand Down
6 changes: 3 additions & 3 deletions asgard/hofund.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package asgard

import (
"encoding/pem"
"log/slog"
"net/http"
"net/url"

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

cert, err := bifrost.NewCertificate(r.TLS.PeerCertificates[0])
if err != nil {
slog.ErrorContext(ctx, "error validating client certificate", "error", err)
bifrost.Logger().
ErrorContext(ctx, "error validating client certificate", "error", err)
http.Error(w, "invalid client certificate", http.StatusUnauthorized)
return
}

if cert.Namespace != ns {
slog.ErrorContext(
bifrost.Logger().ErrorContext(
ctx, "client certificate namespace mismatch",
"expected", ns,
"actual", cert.Namespace,
Expand Down
38 changes: 38 additions & 0 deletions bifrost.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Package bifrost contains an API client for the Bifrost CA service.
package bifrost

import (
"context"
"log/slog"
"sync/atomic"
)

var (
// LogLevel is the log level used by the bifrost logger.
LogLevel = new(slog.LevelVar)

logger atomic.Pointer[slog.Logger]
)

// Logger returns the global Bifrost logger.
func Logger() *slog.Logger {
return logger.Load()
}

// SetLogger sets the [*slog.Logger] used by bifrost.
// The default handler disables logging.
func SetLogger(l *slog.Logger) {
logger.Store(l)
}

func init() {
SetLogger(slog.New(discardHandler{}))
}

// discardHandler is an [slog.Handler] which is always disabled and therefore logs nothing.
type discardHandler struct{}

func (discardHandler) Enabled(context.Context, slog.Level) bool { return false }
func (discardHandler) Handle(context.Context, slog.Record) error { return nil }
func (d discardHandler) WithAttrs([]slog.Attr) slog.Handler { return d }
func (d discardHandler) WithGroup(string) slog.Handler { return d }
6 changes: 5 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ func (cr *certRefresher) GetClientCertificate(
}

// If the certificate is nil or is going to expire soon, request a new one.
if cert := cr.cert.Load(); cert == nil || cert.NotAfter.Before(time.Now().Add(-time.Minute*10)) {
if cert := cr.cert.Load(); cert == nil ||
cert.NotAfter.Before(time.Now().Add(-time.Minute*10)) {
Logger().DebugContext(ctx, "refreshing client certificate")

cert, err := RequestCertificate(ctx, cr.url, cr.privkey)
if err != nil {
return nil, err
Expand All @@ -66,6 +69,7 @@ func (cr *certRefresher) GetClientCertificate(
break
}
}
Logger().InfoContext(ctx, "got new client certificate")
}

tlsCert := X509ToTLSCertificate(cr.cert.Load().Certificate, cr.privkey.PrivateKey)
Expand Down
35 changes: 18 additions & 17 deletions cmd/bf/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
"encoding/pem"
"errors"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"time"

"github.com/RealImage/bifrost"
"github.com/RealImage/bifrost/cafiles"
"github.com/RealImage/bifrost/internal/webapp"
"github.com/RealImage/bifrost/tinyca"
Expand Down Expand Up @@ -82,10 +82,10 @@ var caServeCmd = &cli.Command{
Action: func(ctx context.Context, _ *cli.Command) error {
cert, key, err := cafiles.GetCertKey(ctx, caCertUri, caPrivKeyUri)
if err != nil {
slog.ErrorContext(ctx, "error reading cert/key", "error", err)
bifrost.Logger().ErrorContext(ctx, "error reading cert/key", "error", err)
return cli.Exit("Error reading cert/key", 1)
}
slog.DebugContext(
bifrost.Logger().DebugContext(
ctx, "loaded CA certificate and private key",
"subject", cert.Subject,
"notBefore", cert.NotBefore,
Expand All @@ -94,13 +94,13 @@ var caServeCmd = &cli.Command{

gauntlet, err := tinyca.LoadGauntlet(gauntletPlugin)
if err != nil {
slog.ErrorContext(ctx, "error loading interceptor plugin", "error", err)
bifrost.Logger().ErrorContext(ctx, "error loading interceptor plugin", "error", err)
return cli.Exit("Error loading interceptor plugin", 1)
}

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

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

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

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

slog.DebugContext(ctx, "shutting down server")
bifrost.Logger().DebugContext(ctx, "shutting down server")
if err := server.Shutdown(ctx); err != nil {
return err
}
slog.InfoContext(ctx, "server shut down")
bifrost.Logger().InfoContext(ctx, "server shut down")

return nil
},
Expand Down Expand Up @@ -174,20 +175,20 @@ var caIssueCmd = &cli.Command{
Action: func(ctx context.Context, _ *cli.Command) error {
caCert, caKey, err := cafiles.GetCertKey(ctx, caCertUri, caPrivKeyUri)
if err != nil {
slog.ErrorContext(ctx, "error reading cert/key", "error", err)
bifrost.Logger().ErrorContext(ctx, "error reading cert/key", "error", err)
return cli.Exit("Error reading cert/key", 1)
}

ca, err := tinyca.New(caCert, caKey, nil)
if err != nil {
slog.ErrorContext(ctx, "error creating CA", "error", err)
bifrost.Logger().ErrorContext(ctx, "error creating CA", "error", err)
return cli.Exit("Error creating CA", 1)
}
defer ca.Close()

clientKey, err := cafiles.GetPrivateKey(ctx, clientPrivKeyUri)
if err != nil {
slog.ErrorContext(ctx, "error reading client key", "error", err)
bifrost.Logger().ErrorContext(ctx, "error reading client key", "error", err)
return cli.Exit("Error reading client key", 1)
}

Expand All @@ -198,30 +199,30 @@ var caIssueCmd = &cli.Command{
},
}, clientKey)
if err != nil {
slog.ErrorContext(ctx, "error creating certificate request", "error", err)
bifrost.Logger().ErrorContext(ctx, "error creating certificate request", "error", err)
return cli.Exit("Error creating certificate request", 1)
}

notBefore, notAfter, err := tinyca.ParseValidity(notBeforeTime, notAfterTime)
if err != nil {
slog.ErrorContext(ctx, "error parsing validity", "error", err)
bifrost.Logger().ErrorContext(ctx, "error parsing validity", "error", err)
return cli.Exit("Error parsing validity", 1)
}

cert, err := ca.IssueCertificate(csr, notBefore, notAfter)
if err != nil {
slog.ErrorContext(ctx, "error issuing certificate", "error", err)
bifrost.Logger().ErrorContext(ctx, "error issuing certificate", "error", err)
return cli.Exit("Error issuing certificate", 1)
}

out, cls, err := getOutputWriter()
if err != nil {
slog.ErrorContext(ctx, "error getting output writer", "error", err)
bifrost.Logger().ErrorContext(ctx, "error getting output writer", "error", err)
return cli.Exit("Error getting output writer", 1)
}
defer func() {
if err := cls(); err != nil {
slog.ErrorContext(ctx, "error closing output writer", "error", err)
bifrost.Logger().ErrorContext(ctx, "error closing output writer", "error", err)
}
}()

Expand Down
8 changes: 4 additions & 4 deletions cmd/bf/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"log/slog"
"os"

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

id, err := bifrost.ParseIdentity(data)
if err != nil {
slog.ErrorContext(ctx, "error parsing id file", "error", err)
bifrost.Logger().ErrorContext(ctx, "error parsing id file", "error", err)
return cli.Exit("Error parsing file", 1)
}

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

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

if namespace != uuid.Nil {
id.Namespace = namespace
}

slog.Debug("using", "namespace", id.Namespace)
bifrost.Logger().Debug("using", "namespace", id.Namespace)
fmt.Println(id.UUID())

return nil
Expand Down
12 changes: 8 additions & 4 deletions cmd/bf/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import (
"log/slog"
"os"

"github.com/RealImage/bifrost"
"github.com/urfave/cli/v3"
)

var version = "devel"

func main() {
logLevel := new(slog.LevelVar)
hdlr := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: logLevel})
slog.SetDefault(slog.New(hdlr))
logger := slog.New(
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: bifrost.LogLevel}),
)

slog.SetDefault(logger)
bifrost.SetLogger(logger)

cli := &cli.Command{
Name: "bifrost",
Expand All @@ -27,7 +31,7 @@ func main() {
Sources: cli.EnvVars("LOG_LEVEL"),
Value: slog.LevelInfo.String(),
Action: func(_ context.Context, _ *cli.Command, level string) error {
return logLevel.UnmarshalText([]byte(level))
return bifrost.LogLevel.UnmarshalText([]byte(level))
},
},
},
Expand Down
13 changes: 8 additions & 5 deletions cmd/bf/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log/slog"

"github.com/RealImage/bifrost"
"github.com/RealImage/bifrost/cafiles"
Expand Down Expand Up @@ -35,7 +34,8 @@ var newCmd = &cli.Command{
}
defer func() {
if err := cls(); err != nil {
slog.ErrorContext(ctx, "error closing output writer", "error", err)
bifrost.Logger().
ErrorContext(ctx, "error closing output writer", "error", err)
}
}()

Expand Down Expand Up @@ -67,7 +67,8 @@ var newCmd = &cli.Command{
}
defer func() {
if err := cls(); err != nil {
slog.ErrorContext(ctx, "error closing output writer", "error", err)
bifrost.Logger().
ErrorContext(ctx, "error closing output writer", "error", err)
}
}()

Expand Down Expand Up @@ -110,7 +111,8 @@ var newCmd = &cli.Command{
}
defer func() {
if err := cls(); err != nil {
slog.ErrorContext(ctx, "error closing output writer", "error", err)
bifrost.Logger().
ErrorContext(ctx, "error closing output writer", "error", err)
}
}()

Expand Down Expand Up @@ -173,7 +175,8 @@ var newCmd = &cli.Command{
}
defer func() {
if err := cls(); err != nil {
slog.ErrorContext(ctx, "error closing output writer", "error", err)
bifrost.Logger().
ErrorContext(ctx, "error closing output writer", "error", err)
}
}()

Expand Down
Loading

0 comments on commit 6f38eff

Please sign in to comment.