-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gRPC server metrics #14922
gRPC server metrics #14922
Changes from 5 commits
1461b1c
5caf1ab
3d0e4e8
7312a37
05f715b
cc4de56
7bd406b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:feature | ||
grpc: Added metrics for external gRPC server. Added `server_type=internal|external` label to gRPC metrics. | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package external | ||
|
||
import ( | ||
"context" | ||
"net" | ||
"sort" | ||
"testing" | ||
"time" | ||
|
||
"github.com/armon/go-metrics" | ||
"github.com/google/go-cmp/cmp" | ||
"github.com/stretchr/testify/require" | ||
"golang.org/x/sync/errgroup" | ||
"google.golang.org/grpc" | ||
|
||
"github.com/hashicorp/go-hclog" | ||
|
||
"github.com/hashicorp/consul/agent/grpc-middleware/testutil" | ||
"github.com/hashicorp/consul/agent/grpc-middleware/testutil/testservice" | ||
"github.com/hashicorp/consul/proto/prototest" | ||
) | ||
|
||
func TestServer_EmitsStats(t *testing.T) { | ||
sink, reset := patchGlobalMetrics(t) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of passing the metrics object as a parameter to func NewServer(logger agentmiddleware.Logger, metrics *metrics.Metrics) *grpc.Server {
if metrics == nil {
metrics = agentmiddleware.DefaultMetrics
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also appreciate that these tests were copied from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💯 I removed the global metrics instance from both grpc-internal and grpc-external |
||
|
||
srv := NewServer(hclog.Default()) | ||
reset() | ||
|
||
testservice.RegisterSimpleServer(srv, &testservice.Simple{}) | ||
|
||
lis, err := net.Listen("tcp", "127.0.0.1:0") | ||
require.NoError(t, err) | ||
|
||
ctx, cancel := context.WithCancel(context.Background()) | ||
t.Cleanup(cancel) | ||
|
||
g, ctx := errgroup.WithContext(ctx) | ||
g.Go(func() error { | ||
return srv.Serve(lis) | ||
}) | ||
t.Cleanup(func() { | ||
srv.Stop() | ||
if err := g.Wait(); err != nil { | ||
t.Logf("grpc server error: %v", err) | ||
} | ||
}) | ||
|
||
conn, err := grpc.DialContext(ctx, lis.Addr().String(), grpc.WithInsecure()) | ||
require.NoError(t, err) | ||
t.Cleanup(func() { conn.Close() }) | ||
|
||
client := testservice.NewSimpleClient(conn) | ||
fClient, err := client.Flow(ctx, &testservice.Req{Datacenter: "mine"}) | ||
require.NoError(t, err) | ||
|
||
// Wait for the first event so that we know the stream is sending. | ||
_, err = fClient.Recv() | ||
require.NoError(t, err) | ||
|
||
cancel() | ||
conn.Close() | ||
srv.GracefulStop() | ||
// Wait for the server to stop so that active_streams is predictable. | ||
require.NoError(t, g.Wait()) | ||
|
||
// Occasionally the active_stream=0 metric may be emitted before the | ||
// active_conns=0 metric. The order of those metrics is not really important | ||
// so we sort the calls to match the expected. | ||
sort.Slice(sink.GaugeCalls, func(i, j int) bool { | ||
if i < 2 || j < 2 { | ||
return i < j | ||
} | ||
if len(sink.GaugeCalls[i].Key) < 4 || len(sink.GaugeCalls[j].Key) < 4 { | ||
return i < j | ||
} | ||
return sink.GaugeCalls[i].Key[3] < sink.GaugeCalls[j].Key[3] | ||
}) | ||
|
||
cmpMetricCalls := cmp.AllowUnexported(testutil.MetricCall{}) | ||
expLabels := []metrics.Label{{ | ||
Name: "server_type", | ||
Value: "external", | ||
}} | ||
expectedGauge := []testutil.MetricCall{ | ||
{Key: []string{"testing", "grpc", "server", "connections"}, Val: 1, Labels: expLabels}, | ||
{Key: []string{"testing", "grpc", "server", "streams"}, Val: 1, Labels: expLabels}, | ||
{Key: []string{"testing", "grpc", "server", "connections"}, Val: 0, Labels: expLabels}, | ||
{Key: []string{"testing", "grpc", "server", "streams"}, Val: 0, Labels: expLabels}, | ||
} | ||
prototest.AssertDeepEqual(t, expectedGauge, sink.GaugeCalls, cmpMetricCalls) | ||
|
||
expectedCounter := []testutil.MetricCall{ | ||
{Key: []string{"testing", "grpc", "server", "connection", "count"}, Val: 1, Labels: expLabels}, | ||
{Key: []string{"testing", "grpc", "server", "request", "count"}, Val: 1, Labels: expLabels}, | ||
{Key: []string{"testing", "grpc", "server", "stream", "count"}, Val: 1, Labels: expLabels}, | ||
} | ||
prototest.AssertDeepEqual(t, expectedCounter, sink.IncrCounterCalls, cmpMetricCalls) | ||
} | ||
|
||
func patchGlobalMetrics(t *testing.T) (*testutil.FakeMetricsSink, func()) { | ||
t.Helper() | ||
|
||
sink := &testutil.FakeMetricsSink{} | ||
cfg := &metrics.Config{ | ||
ServiceName: "testing", | ||
TimerGranularity: time.Millisecond, // Timers are in milliseconds | ||
ProfileInterval: time.Second, // Poll runtime every second | ||
FilterDefault: true, | ||
} | ||
var err error | ||
defaultMetrics = func() *metrics.Metrics { | ||
m, _ := metrics.New(cfg, sink) | ||
return m | ||
} | ||
require.NoError(t, err) | ||
reset := func() { | ||
t.Helper() | ||
defaultMetrics = metrics.Default | ||
require.NoError(t, err, "failed to reset global metrics") | ||
} | ||
return sink, reset | ||
} | ||
|
||
func logError(t *testing.T, f func() error) func() { | ||
return func() { | ||
if err := f(); err != nil { | ||
t.Logf(err.Error()) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This copies the
TestHandler_EmitStats
test. I did some deduplication of the helpers by moving things over togrpc-middleware/testutil
. (I could do more deduplication but kind of wanted a sanity check before I go too far.)