diff --git a/clientv3/client.go b/clientv3/client.go index 6ff5aa45b..3d36c3ab5 100644 --- a/clientv3/client.go +++ b/clientv3/client.go @@ -25,7 +25,6 @@ import ( "time" "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" - prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "golang.org/x/net/context" "google.golang.org/grpc" @@ -281,9 +280,7 @@ func (c *Client) dial(endpoint string, dopts ...grpc.DialOption) (*grpc.ClientCo opts = append(opts, grpc.WithPerRPCCredentials(c.tokenCred)) } - // add metrics options - opts = append(opts, grpc.WithUnaryInterceptor(prometheus.UnaryClientInterceptor)) - opts = append(opts, grpc.WithStreamInterceptor(prometheus.StreamClientInterceptor)) + opts = append(opts, c.cfg.DialOptions...) conn, err := grpc.Dial(host, opts...) if err != nil { diff --git a/clientv3/config.go b/clientv3/config.go index 4511bf242..2082f7b91 100644 --- a/clientv3/config.go +++ b/clientv3/config.go @@ -17,6 +17,8 @@ package clientv3 import ( "crypto/tls" "time" + + "google.golang.org/grpc" ) type Config struct { @@ -38,4 +40,7 @@ type Config struct { // Password is a password for authentication. Password string `json:"password"` + + // DialOptions is a list of dial options for the grpc client (e.g., for interceptors). + DialOptions []grpc.DialOption } diff --git a/clientv3/example_metrics_test.go b/clientv3/example_metrics_test.go index 26f615385..6df797a44 100644 --- a/clientv3/example_metrics_test.go +++ b/clientv3/example_metrics_test.go @@ -18,21 +18,51 @@ import ( "fmt" "io/ioutil" "log" + "net" "net/http" + "strings" + "github.com/coreos/etcd/clientv3" + + grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" + "google.golang.org/grpc" ) -func ExampleMetrics_All() { +func ExampleMetrics_range() { + cli, err := clientv3.New(clientv3.Config{ + Endpoints: endpoints, + DialOptions: []grpc.DialOption{ + grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor), + grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor), + }, + }) + if err != nil { + log.Fatal(err) + } + defer cli.Close() + + // get a key so it shows up in the metrics as a range rpc + cli.Get(context.TODO(), "test_key") + // listen for all prometheus metrics + ln, err := net.Listen("tcp", ":47989") + if err != nil { + log.Fatal(err) + } + donec := make(chan struct{}) go func() { - http.Handle("/metrics", prometheus.Handler()) - log.Fatal(http.ListenAndServe(":47989", nil)) + defer close(donec) + http.Serve(ln, prometheus.Handler()) + }() + defer func() { + ln.Close() + <-donec }() - url := "http://localhost:47989/metrics" - // make an http request to fetch all prometheus metrics + url := "http://localhost:47989/metrics" resp, err := http.Get(url) if err != nil { log.Fatalf("fetch error: %v", err) @@ -42,5 +72,13 @@ func ExampleMetrics_All() { if err != nil { log.Fatalf("fetch error: reading %s: %v", url, err) } - fmt.Printf("%s", b) + + // confirm range request in metrics + for _, l := range strings.Split(string(b), "\n") { + if strings.Contains(l, `grpc_client_started_total{grpc_method="Range"`) { + fmt.Println(l) + break + } + } + // Output: grpc_client_started_total{grpc_method="Range",grpc_service="etcdserverpb.KV",grpc_type="unary"} 1 } diff --git a/clientv3/integration/metrics_test.go b/clientv3/integration/metrics_test.go index de0efd5a2..3543a7fab 100644 --- a/clientv3/integration/metrics_test.go +++ b/clientv3/integration/metrics_test.go @@ -29,8 +29,10 @@ import ( "github.com/coreos/etcd/pkg/testutil" "github.com/coreos/etcd/pkg/transport" + grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/prometheus/client_golang/prometheus" "golang.org/x/net/context" + "google.golang.org/grpc" ) func TestV3ClientMetrics(t *testing.T) { @@ -66,20 +68,26 @@ func TestV3ClientMetrics(t *testing.T) { clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) defer clus.Terminate(t) - client := clus.Client(0) + cfg := clientv3.Config{ + Endpoints: []string{clus.Members[0].GRPCAddr()}, + DialOptions: []grpc.DialOption{ + grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor), + grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor), + }, + } + cli, cerr := clientv3.New(cfg) + if cerr != nil { + t.Fatal(cerr) + } + defer cli.Close() - w := clientv3.NewWatcher(client) - defer w.Close() - - kv := clientv3.NewKV(client) - - wc := w.Watch(context.Background(), "foo") + wc := cli.Watch(context.Background(), "foo") wBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream") pBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary") - _, err = kv.Put(context.Background(), "foo", "bar") + _, err = cli.Put(context.Background(), "foo", "bar") if err != nil { t.Errorf("Error putting value in key store") }