constellation/internal/logger/log.go

123 lines
3.7 KiB
Go
Raw Normal View History

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
/*
2024-02-08 14:20:01 +00:00
Package logger provides helper functions that can be used in combination with slog to increase functionality or make
working with slog easier.
2024-02-08 14:20:01 +00:00
1. Logging in unit tests
2024-02-08 14:20:01 +00:00
To log in unit tests you can create a new slog logger that uses logger.testWriter as its writer. This can be constructed
by creating a logger like this: `logger.NewTest(t)`.
2024-02-08 14:20:01 +00:00
2. Creating a new logger with an increased log level based on another logger
2024-02-08 14:20:01 +00:00
You can create a new logger with a new log level by creating a new slog.Logger with the LevelHandler in this package
and passing the handler of the other logger. As an example, if you have a slog.Logger named `log` you can create a
new logger with an increased log level (here slog.LevelWarn) like this:
2024-02-08 14:20:01 +00:00
slog.New(logger.NewLevelHandler(slog.LevelWarn, log.Handler()))
*/
package logger
import (
"context"
2024-02-08 14:20:01 +00:00
"log/slog"
"os"
2024-02-08 14:20:01 +00:00
"runtime"
"testing"
2024-02-08 14:20:01 +00:00
"time"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"google.golang.org/grpc"
)
// ReplaceGRPCLogger replaces grpc's internal logger with the given logger.
2024-02-08 14:20:01 +00:00
func ReplaceGRPCLogger(l *slog.Logger) {
replaceGRPCLogger(l)
}
// GetServerUnaryInterceptor returns a gRPC server option for intercepting unary gRPC logs.
2024-02-08 14:20:01 +00:00
func GetServerUnaryInterceptor(l *slog.Logger) grpc.ServerOption {
return grpc.UnaryInterceptor(
2024-02-08 14:20:01 +00:00
logging.UnaryServerInterceptor(middlewareLogger(l)),
)
}
// GetServerStreamInterceptor returns a gRPC server option for intercepting streaming gRPC logs.
2024-02-08 14:20:01 +00:00
func GetServerStreamInterceptor(l *slog.Logger) grpc.ServerOption {
return grpc.StreamInterceptor(
2024-02-08 14:20:01 +00:00
logging.StreamServerInterceptor(middlewareLogger(l)),
)
}
// GetClientUnaryInterceptor returns a gRPC client option for intercepting unary gRPC logs.
2024-02-08 14:20:01 +00:00
func GetClientUnaryInterceptor(l *slog.Logger) grpc.DialOption {
return grpc.WithUnaryInterceptor(
2024-02-08 14:20:01 +00:00
logging.UnaryClientInterceptor(middlewareLogger(l)),
)
}
// GetClientStreamInterceptor returns a gRPC client option for intercepting stream gRPC logs.
2024-02-08 14:20:01 +00:00
func GetClientStreamInterceptor(l *slog.Logger) grpc.DialOption {
return grpc.WithStreamInterceptor(
2024-02-08 14:20:01 +00:00
logging.StreamClientInterceptor(middlewareLogger(l)),
)
}
2024-02-08 14:20:01 +00:00
func middlewareLogger(l *slog.Logger) logging.Logger {
return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) {
2024-02-08 14:20:01 +00:00
var pcs [1]uintptr
runtime.Callers(2, pcs[:]) // skip [Callers, LoggerFunc]
2024-02-08 14:20:01 +00:00
level := slog.LevelDebug
switch lvl {
case logging.LevelDebug:
2024-02-08 14:20:01 +00:00
break
case logging.LevelInfo:
2024-02-08 14:20:01 +00:00
level = slog.LevelInfo
case logging.LevelWarn:
2024-02-08 14:20:01 +00:00
level = slog.LevelWarn
case logging.LevelError:
2024-02-08 14:20:01 +00:00
level = slog.LevelError
default:
2024-02-08 14:20:01 +00:00
level = slog.LevelError
}
2024-02-08 14:20:01 +00:00
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
r.Add(fields...)
_ = l.Handler().Handle(context.Background(), r)
})
}
2024-02-08 14:20:01 +00:00
// NewTextLogger creates a new slog.Logger that writes text formatted log messages
// to os.Stderr.
func NewTextLogger(level slog.Level) *slog.Logger {
return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{AddSource: true, Level: level}))
}
// NewJSONLogger creates a new slog.Logger that writes JSON formatted log messages
// to os.Stderr.
func NewJSONLogger(level slog.Level) *slog.Logger {
return slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{AddSource: true, Level: level}))
}
// NewTest creates a new slog.Logger that writes to a testing.T.
func NewTest(t *testing.T) *slog.Logger {
return slog.New(slog.NewTextHandler(testWriter{t: t}, &slog.HandlerOptions{AddSource: true}))
}
// TestWriter is a writer to a testing.T used in tests for logging with slog.
type testWriter struct {
t *testing.T
}
func (t testWriter) Write(p []byte) (int, error) {
t.t.Helper()
t.t.Log(string(p))
return len(p), nil
}