2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-06-23 10:42:33 -04:00
|
|
|
/*
|
2024-02-08 09:20:01 -05:00
|
|
|
Package logger provides helper functions that can be used in combination with slog to increase functionality or make
|
|
|
|
working with slog easier.
|
2022-06-23 10:42:33 -04:00
|
|
|
|
2024-02-08 09:20:01 -05:00
|
|
|
1. Logging in unit tests
|
2022-06-23 10:42:33 -04:00
|
|
|
|
2024-02-08 09:20:01 -05: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)`.
|
2022-06-23 10:42:33 -04:00
|
|
|
|
2024-02-08 09:20:01 -05:00
|
|
|
2. Creating a new logger with an increased log level based on another logger
|
2022-06-23 10:42:33 -04:00
|
|
|
|
2024-02-08 09:20:01 -05: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:
|
2022-06-23 10:42:33 -04:00
|
|
|
|
2024-02-08 09:20:01 -05:00
|
|
|
slog.New(logger.NewLevelHandler(slog.LevelWarn, log.Handler()))
|
2022-06-23 10:42:33 -04:00
|
|
|
*/
|
|
|
|
package logger
|
|
|
|
|
|
|
|
import (
|
2023-08-29 08:07:19 -04:00
|
|
|
"context"
|
2024-02-08 09:20:01 -05:00
|
|
|
"log/slog"
|
2022-06-23 10:42:33 -04:00
|
|
|
"os"
|
2024-02-08 09:20:01 -05:00
|
|
|
"runtime"
|
2022-06-28 10:51:30 -04:00
|
|
|
"testing"
|
2024-02-08 09:20:01 -05:00
|
|
|
"time"
|
2022-06-23 10:42:33 -04:00
|
|
|
|
2023-08-29 08:07:19 -04:00
|
|
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
|
2022-06-23 10:42:33 -04:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
)
|
|
|
|
|
2022-06-28 10:51:30 -04:00
|
|
|
// ReplaceGRPCLogger replaces grpc's internal logger with the given logger.
|
2024-02-08 09:20:01 -05:00
|
|
|
func ReplaceGRPCLogger(l *slog.Logger) {
|
|
|
|
replaceGRPCLogger(l)
|
2022-06-23 10:42:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetServerUnaryInterceptor returns a gRPC server option for intercepting unary gRPC logs.
|
2024-02-08 09:20:01 -05:00
|
|
|
func GetServerUnaryInterceptor(l *slog.Logger) grpc.ServerOption {
|
2023-08-29 08:07:19 -04:00
|
|
|
return grpc.UnaryInterceptor(
|
2024-02-08 09:20:01 -05:00
|
|
|
logging.UnaryServerInterceptor(middlewareLogger(l)),
|
2023-08-29 08:07:19 -04:00
|
|
|
)
|
2022-06-23 10:42:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetServerStreamInterceptor returns a gRPC server option for intercepting streaming gRPC logs.
|
2024-02-08 09:20:01 -05:00
|
|
|
func GetServerStreamInterceptor(l *slog.Logger) grpc.ServerOption {
|
2023-08-29 08:07:19 -04:00
|
|
|
return grpc.StreamInterceptor(
|
2024-02-08 09:20:01 -05:00
|
|
|
logging.StreamServerInterceptor(middlewareLogger(l)),
|
2023-08-29 08:07:19 -04:00
|
|
|
)
|
2022-06-23 10:42:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetClientUnaryInterceptor returns a gRPC client option for intercepting unary gRPC logs.
|
2024-02-08 09:20:01 -05:00
|
|
|
func GetClientUnaryInterceptor(l *slog.Logger) grpc.DialOption {
|
2023-08-29 08:07:19 -04:00
|
|
|
return grpc.WithUnaryInterceptor(
|
2024-02-08 09:20:01 -05:00
|
|
|
logging.UnaryClientInterceptor(middlewareLogger(l)),
|
2023-08-29 08:07:19 -04:00
|
|
|
)
|
2022-06-23 10:42:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetClientStreamInterceptor returns a gRPC client option for intercepting stream gRPC logs.
|
2024-02-08 09:20:01 -05:00
|
|
|
func GetClientStreamInterceptor(l *slog.Logger) grpc.DialOption {
|
2023-08-29 08:07:19 -04:00
|
|
|
return grpc.WithStreamInterceptor(
|
2024-02-08 09:20:01 -05:00
|
|
|
logging.StreamClientInterceptor(middlewareLogger(l)),
|
2023-08-29 08:07:19 -04:00
|
|
|
)
|
2022-06-23 10:42:33 -04:00
|
|
|
}
|
2022-06-28 10:51:30 -04:00
|
|
|
|
2024-02-08 09:20:01 -05:00
|
|
|
func middlewareLogger(l *slog.Logger) logging.Logger {
|
2024-02-21 07:30:31 -05:00
|
|
|
return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) {
|
2024-02-08 09:20:01 -05:00
|
|
|
var pcs [1]uintptr
|
|
|
|
runtime.Callers(2, pcs[:]) // skip [Callers, LoggerFunc]
|
2023-08-29 08:07:19 -04:00
|
|
|
|
2024-02-08 09:20:01 -05:00
|
|
|
level := slog.LevelDebug
|
2023-08-29 08:07:19 -04:00
|
|
|
switch lvl {
|
|
|
|
case logging.LevelDebug:
|
2024-02-08 09:20:01 -05:00
|
|
|
break
|
2023-08-29 08:07:19 -04:00
|
|
|
case logging.LevelInfo:
|
2024-02-08 09:20:01 -05:00
|
|
|
level = slog.LevelInfo
|
2023-08-29 08:07:19 -04:00
|
|
|
case logging.LevelWarn:
|
2024-02-08 09:20:01 -05:00
|
|
|
level = slog.LevelWarn
|
2023-08-29 08:07:19 -04:00
|
|
|
case logging.LevelError:
|
2024-02-08 09:20:01 -05:00
|
|
|
level = slog.LevelError
|
2023-08-29 08:07:19 -04:00
|
|
|
default:
|
2024-02-08 09:20:01 -05:00
|
|
|
level = slog.LevelError
|
2023-08-29 08:07:19 -04:00
|
|
|
}
|
2024-02-08 09:20:01 -05:00
|
|
|
|
|
|
|
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
|
|
|
|
r.Add(fields...)
|
|
|
|
_ = l.Handler().Handle(context.Background(), r)
|
2023-08-29 08:07:19 -04:00
|
|
|
})
|
|
|
|
}
|
2024-02-08 09:20:01 -05: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
|
|
|
|
}
|