Skip to content
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

Add gRPC client handler. #95

Merged
merged 16 commits into from
Nov 30, 2018
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text eol=lf
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ _testmain.go
*.exe
*.test
*.prof

.idea
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ language: go
sudo: false

go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
Expand All @@ -14,7 +13,7 @@ before_install:

install:
- go get -d -t ./...
- if [[ ! $TRAVIS_GO_VERSION = 1.8* ]]; then go get -u golang.org/x/lint/golint ; fi
- go get -u golang.org/x/lint/golint

script:
- make test vet lint bench
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ bench:
.PHONY: protoc
protoc:
protoc --go_out=. proto/v2/zipkin.proto
protoc --go_out=plugins=grpc:. proto/testing/service.proto

.PHONY: lint
lint:
# Ignore grep's exit code since no match returns 1.
-if [[ ! $TRAVIS_GO_VERSION = 1.8* ]]; then echo 'linting...' ; golint ./... ; fi
echo 'linting...' ; golint ./...

.PHONY: vet
vet:
Expand Down
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
module github.com/openzipkin/zipkin-go

require (
cloud.google.com/go v0.33.1 // indirect
github.com/Shopify/sarama v1.19.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/eapache/go-resiliency v1.1.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3 // indirect
github.com/golang/protobuf v1.2.0
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/mux v1.6.2
github.com/onsi/ginkgo v1.7.0
github.com/onsi/gomega v1.4.3
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 // indirect
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba // indirect
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b // indirect
golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1 // indirect
google.golang.org/appengine v1.3.0 // indirect
google.golang.org/genproto v0.0.0-20181109154231-b5d43981345b // indirect
google.golang.org/grpc v1.16.0
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3 // indirect
)
36 changes: 36 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand All @@ -10,30 +11,65 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b h1:MQE+LT/ABUuuvEZ+YQAMSXindAdUh7slEmAkup74op4=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181109154231-b5d43981345b h1:WkFtVmaZoTRVoRYr0LTC9SYNhlw0X0HrVPz2OVssVm4=
google.golang.org/genproto v0.0.0-20181109154231-b5d43981345b/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
105 changes: 105 additions & 0 deletions middleware/grpc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// +build go1.9

package grpc

import (
"context"
"strings"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/status"

"github.com/openzipkin/zipkin-go"
"github.com/openzipkin/zipkin-go/model"
"github.com/openzipkin/zipkin-go/propagation/b3"
)

type clientHandler struct {
tracer *zipkin.Tracer
rpcHandlers []RPCHandler
remoteServiceName string
}

// A ClientOption can be passed to NewClientHandler to customize the returned handler.
type ClientOption func(*clientHandler)

// A RPCHandler can be registered using WithRPCHandler to intercept calls to HandleRPC of a
// handler for additional span customization.
type RPCHandler func(span zipkin.Span, rpcStats stats.RPCStats)

// WithRPCHandler allows one to add custom logic for handling a stats.RPCStats, e.g.,
// to add additional tags.
func WithRPCHandler(handler RPCHandler) ClientOption {
return func(c *clientHandler) {
c.rpcHandlers = append(c.rpcHandlers, handler)
}
}

// NewClientHandler returns a stats.Handler which can be used with grpc.WithStatsHandler to add
// tracing to a gRPC client. The gRPC method name is used as the span name and by default the only
// tags are the gRPC status code if the call fails. Use WithRPCHandler to add additional tags.
func NewClientHandler(tracer *zipkin.Tracer, options ...ClientOption) stats.Handler {
c := &clientHandler{
tracer: tracer,
}
for _, option := range options {
option(c)
}
return c
}

// HandleConn exists to satisfy gRPC stats.Handler.
func (c *clientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {
// no-op
}

// TagConn exists to satisfy gRPC stats.Handler.
func (c *clientHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) context.Context {
// no-op
return ctx
}

// HandleRPC implements per-RPC tracing and stats instrumentation.
func (c *clientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
span := zipkin.SpanFromContext(ctx)

for _, h := range c.rpcHandlers {
h(span, rs)
}

switch rs := rs.(type) {
case *stats.End:
s, ok := status.FromError(rs.Error)
// rs.Error should always be convertable to a status, this is just a defensive check.
if ok {
if s.Code() != codes.OK {
// Uppercase for consistency with Brave
c := strings.ToUpper(s.Code().String())
span.Tag("grpc.status_code", c)
zipkin.TagError.Set(span, c)
}
} else {
zipkin.TagError.Set(span, rs.Error.Error())
}
span.Finish()
}
}

// TagRPC implements per-RPC context management.
func (c *clientHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
var span zipkin.Span

name := spanName(rti)
span, ctx = c.tracer.StartSpanFromContext(ctx, name, zipkin.Kind(model.Client))
md, ok := metadata.FromOutgoingContext(ctx)
if ok {
md = md.Copy()
} else {
md = metadata.New(nil)
}
_ = b3.InjectGRPC(&md)(span.Context())
ctx = metadata.NewOutgoingContext(ctx, md)
return ctx
}
123 changes: 123 additions & 0 deletions middleware/grpc/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// +build go1.9

package grpc_test

import (
"context"

"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"

"github.com/openzipkin/zipkin-go"
zipkingrpc "github.com/openzipkin/zipkin-go/middleware/grpc"
"github.com/openzipkin/zipkin-go/propagation/b3"
service "github.com/openzipkin/zipkin-go/proto/testing"
"github.com/openzipkin/zipkin-go/reporter/recorder"
)

var _ = ginkgo.Describe("gRPC Client", func() {
var (
reporter *recorder.ReporterRecorder
tracer *zipkin.Tracer
conn *grpc.ClientConn
client service.HelloServiceClient
)

ginkgo.BeforeEach(func() {
var err error

reporter = recorder.NewReporter()
ep, _ := zipkin.NewEndpoint("grpcClient", "")
tracer, err = zipkin.NewTracer(
reporter, zipkin.WithLocalEndpoint(ep), zipkin.WithIDGenerator(newSequentialIdGenerator()))
gomega.Expect(tracer, err).ToNot(gomega.BeNil())
})

ginkgo.AfterEach(func() {
_ = reporter.Close()
_ = conn.Close()
})

ginkgo.Context("with defaults", func() {
ginkgo.BeforeEach(func() {
var err error

conn, err = grpc.Dial(serverAddr, grpc.WithInsecure(), grpc.WithStatsHandler(zipkingrpc.NewClientHandler(tracer)))
gomega.Expect(conn, err).ToNot(gomega.BeNil())
client = service.NewHelloServiceClient(conn)
})

ginkgo.It("creates a span", func() {
resp, err := client.Hello(context.Background(), &service.HelloRequest{Payload: "Hello"})
gomega.Expect(resp, err).ToNot(gomega.BeNil())

spans := reporter.Flush()
gomega.Expect(spans).To(gomega.HaveLen(1))
gomega.Expect(spans[0].Name).To(gomega.Equal("zipkin.testing.HelloService.Hello"))
gomega.Expect(spans[0].Tags).To(gomega.BeEmpty())
})

ginkgo.It("propagates trace context", func() {
resp, err := client.Hello(context.Background(), &service.HelloRequest{Payload: "Hello"})
gomega.Expect(resp.GetMetadata(), err).To(gomega.HaveKeyWithValue(b3.TraceID, "0000000000000001"))
gomega.Expect(resp.GetMetadata(), err).To(gomega.HaveKeyWithValue(b3.SpanID, "0000000000000001"))
gomega.Expect(resp.GetMetadata(), err).ToNot(gomega.HaveKey(b3.ParentSpanID))
})

ginkgo.It("propagates parent span", func() {
_, ctx := tracer.StartSpanFromContext(context.Background(), "parent")
resp, err := client.Hello(ctx, &service.HelloRequest{Payload: "Hello"})
gomega.Expect(resp.GetMetadata(), err).To(gomega.HaveKeyWithValue(b3.TraceID, "0000000000000001"))
gomega.Expect(resp.GetMetadata(), err).To(gomega.HaveKeyWithValue(b3.SpanID, "0000000000000002"))
gomega.Expect(resp.GetMetadata(), err).To(gomega.HaveKeyWithValue(b3.ParentSpanID, "0000000000000001"))
})

ginkgo.It("tags with error code", func() {
_, err := client.Hello(context.Background(), &service.HelloRequest{Payload: "fail"})
gomega.Expect(err).To(gomega.HaveOccurred())

spans := reporter.Flush()
gomega.Expect(spans).To(gomega.HaveLen(1))
gomega.Expect(spans[0].Tags).To(gomega.HaveLen(2))
gomega.Expect(spans[0].Tags).To(gomega.HaveKeyWithValue("grpc.status_code", "ABORTED"))
gomega.Expect(spans[0].Tags).To(gomega.HaveKeyWithValue(string(zipkin.TagError), "ABORTED"))
})

ginkgo.It("copies existing metadata", func() {
ctx := metadata.AppendToOutgoingContext(context.Background(), "existing", "metadata")
resp, err := client.Hello(ctx, &service.HelloRequest{Payload: "Hello"})

gomega.Expect(resp.GetMetadata(), err).To(gomega.HaveKeyWithValue("existing", "metadata"))
})
})

ginkgo.Context("with custom RPCHandler", func() {
ginkgo.BeforeEach(func() {
var err error

conn, err = grpc.Dial(
serverAddr,
grpc.WithInsecure(),
grpc.WithStatsHandler(zipkingrpc.NewClientHandler(
tracer,
zipkingrpc.WithRPCHandler(func(span zipkin.Span, rpcStats stats.RPCStats) {
span.Tag("custom", "tag")
}))))
gomega.Expect(conn, err).ToNot(gomega.BeNil())
client = service.NewHelloServiceClient(conn)
})

ginkgo.It("calls custom RPCHandler", func() {
resp, err := client.Hello(context.Background(), &service.HelloRequest{Payload: "Hello"})
gomega.Expect(resp, err).ToNot(gomega.BeNil())

spans := reporter.Flush()
gomega.Expect(spans).To(gomega.HaveLen(1))
gomega.Expect(spans[0].Tags).To(gomega.HaveLen(1))
gomega.Expect(spans[0].Tags).To(gomega.HaveKeyWithValue("custom", "tag"))
})
})
})
6 changes: 6 additions & 0 deletions middleware/grpc/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// +build go1.9

/*
Package grpc contains several gRPC handlers which can be used for instrumenting calls with Zipkin.
*/
package grpc
Loading