-
Notifications
You must be signed in to change notification settings - Fork 693
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* SettableLoggerV2 to dynamically change grpc logger. The go-grpc assumes that logger can be only configured once as the `SetLoggerV2` method is: ```Not mutex-protected, should be called before any gRPC functions.`` This is insufficient in case of e.g. "testing" scenarios where loggers might need to be supplied on per-test-case basis. * Integrate ZAP with SettableLoggerV2. The settable_test.go is a good example of integration of testing harness with grpclogging using zaptest, that is canonical usecase for this infrastructure.
- Loading branch information
Showing
8 changed files
with
217 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
/* | ||
grpc_logsettable contains a thread-safe wrapper around grpc-logging | ||
infrastructure. | ||
The go-grpc assumes that logger can be only configured once as the `SetLoggerV2` | ||
method is: | ||
```Not mutex-protected, should be called before any gRPC functions.``` | ||
This package allows to supply parent logger once ("before any grpc"), but | ||
later change underlying implementation in thread-safe way when needed. | ||
It's in particular useful for testing, where each testcase might need its own | ||
logger. | ||
*/ | ||
package grpc_logsettable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package grpc_logsettable | ||
|
||
import ( | ||
"io/ioutil" | ||
"sync" | ||
|
||
"google.golang.org/grpc/grpclog" | ||
) | ||
|
||
// SettableLoggerV2 is thread-safe. | ||
type SettableLoggerV2 interface { | ||
grpclog.LoggerV2 | ||
// Sets given logger as the underlying implementation. | ||
Set(loggerv2 grpclog.LoggerV2) | ||
// Sets `discard` logger as the underlying implementation. | ||
Reset() | ||
} | ||
|
||
// ReplaceGrpcLoggerV2 creates and configures SettableLoggerV2 as grpc logger. | ||
func ReplaceGrpcLoggerV2() SettableLoggerV2 { | ||
settable := &settableLoggerV2{} | ||
settable.Reset() | ||
grpclog.SetLoggerV2(settable) | ||
return settable | ||
} | ||
|
||
// SettableLoggerV2 implements SettableLoggerV2 | ||
type settableLoggerV2 struct { | ||
log grpclog.LoggerV2 | ||
mu sync.RWMutex | ||
} | ||
|
||
func (s *settableLoggerV2) Set(log grpclog.LoggerV2) { | ||
s.mu.Lock() | ||
defer s.mu.Unlock() | ||
s.log = log | ||
} | ||
|
||
func (s *settableLoggerV2) Reset() { | ||
s.Set(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard)) | ||
} | ||
|
||
func (s *settableLoggerV2) get() grpclog.LoggerV2 { | ||
s.mu.RLock() | ||
defer s.mu.RUnlock() | ||
return s.log | ||
} | ||
|
||
func (s *settableLoggerV2) Info(args ...interface{}) { | ||
s.get().Info(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Infoln(args ...interface{}) { | ||
s.get().Infoln(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Infof(format string, args ...interface{}) { | ||
s.get().Infof(format, args) | ||
} | ||
|
||
func (s *settableLoggerV2) Warning(args ...interface{}) { | ||
s.get().Warning(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Warningln(args ...interface{}) { | ||
s.get().Warningln(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Warningf(format string, args ...interface{}) { | ||
s.get().Warningf(format, args) | ||
} | ||
|
||
func (s *settableLoggerV2) Error(args ...interface{}) { | ||
s.get().Error(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Errorln(args ...interface{}) { | ||
s.get().Errorln(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Errorf(format string, args ...interface{}) { | ||
s.get().Errorf(format, args) | ||
} | ||
|
||
func (s *settableLoggerV2) Fatal(args ...interface{}) { | ||
s.get().Fatal(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Fatalln(args ...interface{}) { | ||
s.get().Fatalln(args) | ||
} | ||
|
||
func (s *settableLoggerV2) Fatalf(format string, args ...interface{}) { | ||
s.get().Fatalf(format, args) | ||
} | ||
|
||
func (s *settableLoggerV2) V(l int) bool { | ||
return s.get().V(l) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package grpc_logsettable_test | ||
|
||
import ( | ||
"bytes" | ||
"io/ioutil" | ||
"os" | ||
"testing" | ||
|
||
grpc_logsettable "github.com/grpc-ecosystem/go-grpc-middleware/logging/settable" | ||
"github.com/stretchr/testify/assert" | ||
"google.golang.org/grpc/grpclog" | ||
) | ||
|
||
func ExampleSettableLoggerV2_init() { | ||
l1 := grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard) | ||
l2 := grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stdout) | ||
|
||
settableLogger := grpc_logsettable.ReplaceGrpcLoggerV2() | ||
grpclog.Info("Discarded by default") | ||
|
||
settableLogger.Set(l1) | ||
grpclog.Info("Discarded log by l1") | ||
|
||
settableLogger.Set(l2) | ||
grpclog.Info("Emitted log by l2") | ||
// Expected output: INFO: 2021/03/15 12:59:54 [Emitted log by l2] | ||
} | ||
|
||
func TestSettableLoggerV2_init(t *testing.T) { | ||
l1buffer := &bytes.Buffer{} | ||
l1 := grpclog.NewLoggerV2(l1buffer, l1buffer, l1buffer) | ||
|
||
l2buffer := &bytes.Buffer{} | ||
l2 := grpclog.NewLoggerV2(l2buffer, l2buffer, l2buffer) | ||
|
||
settableLogger := grpc_logsettable.ReplaceGrpcLoggerV2() | ||
grpclog.Info("Discarded by default") | ||
|
||
settableLogger.Set(l1) | ||
grpclog.SetLoggerV2(settableLogger) | ||
grpclog.Info("Emitted log by l1") | ||
|
||
settableLogger.Set(l2) | ||
grpclog.Info("Emitted log by l2") | ||
|
||
assert.Contains(t, l1buffer.String(), "Emitted log by l1") | ||
assert.Contains(t, l2buffer.String(), "Emitted log by l2") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package grpc_zap_test | ||
|
||
import ( | ||
"testing" | ||
|
||
grpc_logsettable "github.com/grpc-ecosystem/go-grpc-middleware/logging/settable" | ||
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" | ||
"go.uber.org/zap/zaptest" | ||
) | ||
|
||
var grpc_logger grpc_logsettable.SettableLoggerV2 | ||
|
||
func init() { | ||
grpc_logger = grpc_logsettable.ReplaceGrpcLoggerV2() | ||
} | ||
|
||
func beforeTest(t testing.TB) { | ||
grpc_zap.SetGrpcLoggerV2(grpc_logger, zaptest.NewLogger(t)) | ||
|
||
// Starting from go-1.15+ automated 'reset' can also be set: | ||
// t.Cleanup(func() { | ||
// grpc_logger.Reset() | ||
// }) | ||
} | ||
|
||
// This test illustrates setting up a testing harness that attributes | ||
// all grpc logs emitted during the test to the test-specific log. | ||
// | ||
// In case of test failure, only logs emitted by this testcase will be printed. | ||
func TestSpecificLogging(t *testing.T) { | ||
beforeTest(t) | ||
grpc_logger.Info("Test specific log-line") | ||
} |