diff --git a/interceptors/logging/examples/kit/example.go b/interceptors/logging/examples/kit/example.go new file mode 100644 index 000000000..ccae658aa --- /dev/null +++ b/interceptors/logging/examples/kit/example.go @@ -0,0 +1,33 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +package examplekit + +import ( + "context" + "fmt" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" +) + +// InterceptorLogger adapts go-kit logger to interceptor logger. +// This code is simple enough to be copied and not imported. +func InterceptorLogger(l log.Logger) logging.Logger { + return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { + largs := append([]any{"msg", msg}, fields...) + switch lvl { + case logging.LevelDebug: + _ = level.Debug(l).Log(largs...) + case logging.LevelInfo: + _ = level.Info(l).Log(largs...) + case logging.LevelWarn: + _ = level.Warn(l).Log(largs...) + case logging.LevelError: + _ = level.Error(l).Log(largs...) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + }) +} diff --git a/interceptors/logging/examples/kit/example_test.go b/interceptors/logging/examples/kit/example_test.go index e41bada31..b79f6b999 100644 --- a/interceptors/logging/examples/kit/example_test.go +++ b/interceptors/logging/examples/kit/example_test.go @@ -1,70 +1,67 @@ // Copyright (c) The go-grpc-middleware Authors. // Licensed under the Apache License 2.0. -package kit_test +package examplekit_test import ( + "bytes" "context" - "fmt" + "runtime" + "strings" + "testing" "github.com/go-kit/log" - "github.com/go-kit/log/level" + examplekit "github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples/kit" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "google.golang.org/grpc" ) -// InterceptorLogger adapts go-kit logger to interceptor logger. -// This code is simple enough to be copied and not imported. -func InterceptorLogger(l log.Logger) logging.Logger { - return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { - largs := append([]any{"msg", msg}, fields...) - switch lvl { - case logging.LevelDebug: - _ = level.Debug(l).Log(largs...) - case logging.LevelInfo: - _ = level.Info(l).Log(largs...) - case logging.LevelWarn: - _ = level.Warn(l).Log(largs...) - case logging.LevelError: - _ = level.Error(l).Log(largs...) - default: - panic(fmt.Sprintf("unknown level %v", lvl)) - } - }) +type kitExampleTestSuite struct { + *testpb.InterceptorTestSuite + logBuffer *bytes.Buffer } -func ExampleInterceptorLogger() { - logger := log.NewNopLogger() +func TestSuite(t *testing.T) { + if strings.HasPrefix(runtime.Version(), "go1.7") { + t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") + return + } + + buffer := &bytes.Buffer{} + logger := examplekit.InterceptorLogger(log.NewLogfmtLogger(buffer)) + + s := &kitExampleTestSuite{ + InterceptorTestSuite: &testpb.InterceptorTestSuite{ + TestService: &testpb.TestPingService{}, + }, + logBuffer: buffer, + } - opts := []logging.Option{ - logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), - // Add any other option (check functions starting with logging.With). + s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ + grpc.StreamInterceptor(logging.StreamServerInterceptor(logger)), + grpc.UnaryInterceptor(logging.UnaryServerInterceptor(logger)), } - // You can now create a server with logging instrumentation that e.g. logs when the unary or stream call is started or finished. - _ = grpc.NewServer( - grpc.ChainUnaryInterceptor( - logging.UnaryServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.ChainStreamInterceptor( - logging.StreamServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // ...user server. + suite.Run(t, s) +} + +func (s *kitExampleTestSuite) TestPing() { + ctx := context.Background() + _, err := s.Client.Ping(ctx, testpb.GoodPing) + assert.NoError(s.T(), err, "there must be not be an on a successful call") + logStr := s.logBuffer.String() + require.Contains(s.T(), logStr, "level=info") + require.Contains(s.T(), logStr, "msg=\"started call\"") + require.Contains(s.T(), logStr, "protocol=grpc") + require.Contains(s.T(), logStr, "grpc.component=server") + require.Contains(s.T(), logStr, "grpc.service=testing.testpb.v1.TestService") + require.Contains(s.T(), logStr, "grpc.method=Ping") + require.Contains(s.T(), logStr, "grpc.method_type=unary") + require.Contains(s.T(), logStr, "start_time=") + require.Contains(s.T(), logStr, "grpc.time_ms=") - // Similarly you can create client that will log for the unary and stream client started or finished calls. - _, _ = grpc.Dial( - "some-target", - grpc.WithChainUnaryInterceptor( - logging.UnaryClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.WithChainStreamInterceptor( - logging.StreamClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // Output: } diff --git a/interceptors/logging/examples/log/example.go b/interceptors/logging/examples/log/example.go new file mode 100644 index 000000000..604929e7a --- /dev/null +++ b/interceptors/logging/examples/log/example.go @@ -0,0 +1,32 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +package examplelog + +import ( + "context" + "fmt" + "log" + + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" +) + +// InterceptorLogger adapts standard Go logger to interceptor logger. +// This code is simple enough to be copied and not imported. +func InterceptorLogger(l *log.Logger) logging.Logger { + return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { + switch lvl { + case logging.LevelDebug: + msg = fmt.Sprintf("DEBUG :%v", msg) + case logging.LevelInfo: + msg = fmt.Sprintf("INFO :%v", msg) + case logging.LevelWarn: + msg = fmt.Sprintf("WARN :%v", msg) + case logging.LevelError: + msg = fmt.Sprintf("ERROR :%v", msg) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + l.Println(append([]any{"msg", msg}, fields...)) + }) +} diff --git a/interceptors/logging/examples/log/example_test.go b/interceptors/logging/examples/log/example_test.go index 44dacc56e..3eebe6355 100644 --- a/interceptors/logging/examples/log/example_test.go +++ b/interceptors/logging/examples/log/example_test.go @@ -1,70 +1,66 @@ // Copyright (c) The go-grpc-middleware Authors. // Licensed under the Apache License 2.0. -package log_test +package examplelog_test import ( + "bytes" "context" - "fmt" "log" - "os" + "runtime" + "strings" + "testing" + examplelog "github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples/log" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "google.golang.org/grpc" ) -// InterceptorLogger adapts standard Go logger to interceptor logger. -// This code is simple enough to be copied and not imported. -func InterceptorLogger(l *log.Logger) logging.Logger { - return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { - switch lvl { - case logging.LevelDebug: - msg = fmt.Sprintf("DEBUG :%v", msg) - case logging.LevelInfo: - msg = fmt.Sprintf("INFO :%v", msg) - case logging.LevelWarn: - msg = fmt.Sprintf("WARN :%v", msg) - case logging.LevelError: - msg = fmt.Sprintf("ERROR :%v", msg) - default: - panic(fmt.Sprintf("unknown level %v", lvl)) - } - l.Println(append([]any{"msg", msg}, fields...)) - }) +type logExampleTestSuite struct { + *testpb.InterceptorTestSuite + logBuffer *bytes.Buffer } -func ExampleInterceptorLogger() { - logger := log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile) +func TestSuite(t *testing.T) { + if strings.HasPrefix(runtime.Version(), "go1.7") { + t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") + return + } + buffer := &bytes.Buffer{} + logger := examplelog.InterceptorLogger(log.New(buffer, "", 0)) + + s := &logExampleTestSuite{ + InterceptorTestSuite: &testpb.InterceptorTestSuite{ + TestService: &testpb.TestPingService{}, + }, + logBuffer: buffer, + } - opts := []logging.Option{ - logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), - // Add any other option (check functions starting with logging.With). + s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ + grpc.StreamInterceptor(logging.StreamServerInterceptor(logger)), + grpc.UnaryInterceptor(logging.UnaryServerInterceptor(logger)), } - // You can now create a server with logging instrumentation that e.g. logs when the unary or stream call is started or finished. - _ = grpc.NewServer( - grpc.ChainUnaryInterceptor( - logging.UnaryServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.ChainStreamInterceptor( - logging.StreamServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // ...user server. + suite.Run(t, s) +} + +func (s *logExampleTestSuite) TestPing() { + ctx := context.Background() + _, err := s.Client.Ping(ctx, testpb.GoodPing) + assert.NoError(s.T(), err, "there must be not be an on a successful call") + logStr := s.logBuffer.String() + require.Contains(s.T(), logStr, "msg INFO") + require.Contains(s.T(), logStr, ":started call") + require.Contains(s.T(), logStr, "protocol grpc") + require.Contains(s.T(), logStr, "grpc.component server") + require.Contains(s.T(), logStr, "grpc.service testing.testpb.v1.TestService") + require.Contains(s.T(), logStr, "grpc.method Ping") + require.Contains(s.T(), logStr, "grpc.method_type unary") + require.Contains(s.T(), logStr, "start_time ") + require.Contains(s.T(), logStr, "grpc.time_ms") - // Similarly you can create client that will log for the unary and stream client started or finished calls. - _, _ = grpc.Dial( - "some-target", - grpc.WithChainUnaryInterceptor( - logging.UnaryClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.WithChainStreamInterceptor( - logging.StreamClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // Output: } diff --git a/interceptors/logging/examples/logr/example.go b/interceptors/logging/examples/logr/example.go new file mode 100644 index 000000000..ab2edaa11 --- /dev/null +++ b/interceptors/logging/examples/logr/example.go @@ -0,0 +1,40 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +package examplelogr + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" +) + +// verbosity https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md#what-method-to-use +const ( + debugVerbosity = 4 + infoVerbosity = 2 + warnVerbosity = 1 + errorVerbosity = 0 +) + +// InterceptorLogger adapts logr logger to interceptor logger. +// This code is simple enough to be copied and not imported. +func InterceptorLogger(l logr.Logger) logging.Logger { + return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { + l = l.WithValues(fields...) + switch lvl { + case logging.LevelDebug: + l.V(debugVerbosity).Info(msg) + case logging.LevelInfo: + l.V(infoVerbosity).Info(msg) + case logging.LevelWarn: + l.V(warnVerbosity).Info(msg) + case logging.LevelError: + l.V(errorVerbosity).Info(msg) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + }) +} diff --git a/interceptors/logging/examples/logr/example_test.go b/interceptors/logging/examples/logr/example_test.go index 1b1701bc2..ca278229d 100644 --- a/interceptors/logging/examples/logr/example_test.go +++ b/interceptors/logging/examples/logr/example_test.go @@ -1,78 +1,65 @@ // Copyright (c) The go-grpc-middleware Authors. // Licensed under the Apache License 2.0. -package logr_test +package examplelogr_test import ( "context" - "fmt" + "runtime" + "strings" + "testing" - "github.com/go-logr/logr" + examplelogr "github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples/logr" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "google.golang.org/grpc" - "k8s.io/klog/v2" + "k8s.io/klog/v2/ktesting" ) -// verbosity https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md#what-method-to-use -const ( - debugVerbosity = 4 - infoVerbosity = 2 - warnVerbosity = 1 - errorVerbosity = 0 -) - -// InterceptorLogger adapts logr logger to interceptor logger. -// This code is simple enough to be copied and not imported. -func InterceptorLogger(l logr.Logger) logging.Logger { - return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { - l = l.WithValues(fields...) - switch lvl { - case logging.LevelDebug: - l.V(debugVerbosity).Info(msg) - case logging.LevelInfo: - l.V(infoVerbosity).Info(msg) - case logging.LevelWarn: - l.V(warnVerbosity).Info(msg) - case logging.LevelError: - l.V(errorVerbosity).Info(msg) - default: - panic(fmt.Sprintf("unknown level %v", lvl)) - } - }) +type logrExampleTestSuite struct { + *testpb.InterceptorTestSuite + logBuffer *ktesting.BufferTL } -func ExampleInterceptorLogger() { - logger := klog.NewKlogr() +func TestSuite(t *testing.T) { + if strings.HasPrefix(runtime.Version(), "go1.7") { + t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") + return + } + + buffer := &ktesting.BufferTL{} + cfg := ktesting.NewConfig() + logger := examplelogr.InterceptorLogger(ktesting.NewLogger(buffer, cfg)) - opts := []logging.Option{ - logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), - // Add any other option (check functions starting with logging.With). + s := &logrExampleTestSuite{ + InterceptorTestSuite: &testpb.InterceptorTestSuite{ + TestService: &testpb.TestPingService{}, + }, + logBuffer: buffer, } - // You can now create a server with logging instrumentation that e.g. logs when the unary or stream call is started or finished. - _ = grpc.NewServer( - grpc.ChainUnaryInterceptor( - logging.UnaryServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.ChainStreamInterceptor( - logging.StreamServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // ...user server. + s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ + grpc.StreamInterceptor(logging.StreamServerInterceptor(logger)), + grpc.UnaryInterceptor(logging.UnaryServerInterceptor(logger)), + } + + suite.Run(t, s) +} - // Similarly you can create client that will log for the unary and stream client started or finished calls. - _, _ = grpc.Dial( - "some-target", - grpc.WithChainUnaryInterceptor( - logging.UnaryClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.WithChainStreamInterceptor( - logging.StreamClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // Output: +func (s *logrExampleTestSuite) TestPing() { + ctx := context.Background() + _, err := s.Client.Ping(ctx, testpb.GoodPing) + assert.NoError(s.T(), err, "there must be not be an on a successful call") + logStr := s.logBuffer.String() + require.Contains(s.T(), logStr, "started call") + require.Contains(s.T(), logStr, "protocol=\"grpc\"") + require.Contains(s.T(), logStr, "grpc.component=\"server\"") + require.Contains(s.T(), logStr, "grpc.service=\"testing.testpb.v1.TestService\"") + require.Contains(s.T(), logStr, "grpc.method=\"Ping\"") + require.Contains(s.T(), logStr, "grpc.method_type=\"unary\"") + require.Contains(s.T(), logStr, "start_time=") + require.Contains(s.T(), logStr, "grpc.time_ms=") } diff --git a/interceptors/logging/examples/logrus/example.go b/interceptors/logging/examples/logrus/example.go new file mode 100644 index 000000000..64d6884a1 --- /dev/null +++ b/interceptors/logging/examples/logrus/example.go @@ -0,0 +1,39 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +package examplelogrus + +import ( + "context" + "fmt" + + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/sirupsen/logrus" +) + +// InterceptorLogger adapts logrus logger to interceptor logger. +// This code is simple enough to be copied and not imported. +func InterceptorLogger(l logrus.FieldLogger) logging.Logger { + return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { + f := make(map[string]any, len(fields)/2) + i := logging.Fields(fields).Iterator() + for i.Next() { + k, v := i.At() + f[k] = v + } + l = l.WithFields(f) + + switch lvl { + case logging.LevelDebug: + l.Debug(msg) + case logging.LevelInfo: + l.Info(msg) + case logging.LevelWarn: + l.Warn(msg) + case logging.LevelError: + l.Error(msg) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + }) +} diff --git a/interceptors/logging/examples/logrus/example_test.go b/interceptors/logging/examples/logrus/example_test.go index beb8ad843..f0d1f5f8e 100644 --- a/interceptors/logging/examples/logrus/example_test.go +++ b/interceptors/logging/examples/logrus/example_test.go @@ -1,76 +1,73 @@ // Copyright (c) The go-grpc-middleware Authors. // Licensed under the Apache License 2.0. -package logrus_test +package examplelogrus_test import ( + "bytes" "context" - "fmt" + "encoding/json" + "runtime" + "strings" + "testing" + examplelogrus "github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples/logrus" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb" "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "google.golang.org/grpc" ) -// InterceptorLogger adapts logrus logger to interceptor logger. -// This code is simple enough to be copied and not imported. -func InterceptorLogger(l logrus.FieldLogger) logging.Logger { - return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) { - f := make(map[string]any, len(fields)/2) - i := logging.Fields(fields).Iterator() - if i.Next() { - k, v := i.At() - f[k] = v - } - l = l.WithFields(f) - - switch lvl { - case logging.LevelDebug: - l.Debug(msg) - case logging.LevelInfo: - l.Info(msg) - case logging.LevelWarn: - l.Warn(msg) - case logging.LevelError: - l.Error(msg) - default: - panic(fmt.Sprintf("unknown level %v", lvl)) - } - }) +type logrusExampleTestSuite struct { + *testpb.InterceptorTestSuite + logBuffer *bytes.Buffer } -func ExampleInterceptorLogger() { - logger := logrus.New() +func TestSuite(t *testing.T) { + if strings.HasPrefix(runtime.Version(), "go1.7") { + t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") + return + } + + buffer := &bytes.Buffer{} + testLogger := logrus.New() + testLogger.Out = buffer + testLogger.SetFormatter(&logrus.JSONFormatter{}) + logger := examplelogrus.InterceptorLogger(testLogger) - opts := []logging.Option{ - logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), - // Add any other option (check functions starting with logging.With). + s := &logrusExampleTestSuite{ + InterceptorTestSuite: &testpb.InterceptorTestSuite{ + TestService: &testpb.TestPingService{}, + }, + logBuffer: buffer, } - // You can now create a server with logging instrumentation that e.g. logs when the unary or stream call is started or finished. - _ = grpc.NewServer( - grpc.ChainUnaryInterceptor( - logging.UnaryServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.ChainStreamInterceptor( - logging.StreamServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // ...user server. + s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ + grpc.StreamInterceptor(logging.StreamServerInterceptor(logger, logging.WithLogOnEvents(logging.StartCall))), + grpc.UnaryInterceptor(logging.UnaryServerInterceptor(logger, logging.WithLogOnEvents(logging.StartCall))), + } + + suite.Run(t, s) +} + +func (s *logrusExampleTestSuite) TestPing() { + ctx := context.Background() + _, err := s.Client.Ping(ctx, testpb.GoodPing) + assert.NoError(s.T(), err, "there must be not be an on a successful call") + var logMap map[string]interface{} + err = json.Unmarshal(s.logBuffer.Bytes(), &logMap) + require.NoError(s.T(), err) - // Similarly you can create client that will log for the unary and stream client started or finished calls. - _, _ = grpc.Dial( - "some-target", - grpc.WithChainUnaryInterceptor( - logging.UnaryClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.WithChainStreamInterceptor( - logging.StreamClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // Output: + require.Equal(s.T(), "started call", logMap["msg"]) + require.Equal(s.T(), "grpc", logMap["protocol"]) + require.Equal(s.T(), "server", logMap["grpc.component"]) + require.Equal(s.T(), "testing.testpb.v1.TestService", logMap["grpc.service"]) + require.Equal(s.T(), "Ping", logMap["grpc.method"]) + require.Equal(s.T(), "unary", logMap["grpc.method_type"]) + require.Contains(s.T(), logMap["peer.address"], "127.0.0.1") + require.NotEmpty(s.T(), logMap["grpc.start_time"]) + require.NotEmpty(s.T(), logMap["grpc.time_ms"]) } diff --git a/interceptors/logging/examples/slog/example.go b/interceptors/logging/examples/slog/example.go new file mode 100644 index 000000000..7385502cb --- /dev/null +++ b/interceptors/logging/examples/slog/example.go @@ -0,0 +1,18 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +package exampleslog + +import ( + "context" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "golang.org/x/exp/slog" +) + +// InterceptorLogger adapts slog logger to interceptor logger. +// This code is simple enough to be copied and not imported. +func InterceptorLogger(l *slog.Logger) logging.Logger { + return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { + l.Log(ctx, slog.Level(lvl), msg, fields...) + }) +} diff --git a/interceptors/logging/examples/slog/example_test.go b/interceptors/logging/examples/slog/example_test.go index 0398b72d6..9022879f8 100644 --- a/interceptors/logging/examples/slog/example_test.go +++ b/interceptors/logging/examples/slog/example_test.go @@ -1,57 +1,63 @@ // Copyright (c) The go-grpc-middleware Authors. // Licensed under the Apache License 2.0. -package slog_test +package exampleslog_test import ( + "bytes" "context" - "os" + "runtime" + "strings" + "testing" + exampleslog "github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples/slog" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "golang.org/x/exp/slog" "google.golang.org/grpc" ) -// InterceptorLogger adapts slog logger to interceptor logger. -// This code is simple enough to be copied and not imported. -func InterceptorLogger(l *slog.Logger) logging.Logger { - return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { - l.Log(ctx, slog.Level(lvl), msg, fields...) - }) +type slogExampleTestSuite struct { + *testpb.InterceptorTestSuite + logBuffer *bytes.Buffer } -func ExampleInterceptorLogger() { - logger := slog.New(slog.NewTextHandler(os.Stderr)) +func TestSuite(t *testing.T) { + if strings.HasPrefix(runtime.Version(), "go1.7") { + t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") + return + } + buffer := &bytes.Buffer{} + logger := exampleslog.InterceptorLogger(slog.New(slog.NewTextHandler(buffer))) + s := &slogExampleTestSuite{ + InterceptorTestSuite: &testpb.InterceptorTestSuite{ + TestService: &testpb.TestPingService{}, + }, + logBuffer: buffer, + } - opts := []logging.Option{ - logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), - // Add any other option (check functions starting with logging.With). + s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ + grpc.StreamInterceptor(logging.StreamServerInterceptor(logger, logging.WithLogOnEvents(logging.StartCall))), + grpc.UnaryInterceptor(logging.UnaryServerInterceptor(logger, logging.WithLogOnEvents(logging.StartCall))), } - // You can now create a server with logging instrumentation that e.g. logs when the unary or stream call is started or finished. - _ = grpc.NewServer( - grpc.ChainUnaryInterceptor( - logging.UnaryServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.ChainStreamInterceptor( - logging.StreamServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // ...user server. - - // Similarly you can create client that will log for the unary and stream client started or finished calls. - _, _ = grpc.Dial( - "some-target", - grpc.WithChainUnaryInterceptor( - logging.UnaryClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.WithChainStreamInterceptor( - logging.StreamClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // Output: + suite.Run(t, s) +} + +func (s *slogExampleTestSuite) TestPing() { + ctx := context.Background() + _, err := s.Client.Ping(ctx, testpb.GoodPing) + assert.NoError(s.T(), err, "there must be not be an on a successful call") + logStr := s.logBuffer.String() + require.Contains(s.T(), logStr, "started call") + require.Contains(s.T(), logStr, "protocol=grpc") + require.Contains(s.T(), logStr, "grpc.component=server") + require.Contains(s.T(), logStr, "grpc.service=testing.testpb.v1.TestService") + require.Contains(s.T(), logStr, "grpc.method=Ping") + require.Contains(s.T(), logStr, "grpc.method_type=unary") + require.Contains(s.T(), logStr, "start_time=") + require.Contains(s.T(), logStr, "grpc.time_ms=") } diff --git a/interceptors/logging/examples/zerolog/example.go b/interceptors/logging/examples/zerolog/example.go new file mode 100644 index 000000000..080a2e322 --- /dev/null +++ b/interceptors/logging/examples/zerolog/example.go @@ -0,0 +1,33 @@ +// Copyright (c) The go-grpc-middleware Authors. +// Licensed under the Apache License 2.0. + +package examplezerolog + +import ( + "context" + "fmt" + + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/rs/zerolog" +) + +// InterceptorLogger adapts zerolog logger to interceptor logger. +// This code is simple enough to be copied and not imported. +func InterceptorLogger(l zerolog.Logger) logging.Logger { + return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { + l := l.With().Fields(fields).Logger() + + switch lvl { + case logging.LevelDebug: + l.Debug().Msg(msg) + case logging.LevelInfo: + l.Info().Msg(msg) + case logging.LevelWarn: + l.Warn().Msg(msg) + case logging.LevelError: + l.Error().Msg(msg) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + }) +} diff --git a/interceptors/logging/examples/zerolog/example_test.go b/interceptors/logging/examples/zerolog/example_test.go index 76fe24403..4114c757d 100644 --- a/interceptors/logging/examples/zerolog/example_test.go +++ b/interceptors/logging/examples/zerolog/example_test.go @@ -1,71 +1,68 @@ // Copyright (c) The go-grpc-middleware Authors. // Licensed under the Apache License 2.0. -package zerolog_test +package examplezerolog_test import ( + "bytes" "context" - "fmt" - "os" + "encoding/json" + "runtime" + "strings" + "testing" + examplezerolog "github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples/zerolog" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb" "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "google.golang.org/grpc" ) -// InterceptorLogger adapts zerolog logger to interceptor logger. -// This code is simple enough to be copied and not imported. -func InterceptorLogger(l zerolog.Logger) logging.Logger { - return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { - l := l.With().Fields(fields).Logger() - - switch lvl { - case logging.LevelDebug: - l.Debug().Msg(msg) - case logging.LevelInfo: - l.Info().Msg(msg) - case logging.LevelWarn: - l.Warn().Msg(msg) - case logging.LevelError: - l.Error().Msg(msg) - default: - panic(fmt.Sprintf("unknown level %v", lvl)) - } - }) +type zerologExampleTestSuite struct { + *testpb.InterceptorTestSuite + logBuffer *bytes.Buffer } -func ExampleInterceptorLogger() { - logger := zerolog.New(os.Stderr) +func TestSuite(t *testing.T) { + if strings.HasPrefix(runtime.Version(), "go1.7") { + t.Skipf("Skipping due to json.RawMessage incompatibility with go1.7") + return + } + buffer := &bytes.Buffer{} + logger := examplezerolog.InterceptorLogger(zerolog.New(buffer)) + s := &zerologExampleTestSuite{ + InterceptorTestSuite: &testpb.InterceptorTestSuite{ + TestService: &testpb.TestPingService{}, + }, + logBuffer: buffer, + } - opts := []logging.Option{ - logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), - // Add any other option (check functions starting with logging.With). + s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{ + grpc.StreamInterceptor(logging.StreamServerInterceptor(logger, logging.WithLogOnEvents(logging.StartCall))), + grpc.UnaryInterceptor(logging.UnaryServerInterceptor(logger, logging.WithLogOnEvents(logging.StartCall))), } - // You can now create a server with logging instrumentation that e.g. logs when the unary or stream call is started or finished. - _ = grpc.NewServer( - grpc.ChainUnaryInterceptor( - logging.UnaryServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.ChainStreamInterceptor( - logging.StreamServerInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // ...user server. + suite.Run(t, s) +} + +func (s *zerologExampleTestSuite) TestPing() { + ctx := context.Background() + _, err := s.Client.Ping(ctx, testpb.GoodPing) + assert.NoError(s.T(), err, "there must be not be an on a successful call") + var logMap map[string]interface{} + err = json.Unmarshal(s.logBuffer.Bytes(), &logMap) + require.NoError(s.T(), err) - // Similarly you can create client that will log for the unary and stream client started or finished calls. - _, _ = grpc.Dial( - "some-target", - grpc.WithChainUnaryInterceptor( - logging.UnaryClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - grpc.WithChainStreamInterceptor( - logging.StreamClientInterceptor(InterceptorLogger(logger), opts...), - // Add any other interceptor you want. - ), - ) - // Output: + require.Equal(s.T(), "started call", logMap["message"]) + require.Equal(s.T(), "grpc", logMap["protocol"]) + require.Equal(s.T(), "server", logMap["grpc.component"]) + require.Equal(s.T(), "testing.testpb.v1.TestService", logMap["grpc.service"]) + require.Equal(s.T(), "Ping", logMap["grpc.method"]) + require.Equal(s.T(), "unary", logMap["grpc.method_type"]) + require.Contains(s.T(), logMap["peer.address"], "127.0.0.1") + require.NotEmpty(s.T(), logMap["grpc.start_time"]) + require.NotEmpty(s.T(), logMap["grpc.time_ms"]) }