1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-01-28 03:57:09 +02:00

Move grpctrace to contrib repo (#1027)

* Remove grpctrace package

This is being moved to the contrib repo with
https://github.com/open-telemetry/opentelemetry-go-contrib/pull/189
as part of #976.

* Update Changelog

* Remove the grpc example

Moved to contrib repo

* Fix spelling error

* Update Changelog
This commit is contained in:
Tyler Yahn 2020-08-05 20:24:39 -07:00 committed by GitHub
parent ccfa2e7bdf
commit f0620dc0ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 5 additions and 1920 deletions

View File

@ -12,6 +12,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Renamed `go.opentelemetry.io/otel/api/standard` package to `go.opentelemetry.io/otel/semconv` to avoid the ambiguous and generic name `standard` and better describe the package as containing OpenTelemetry semantic conventions. (#1016)
### Removed
- The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository.
This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027)
### Fixed
- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031)

View File

@ -1,28 +0,0 @@
# gRPC Tracing Example
Traces client and server calls via interceptors.
### Compile .proto
Only required if the service definition (.proto) changes.
```sh
cd ./example/grpc
# protobuf v1.3.2
protoc -I api --go_out=plugins=grpc,paths=source_relative:./api api/hello-service.proto
```
### Run server
```sh
cd ./example/grpc
go run ./server
```
### Run client
```sh
go run ./client
```

View File

@ -1,354 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello-service.proto
/*
Package api is a generated protocol buffer package.
It is generated from these files:
hello-service.proto
It has these top-level messages:
HelloRequest
HelloResponse
*/
package api
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type HelloRequest struct {
Greeting string `protobuf:"bytes,1,opt,name=greeting" json:"greeting,omitempty"`
}
func (m *HelloRequest) Reset() { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage() {}
func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *HelloRequest) GetGreeting() string {
if m != nil {
return m.Greeting
}
return ""
}
type HelloResponse struct {
Reply string `protobuf:"bytes,1,opt,name=reply" json:"reply,omitempty"`
}
func (m *HelloResponse) Reset() { *m = HelloResponse{} }
func (m *HelloResponse) String() string { return proto.CompactTextString(m) }
func (*HelloResponse) ProtoMessage() {}
func (*HelloResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *HelloResponse) GetReply() string {
if m != nil {
return m.Reply
}
return ""
}
func init() {
proto.RegisterType((*HelloRequest)(nil), "api.HelloRequest")
proto.RegisterType((*HelloResponse)(nil), "api.HelloResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for HelloService service
type HelloServiceClient interface {
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (HelloService_SayHelloServerStreamClient, error)
SayHelloClientStream(ctx context.Context, opts ...grpc.CallOption) (HelloService_SayHelloClientStreamClient, error)
SayHelloBidiStream(ctx context.Context, opts ...grpc.CallOption) (HelloService_SayHelloBidiStreamClient, error)
}
type helloServiceClient struct {
cc *grpc.ClientConn
}
func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient {
return &helloServiceClient{cc}
}
func (c *helloServiceClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) {
out := new(HelloResponse)
err := grpc.Invoke(ctx, "/api.HelloService/SayHello", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *helloServiceClient) SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (HelloService_SayHelloServerStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_HelloService_serviceDesc.Streams[0], c.cc, "/api.HelloService/SayHelloServerStream", opts...)
if err != nil {
return nil, err
}
x := &helloServiceSayHelloServerStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type HelloService_SayHelloServerStreamClient interface {
Recv() (*HelloResponse, error)
grpc.ClientStream
}
type helloServiceSayHelloServerStreamClient struct {
grpc.ClientStream
}
func (x *helloServiceSayHelloServerStreamClient) Recv() (*HelloResponse, error) {
m := new(HelloResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *helloServiceClient) SayHelloClientStream(ctx context.Context, opts ...grpc.CallOption) (HelloService_SayHelloClientStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_HelloService_serviceDesc.Streams[1], c.cc, "/api.HelloService/SayHelloClientStream", opts...)
if err != nil {
return nil, err
}
x := &helloServiceSayHelloClientStreamClient{stream}
return x, nil
}
type HelloService_SayHelloClientStreamClient interface {
Send(*HelloRequest) error
CloseAndRecv() (*HelloResponse, error)
grpc.ClientStream
}
type helloServiceSayHelloClientStreamClient struct {
grpc.ClientStream
}
func (x *helloServiceSayHelloClientStreamClient) Send(m *HelloRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *helloServiceSayHelloClientStreamClient) CloseAndRecv() (*HelloResponse, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(HelloResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *helloServiceClient) SayHelloBidiStream(ctx context.Context, opts ...grpc.CallOption) (HelloService_SayHelloBidiStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_HelloService_serviceDesc.Streams[2], c.cc, "/api.HelloService/SayHelloBidiStream", opts...)
if err != nil {
return nil, err
}
x := &helloServiceSayHelloBidiStreamClient{stream}
return x, nil
}
type HelloService_SayHelloBidiStreamClient interface {
Send(*HelloRequest) error
Recv() (*HelloResponse, error)
grpc.ClientStream
}
type helloServiceSayHelloBidiStreamClient struct {
grpc.ClientStream
}
func (x *helloServiceSayHelloBidiStreamClient) Send(m *HelloRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *helloServiceSayHelloBidiStreamClient) Recv() (*HelloResponse, error) {
m := new(HelloResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for HelloService service
type HelloServiceServer interface {
SayHello(context.Context, *HelloRequest) (*HelloResponse, error)
SayHelloServerStream(*HelloRequest, HelloService_SayHelloServerStreamServer) error
SayHelloClientStream(HelloService_SayHelloClientStreamServer) error
SayHelloBidiStream(HelloService_SayHelloBidiStreamServer) error
}
func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) {
s.RegisterService(&_HelloService_serviceDesc, srv)
}
func _HelloService_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HelloServiceServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.HelloService/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HelloServiceServer).SayHello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HelloService_SayHelloServerStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(HelloRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(HelloServiceServer).SayHelloServerStream(m, &helloServiceSayHelloServerStreamServer{stream})
}
type HelloService_SayHelloServerStreamServer interface {
Send(*HelloResponse) error
grpc.ServerStream
}
type helloServiceSayHelloServerStreamServer struct {
grpc.ServerStream
}
func (x *helloServiceSayHelloServerStreamServer) Send(m *HelloResponse) error {
return x.ServerStream.SendMsg(m)
}
func _HelloService_SayHelloClientStream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(HelloServiceServer).SayHelloClientStream(&helloServiceSayHelloClientStreamServer{stream})
}
type HelloService_SayHelloClientStreamServer interface {
SendAndClose(*HelloResponse) error
Recv() (*HelloRequest, error)
grpc.ServerStream
}
type helloServiceSayHelloClientStreamServer struct {
grpc.ServerStream
}
func (x *helloServiceSayHelloClientStreamServer) SendAndClose(m *HelloResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *helloServiceSayHelloClientStreamServer) Recv() (*HelloRequest, error) {
m := new(HelloRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _HelloService_SayHelloBidiStream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(HelloServiceServer).SayHelloBidiStream(&helloServiceSayHelloBidiStreamServer{stream})
}
type HelloService_SayHelloBidiStreamServer interface {
Send(*HelloResponse) error
Recv() (*HelloRequest, error)
grpc.ServerStream
}
type helloServiceSayHelloBidiStreamServer struct {
grpc.ServerStream
}
func (x *helloServiceSayHelloBidiStreamServer) Send(m *HelloResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *helloServiceSayHelloBidiStreamServer) Recv() (*HelloRequest, error) {
m := new(HelloRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _HelloService_serviceDesc = grpc.ServiceDesc{
ServiceName: "api.HelloService",
HandlerType: (*HelloServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _HelloService_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "SayHelloServerStream",
Handler: _HelloService_SayHelloServerStream_Handler,
ServerStreams: true,
},
{
StreamName: "SayHelloClientStream",
Handler: _HelloService_SayHelloClientStream_Handler,
ClientStreams: true,
},
{
StreamName: "SayHelloBidiStream",
Handler: _HelloService_SayHelloBidiStream_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "hello-service.proto",
}
func init() { proto.RegisterFile("hello-service.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 192 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xce, 0x48, 0xcd, 0xc9,
0xc9, 0xd7, 0x2d, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
0x62, 0x4e, 0x2c, 0xc8, 0x54, 0xd2, 0xe2, 0xe2, 0xf1, 0x00, 0xc9, 0x05, 0xa5, 0x16, 0x96, 0xa6,
0x16, 0x97, 0x08, 0x49, 0x71, 0x71, 0xa4, 0x17, 0xa5, 0xa6, 0x96, 0x64, 0xe6, 0xa5, 0x4b, 0x30,
0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xf9, 0x4a, 0xaa, 0x5c, 0xbc, 0x50, 0xb5, 0xc5, 0x05, 0xf9,
0x79, 0xc5, 0xa9, 0x42, 0x22, 0x5c, 0xac, 0x45, 0xa9, 0x05, 0x39, 0x95, 0x50, 0x95, 0x10, 0x8e,
0x51, 0x0b, 0x13, 0xd4, 0xcc, 0x60, 0x88, 0x75, 0x42, 0x86, 0x5c, 0x1c, 0xc1, 0x89, 0x95, 0x60,
0x21, 0x21, 0x41, 0xbd, 0xc4, 0x82, 0x4c, 0x3d, 0x64, 0x2b, 0xa5, 0x84, 0x90, 0x85, 0xa0, 0x26,
0xdb, 0x73, 0x89, 0xc0, 0xb4, 0x80, 0x4c, 0x49, 0x2d, 0x0a, 0x2e, 0x29, 0x4a, 0x4d, 0xcc, 0x25,
0x52, 0xbb, 0x01, 0x23, 0xb2, 0x01, 0xce, 0x39, 0x99, 0xa9, 0x79, 0x25, 0x24, 0x19, 0xa0, 0x01,
0x32, 0x40, 0x08, 0x66, 0x80, 0x53, 0x66, 0x4a, 0x26, 0x89, 0xda, 0x0d, 0x18, 0x93, 0xd8, 0xc0,
0xa1, 0x6c, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x0e, 0xd5, 0x1c, 0xd2, 0x7c, 0x01, 0x00, 0x00,
}

View File

@ -1,34 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package api;
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
rpc SayHelloServerStream (HelloRequest) returns (stream HelloResponse);
rpc SayHelloClientStream (stream HelloRequest) returns (HelloResponse);
rpc SayHelloBidiStream (stream HelloRequest) returns (stream HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}

View File

@ -1,182 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"io"
"log"
"time"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/example/grpc/api"
"go.opentelemetry.io/otel/example/grpc/config"
"go.opentelemetry.io/otel/instrumentation/grpctrace"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func main() {
config.Init()
var conn *grpc.ClientConn
conn, err := grpc.Dial(":7777", grpc.WithInsecure(),
grpc.WithUnaryInterceptor(grpctrace.UnaryClientInterceptor(global.Tracer(""))),
grpc.WithStreamInterceptor(grpctrace.StreamClientInterceptor(global.Tracer(""))),
)
if err != nil {
log.Fatalf("did not connect: %s", err)
}
defer func() { _ = conn.Close() }()
c := api.NewHelloServiceClient(conn)
callSayHello(c)
callSayHelloClientStream(c)
callSayHelloServerStream(c)
callSayHelloBidiStream(c)
time.Sleep(10 * time.Millisecond)
}
func callSayHello(c api.HelloServiceClient) {
md := metadata.Pairs(
"timestamp", time.Now().Format(time.StampNano),
"client-id", "web-api-client-us-east-1",
"user-id", "some-test-user-id",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
response, err := c.SayHello(ctx, &api.HelloRequest{Greeting: "World"})
if err != nil {
log.Fatalf("Error when calling SayHello: %s", err)
}
log.Printf("Response from server: %s", response.Reply)
}
func callSayHelloClientStream(c api.HelloServiceClient) {
md := metadata.Pairs(
"timestamp", time.Now().Format(time.StampNano),
"client-id", "web-api-client-us-east-1",
"user-id", "some-test-user-id",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
stream, err := c.SayHelloClientStream(ctx)
if err != nil {
log.Fatalf("Error when opening SayHelloClientStream: %s", err)
}
for i := 0; i < 5; i++ {
err := stream.Send(&api.HelloRequest{Greeting: "World"})
time.Sleep(time.Duration(i*50) * time.Millisecond)
if err != nil {
log.Fatalf("Error when sending to SayHelloClientStream: %s", err)
}
}
response, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("Error when closing SayHelloClientStream: %s", err)
}
log.Printf("Response from server: %s", response.Reply)
}
func callSayHelloServerStream(c api.HelloServiceClient) {
md := metadata.Pairs(
"timestamp", time.Now().Format(time.StampNano),
"client-id", "web-api-client-us-east-1",
"user-id", "some-test-user-id",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
stream, err := c.SayHelloServerStream(ctx, &api.HelloRequest{Greeting: "World"})
if err != nil {
log.Fatalf("Error when opening SayHelloServerStream: %s", err)
}
for {
response, err := stream.Recv()
if err == io.EOF {
break
} else if err != nil {
log.Fatalf("Error when receiving from SayHelloServerStream: %s", err)
}
log.Printf("Response from server: %s", response.Reply)
time.Sleep(50 * time.Millisecond)
}
}
func callSayHelloBidiStream(c api.HelloServiceClient) {
md := metadata.Pairs(
"timestamp", time.Now().Format(time.StampNano),
"client-id", "web-api-client-us-east-1",
"user-id", "some-test-user-id",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
stream, err := c.SayHelloBidiStream(ctx)
if err != nil {
log.Fatalf("Error when opening SayHelloBidiStream: %s", err)
}
serverClosed := make(chan struct{})
clientClosed := make(chan struct{})
go func() {
for i := 0; i < 5; i++ {
err := stream.Send(&api.HelloRequest{Greeting: "World"})
if err != nil {
log.Fatalf("Error when sending to SayHelloBidiStream: %s", err)
}
time.Sleep(50 * time.Millisecond)
}
err := stream.CloseSend()
if err != nil {
log.Fatalf("Error when closing SayHelloBidiStream: %s", err)
}
clientClosed <- struct{}{}
}()
go func() {
for {
response, err := stream.Recv()
if err == io.EOF {
break
} else if err != nil {
log.Fatalf("Error when receiving from SayHelloBidiStream: %s", err)
}
log.Printf("Response from server: %s", response.Reply)
time.Sleep(50 * time.Millisecond)
}
serverClosed <- struct{}{}
}()
// Wait until client and server both closed the connection.
<-clientClosed
<-serverClosed
}

View File

@ -1,39 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"log"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/exporters/stdout"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
// Init configures an OpenTelemetry exporter and trace provider
func Init() {
exporter, err := stdout.NewExporter(stdout.WithPrettyPrint())
if err != nil {
log.Fatal(err)
}
tp, err := sdktrace.NewProvider(
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
sdktrace.WithSyncer(exporter),
)
if err != nil {
log.Fatal(err)
}
global.SetTraceProvider(tp)
}

View File

@ -1,18 +0,0 @@
module go.opentelemetry.io/otel/example/grpc
go 1.14
replace (
go.opentelemetry.io/otel => ../..
go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout
go.opentelemetry.io/otel/sdk => ../../sdk
)
require (
github.com/golang/protobuf v1.4.2
go.opentelemetry.io/otel v0.10.0
go.opentelemetry.io/otel/exporters/stdout v0.10.0
go.opentelemetry.io/otel/sdk v0.10.0
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
google.golang.org/grpc v1.31.0
)

View File

@ -1,104 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/sketches-go v0.0.1 h1:RtG+76WKgZuz6FIaGsjoPePmadDBkuD/KC6+ZWu78b8=
github.com/DataDog/sketches-go v0.0.1/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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/sync v0.0.0-20190423024810-112230192c58/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-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -1,128 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"io"
"log"
"net"
"time"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/example/grpc/api"
"go.opentelemetry.io/otel/example/grpc/config"
"go.opentelemetry.io/otel/instrumentation/grpctrace"
"google.golang.org/grpc"
)
const (
port = ":7777"
)
// server is used to implement api.HelloServiceServer
type server struct {
api.HelloServiceServer
}
// SayHello implements api.HelloServiceServer
func (s *server) SayHello(ctx context.Context, in *api.HelloRequest) (*api.HelloResponse, error) {
log.Printf("Received: %v\n", in.GetGreeting())
time.Sleep(50 * time.Millisecond)
return &api.HelloResponse{Reply: "Hello " + in.Greeting}, nil
}
func (s *server) SayHelloServerStream(in *api.HelloRequest, out api.HelloService_SayHelloServerStreamServer) error {
log.Printf("Received: %v\n", in.GetGreeting())
for i := 0; i < 5; i++ {
err := out.Send(&api.HelloResponse{Reply: "Hello " + in.Greeting})
if err != nil {
return err
}
time.Sleep(time.Duration(i*50) * time.Millisecond)
}
return nil
}
func (s *server) SayHelloClientStream(stream api.HelloService_SayHelloClientStreamServer) error {
i := 0
for {
in, err := stream.Recv()
if err == io.EOF {
break
} else if err != nil {
log.Printf("Non EOF error: %v\n", err)
return err
}
log.Printf("Received: %v\n", in.GetGreeting())
i++
}
time.Sleep(50 * time.Millisecond)
return stream.SendAndClose(&api.HelloResponse{Reply: fmt.Sprintf("Hello (%v times)", i)})
}
func (s *server) SayHelloBidiStream(stream api.HelloService_SayHelloBidiStreamServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
break
} else if err != nil {
log.Printf("Non EOF error: %v\n", err)
return err
}
time.Sleep(50 * time.Millisecond)
log.Printf("Received: %v\n", in.GetGreeting())
err = stream.Send(&api.HelloResponse{Reply: "Hello " + in.Greeting})
if err != nil {
return err
}
}
return nil
}
func main() {
config.Init()
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(
grpc.UnaryInterceptor(grpctrace.UnaryServerInterceptor(global.Tracer(""))),
grpc.StreamInterceptor(grpctrace.StreamServerInterceptor(global.Tracer(""))),
)
api.RegisterHelloServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

View File

@ -1,51 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package grpctrace
import (
"google.golang.org/grpc"
"go.opentelemetry.io/otel/api/global"
)
func ExampleStreamClientInterceptor() {
tracer := global.Tracer("client-instrumentation")
_, _ = grpc.Dial(
"localhost",
grpc.WithStreamInterceptor(StreamClientInterceptor(tracer)),
)
}
func ExampleUnaryClientInterceptor() {
tracer := global.Tracer("client-instrumentation")
_, _ = grpc.Dial(
"localhost",
grpc.WithUnaryInterceptor(UnaryClientInterceptor(tracer)),
)
}
func ExampleStreamServerInterceptor() {
tracer := global.Tracer("server-instrumentation")
_ = grpc.NewServer(
grpc.StreamInterceptor(StreamServerInterceptor(tracer)),
)
}
func ExampleUnaryServerInterceptor() {
tracer := global.Tracer("server-instrumentation")
_ = grpc.NewServer(
grpc.UnaryInterceptor(UnaryServerInterceptor(tracer)),
)
}

View File

@ -1,95 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package grpctrace
import (
"context"
"google.golang.org/grpc/metadata"
"go.opentelemetry.io/otel/api/correlation"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/trace"
)
// Option is a function that allows configuration of the grpctrace Extract()
// and Inject() functions
type Option func(*config)
type config struct {
propagators propagation.Propagators
}
func newConfig(opts []Option) *config {
c := &config{propagators: global.Propagators()}
for _, o := range opts {
o(c)
}
return c
}
// WithPropagators sets the propagators to use for Extraction and Injection
func WithPropagators(props propagation.Propagators) Option {
return func(c *config) {
c.propagators = props
}
}
type metadataSupplier struct {
metadata *metadata.MD
}
func (s *metadataSupplier) Get(key string) string {
values := s.metadata.Get(key)
if len(values) == 0 {
return ""
}
return values[0]
}
func (s *metadataSupplier) Set(key string, value string) {
s.metadata.Set(key, value)
}
// Inject injects correlation context and span context into the gRPC
// metadata object. This function is meant to be used on outgoing
// requests.
func Inject(ctx context.Context, metadata *metadata.MD, opts ...Option) {
c := newConfig(opts)
propagation.InjectHTTP(ctx, c.propagators, &metadataSupplier{
metadata: metadata,
})
}
// Extract returns the correlation context and span context that
// another service encoded in the gRPC metadata object with Inject.
// This function is meant to be used on incoming requests.
func Extract(ctx context.Context, metadata *metadata.MD, opts ...Option) ([]kv.KeyValue, trace.SpanContext) {
c := newConfig(opts)
ctx = propagation.ExtractHTTP(ctx, c.propagators, &metadataSupplier{
metadata: metadata,
})
spanContext := trace.RemoteSpanContextFromContext(ctx)
var correlationCtxKVs []kv.KeyValue
correlation.MapFromContext(ctx).Foreach(func(kv kv.KeyValue) bool {
correlationCtxKVs = append(correlationCtxKVs, kv)
return true
})
return correlationCtxKVs, spanContext
}

View File

@ -1,459 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package grpctrace
// gRPC tracing middleware
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md
import (
"context"
"io"
"net"
"strings"
"go.opentelemetry.io/otel/semconv"
"github.com/golang/protobuf/proto" //nolint:staticcheck
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"go.opentelemetry.io/otel/api/correlation"
"go.opentelemetry.io/otel/api/kv"
"go.opentelemetry.io/otel/api/trace"
)
type messageType kv.KeyValue
// Event adds an event of the messageType to the span associated with the
// passed context with id and size (if message is a proto message).
func (m messageType) Event(ctx context.Context, id int, message interface{}) {
span := trace.SpanFromContext(ctx)
if p, ok := message.(proto.Message); ok {
span.AddEvent(ctx, "message",
kv.KeyValue(m),
semconv.RPCMessageIDKey.Int(id),
semconv.RPCMessageUncompressedSizeKey.Int(proto.Size(p)),
)
} else {
span.AddEvent(ctx, "message",
kv.KeyValue(m),
semconv.RPCMessageIDKey.Int(id),
)
}
}
var (
messageSent = messageType(semconv.RPCMessageTypeSent)
messageReceived = messageType(semconv.RPCMessageTypeReceived)
)
// UnaryClientInterceptor returns a grpc.UnaryClientInterceptor suitable
// for use in a grpc.Dial call.
func UnaryClientInterceptor(tracer trace.Tracer, opts ...Option) grpc.UnaryClientInterceptor {
return func(
ctx context.Context,
method string,
req, reply interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
callOpts ...grpc.CallOption,
) error {
requestMetadata, _ := metadata.FromOutgoingContext(ctx)
metadataCopy := requestMetadata.Copy()
name, attr := spanInfo(method, cc.Target())
var span trace.Span
ctx, span = tracer.Start(
ctx,
name,
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(attr...),
)
defer span.End()
Inject(ctx, &metadataCopy, opts...)
ctx = metadata.NewOutgoingContext(ctx, metadataCopy)
messageSent.Event(ctx, 1, req)
err := invoker(ctx, method, req, reply, cc, callOpts...)
messageReceived.Event(ctx, 1, reply)
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(s.Code(), s.Message())
}
return err
}
}
type streamEventType int
type streamEvent struct {
Type streamEventType
Err error
}
const (
closeEvent streamEventType = iota
receiveEndEvent
errorEvent
)
// clientStream wraps around the embedded grpc.ClientStream, and intercepts the RecvMsg and
// SendMsg method call.
type clientStream struct {
grpc.ClientStream
desc *grpc.StreamDesc
events chan streamEvent
eventsDone chan struct{}
finished chan error
receivedMessageID int
sentMessageID int
}
var _ = proto.Marshal
func (w *clientStream) RecvMsg(m interface{}) error {
err := w.ClientStream.RecvMsg(m)
if err == nil && !w.desc.ServerStreams {
w.sendStreamEvent(receiveEndEvent, nil)
} else if err == io.EOF {
w.sendStreamEvent(receiveEndEvent, nil)
} else if err != nil {
w.sendStreamEvent(errorEvent, err)
} else {
w.receivedMessageID++
messageReceived.Event(w.Context(), w.receivedMessageID, m)
}
return err
}
func (w *clientStream) SendMsg(m interface{}) error {
err := w.ClientStream.SendMsg(m)
w.sentMessageID++
messageSent.Event(w.Context(), w.sentMessageID, m)
if err != nil {
w.sendStreamEvent(errorEvent, err)
}
return err
}
func (w *clientStream) Header() (metadata.MD, error) {
md, err := w.ClientStream.Header()
if err != nil {
w.sendStreamEvent(errorEvent, err)
}
return md, err
}
func (w *clientStream) CloseSend() error {
err := w.ClientStream.CloseSend()
if err != nil {
w.sendStreamEvent(errorEvent, err)
} else {
w.sendStreamEvent(closeEvent, nil)
}
return err
}
const (
clientClosedState byte = 1 << iota
receiveEndedState
)
func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
events := make(chan streamEvent)
eventsDone := make(chan struct{})
finished := make(chan error)
go func() {
defer close(eventsDone)
// Both streams have to be closed
state := byte(0)
for event := range events {
switch event.Type {
case closeEvent:
state |= clientClosedState
case receiveEndEvent:
state |= receiveEndedState
case errorEvent:
finished <- event.Err
return
}
if state == clientClosedState|receiveEndedState {
finished <- nil
return
}
}
}()
return &clientStream{
ClientStream: s,
desc: desc,
events: events,
eventsDone: eventsDone,
finished: finished,
}
}
func (w *clientStream) sendStreamEvent(eventType streamEventType, err error) {
select {
case <-w.eventsDone:
case w.events <- streamEvent{Type: eventType, Err: err}:
}
}
// StreamClientInterceptor returns a grpc.StreamClientInterceptor suitable
// for use in a grpc.Dial call.
func StreamClientInterceptor(tracer trace.Tracer, opts ...Option) grpc.StreamClientInterceptor {
return func(
ctx context.Context,
desc *grpc.StreamDesc,
cc *grpc.ClientConn,
method string,
streamer grpc.Streamer,
callOpts ...grpc.CallOption,
) (grpc.ClientStream, error) {
requestMetadata, _ := metadata.FromOutgoingContext(ctx)
metadataCopy := requestMetadata.Copy()
name, attr := spanInfo(method, cc.Target())
var span trace.Span
ctx, span = tracer.Start(
ctx,
name,
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(attr...),
)
Inject(ctx, &metadataCopy, opts...)
ctx = metadata.NewOutgoingContext(ctx, metadataCopy)
s, err := streamer(ctx, desc, cc, method, callOpts...)
stream := wrapClientStream(s, desc)
go func() {
if err == nil {
err = <-stream.finished
}
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(s.Code(), s.Message())
}
span.End()
}()
return stream, err
}
}
// UnaryServerInterceptor returns a grpc.UnaryServerInterceptor suitable
// for use in a grpc.NewServer call.
func UnaryServerInterceptor(tracer trace.Tracer, opts ...Option) grpc.UnaryServerInterceptor {
return func(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
requestMetadata, _ := metadata.FromIncomingContext(ctx)
metadataCopy := requestMetadata.Copy()
entries, spanCtx := Extract(ctx, &metadataCopy, opts...)
ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{
MultiKV: entries,
}))
name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
ctx, span := tracer.Start(
trace.ContextWithRemoteSpanContext(ctx, spanCtx),
name,
trace.WithSpanKind(trace.SpanKindServer),
trace.WithAttributes(attr...),
)
defer span.End()
messageReceived.Event(ctx, 1, req)
resp, err := handler(ctx, req)
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(s.Code(), s.Message())
messageSent.Event(ctx, 1, s.Proto())
} else {
messageSent.Event(ctx, 1, resp)
}
return resp, err
}
}
// serverStream wraps around the embedded grpc.ServerStream, and intercepts the RecvMsg and
// SendMsg method call.
type serverStream struct {
grpc.ServerStream
ctx context.Context
receivedMessageID int
sentMessageID int
}
func (w *serverStream) Context() context.Context {
return w.ctx
}
func (w *serverStream) RecvMsg(m interface{}) error {
err := w.ServerStream.RecvMsg(m)
if err == nil {
w.receivedMessageID++
messageReceived.Event(w.Context(), w.receivedMessageID, m)
}
return err
}
func (w *serverStream) SendMsg(m interface{}) error {
err := w.ServerStream.SendMsg(m)
w.sentMessageID++
messageSent.Event(w.Context(), w.sentMessageID, m)
return err
}
func wrapServerStream(ctx context.Context, ss grpc.ServerStream) *serverStream {
return &serverStream{
ServerStream: ss,
ctx: ctx,
}
}
// StreamServerInterceptor returns a grpc.StreamServerInterceptor suitable
// for use in a grpc.NewServer call.
func StreamServerInterceptor(tracer trace.Tracer, opts ...Option) grpc.StreamServerInterceptor {
return func(
srv interface{},
ss grpc.ServerStream,
info *grpc.StreamServerInfo,
handler grpc.StreamHandler,
) error {
ctx := ss.Context()
requestMetadata, _ := metadata.FromIncomingContext(ctx)
metadataCopy := requestMetadata.Copy()
entries, spanCtx := Extract(ctx, &metadataCopy, opts...)
ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{
MultiKV: entries,
}))
name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
ctx, span := tracer.Start(
trace.ContextWithRemoteSpanContext(ctx, spanCtx),
name,
trace.WithSpanKind(trace.SpanKindServer),
trace.WithAttributes(attr...),
)
defer span.End()
err := handler(srv, wrapServerStream(ctx, ss))
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(s.Code(), s.Message())
}
return err
}
}
// spanInfo returns a span name and all appropriate attributes from the gRPC
// method and peer address.
func spanInfo(fullMethod, peerAddress string) (string, []kv.KeyValue) {
attrs := []kv.KeyValue{semconv.RPCSystemGRPC}
name, mAttrs := parseFullMethod(fullMethod)
attrs = append(attrs, mAttrs...)
attrs = append(attrs, peerAttr(peerAddress)...)
return name, attrs
}
// peerAttr returns attributes about the peer address.
func peerAttr(addr string) []kv.KeyValue {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return []kv.KeyValue(nil)
}
if host == "" {
host = "127.0.0.1"
}
return []kv.KeyValue{
semconv.NetPeerIPKey.String(host),
semconv.NetPeerPortKey.String(port),
}
}
// peerFromCtx returns a peer address from a context, if one exists.
func peerFromCtx(ctx context.Context) string {
p, ok := peer.FromContext(ctx)
if !ok {
return ""
}
return p.Addr.String()
}
// parseFullMethod returns a span name following the OpenTelemetry semantic
// conventions as well as all applicable span kv.KeyValue attributes based
// on a gRPC's FullMethod.
func parseFullMethod(fullMethod string) (string, []kv.KeyValue) {
name := strings.TrimLeft(fullMethod, "/")
parts := strings.SplitN(name, "/", 2)
if len(parts) != 2 {
// Invalid format, does not follow `/package.service/method`.
return name, []kv.KeyValue(nil)
}
var attrs []kv.KeyValue
if service := parts[0]; service != "" {
attrs = append(attrs, semconv.RPCServiceKey.String(service))
}
if method := parts[1]; method != "" {
attrs = append(attrs, semconv.RPCMethodKey.String(method))
}
return name, attrs
}

View File

@ -1,428 +0,0 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package grpctrace
import (
"context"
"sync"
"testing"
"time"
"go.opentelemetry.io/otel/api/trace/testtrace"
"go.opentelemetry.io/otel/semconv"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/golang/protobuf/proto" //nolint:staticcheck
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"go.opentelemetry.io/otel/api/kv"
)
type SpanRecorder struct {
mu sync.RWMutex
spans map[string]*testtrace.Span
}
func NewSpanRecorder() *SpanRecorder {
return &SpanRecorder{spans: make(map[string]*testtrace.Span)}
}
func (sr *SpanRecorder) OnStart(span *testtrace.Span) {}
func (sr *SpanRecorder) OnEnd(span *testtrace.Span) {
sr.mu.Lock()
defer sr.mu.Unlock()
sr.spans[span.Name()] = span
}
func (sr *SpanRecorder) Get(name string) (*testtrace.Span, bool) {
sr.mu.RLock()
defer sr.mu.RUnlock()
s, ok := sr.spans[name]
return s, ok
}
type mockUICInvoker struct {
ctx context.Context
}
func (mcuici *mockUICInvoker) invoker(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
mcuici.ctx = ctx
return nil
}
type mockProtoMessage struct{}
func (mm *mockProtoMessage) Reset() {
}
func (mm *mockProtoMessage) String() string {
return "mock"
}
func (mm *mockProtoMessage) ProtoMessage() {
}
func TestUnaryClientInterceptor(t *testing.T) {
clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure())
if err != nil {
t.Fatalf("failed to create client connection: %v", err)
}
sr := NewSpanRecorder()
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
tracer := tp.Tracer("grpctrace/client")
unaryInterceptor := UnaryClientInterceptor(tracer)
req := &mockProtoMessage{}
reply := &mockProtoMessage{}
uniInterceptorInvoker := &mockUICInvoker{}
checks := []struct {
method string
name string
expectedAttr map[kv.Key]kv.Value
eventsAttr []map[kv.Key]kv.Value
}{
{
method: "/github.com.serviceName/bar",
name: "github.com.serviceName/bar",
expectedAttr: map[kv.Key]kv.Value{
semconv.RPCSystemKey: kv.StringValue("grpc"),
semconv.RPCServiceKey: kv.StringValue("github.com.serviceName"),
semconv.RPCMethodKey: kv.StringValue("bar"),
semconv.NetPeerIPKey: kv.StringValue("fake"),
semconv.NetPeerPortKey: kv.StringValue("connection"),
},
eventsAttr: []map[kv.Key]kv.Value{
{
semconv.RPCMessageTypeKey: kv.StringValue("SENT"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(req))),
},
{
semconv.RPCMessageTypeKey: kv.StringValue("RECEIVED"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(reply))),
},
},
},
{
method: "/serviceName/bar",
name: "serviceName/bar",
expectedAttr: map[kv.Key]kv.Value{
semconv.RPCSystemKey: kv.StringValue("grpc"),
semconv.RPCServiceKey: kv.StringValue("serviceName"),
semconv.RPCMethodKey: kv.StringValue("bar"),
semconv.NetPeerIPKey: kv.StringValue("fake"),
semconv.NetPeerPortKey: kv.StringValue("connection"),
},
eventsAttr: []map[kv.Key]kv.Value{
{
semconv.RPCMessageTypeKey: kv.StringValue("SENT"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(req))),
},
{
semconv.RPCMessageTypeKey: kv.StringValue("RECEIVED"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(reply))),
},
},
},
{
method: "serviceName/bar",
name: "serviceName/bar",
expectedAttr: map[kv.Key]kv.Value{
semconv.RPCSystemKey: kv.StringValue("grpc"),
semconv.RPCServiceKey: kv.StringValue("serviceName"),
semconv.RPCMethodKey: kv.StringValue("bar"),
semconv.NetPeerIPKey: kv.StringValue("fake"),
semconv.NetPeerPortKey: kv.StringValue("connection"),
},
eventsAttr: []map[kv.Key]kv.Value{
{
semconv.RPCMessageTypeKey: kv.StringValue("SENT"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(req))),
},
{
semconv.RPCMessageTypeKey: kv.StringValue("RECEIVED"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(reply))),
},
},
},
{
method: "invalidName",
name: "invalidName",
expectedAttr: map[kv.Key]kv.Value{
semconv.RPCSystemKey: kv.StringValue("grpc"),
semconv.NetPeerIPKey: kv.StringValue("fake"),
semconv.NetPeerPortKey: kv.StringValue("connection"),
},
eventsAttr: []map[kv.Key]kv.Value{
{
semconv.RPCMessageTypeKey: kv.StringValue("SENT"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(req))),
},
{
semconv.RPCMessageTypeKey: kv.StringValue("RECEIVED"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(reply))),
},
},
},
{
method: "/github.com.foo.serviceName_123/method",
name: "github.com.foo.serviceName_123/method",
expectedAttr: map[kv.Key]kv.Value{
semconv.RPCSystemKey: kv.StringValue("grpc"),
semconv.RPCServiceKey: kv.StringValue("github.com.foo.serviceName_123"),
semconv.RPCMethodKey: kv.StringValue("method"),
semconv.NetPeerIPKey: kv.StringValue("fake"),
semconv.NetPeerPortKey: kv.StringValue("connection"),
},
eventsAttr: []map[kv.Key]kv.Value{
{
semconv.RPCMessageTypeKey: kv.StringValue("SENT"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(req))),
},
{
semconv.RPCMessageTypeKey: kv.StringValue("RECEIVED"),
semconv.RPCMessageIDKey: kv.IntValue(1),
semconv.RPCMessageUncompressedSizeKey: kv.IntValue(proto.Size(proto.Message(reply))),
},
},
},
}
for _, check := range checks {
if !assert.NoError(t, unaryInterceptor(context.Background(), check.method, req, reply, clientConn, uniInterceptorInvoker.invoker)) {
continue
}
span, ok := sr.Get(check.name)
if !assert.True(t, ok, "missing span %q", check.name) {
continue
}
assert.Equal(t, check.expectedAttr, span.Attributes())
assert.Equal(t, check.eventsAttr, eventAttrMap(span.Events()))
}
}
func eventAttrMap(events []testtrace.Event) []map[kv.Key]kv.Value {
maps := make([]map[kv.Key]kv.Value, len(events))
for i, event := range events {
maps[i] = event.Attributes
}
return maps
}
type mockClientStream struct {
Desc *grpc.StreamDesc
Ctx context.Context
}
func (mockClientStream) SendMsg(m interface{}) error { return nil }
func (mockClientStream) RecvMsg(m interface{}) error { return nil }
func (mockClientStream) CloseSend() error { return nil }
func (c mockClientStream) Context() context.Context { return c.Ctx }
func (mockClientStream) Header() (metadata.MD, error) { return nil, nil }
func (mockClientStream) Trailer() metadata.MD { return nil }
func TestStreamClientInterceptor(t *testing.T) {
clientConn, err := grpc.Dial("fake:connection", grpc.WithInsecure())
if err != nil {
t.Fatalf("failed to create client connection: %v", err)
}
// tracer
sr := NewSpanRecorder()
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
tracer := tp.Tracer("grpctrace/Server")
streamCI := StreamClientInterceptor(tracer)
var mockClStr mockClientStream
method := "/github.com.serviceName/bar"
name := "github.com.serviceName/bar"
streamClient, err := streamCI(
context.Background(),
&grpc.StreamDesc{ServerStreams: true},
clientConn,
method,
func(ctx context.Context,
desc *grpc.StreamDesc,
cc *grpc.ClientConn,
method string,
opts ...grpc.CallOption) (grpc.ClientStream, error) {
mockClStr = mockClientStream{Desc: desc, Ctx: ctx}
return mockClStr, nil
},
)
require.NoError(t, err, "initialize grpc stream client")
_, ok := sr.Get(name)
require.False(t, ok, "span should ended while stream is open")
req := &mockProtoMessage{}
reply := &mockProtoMessage{}
// send and receive fake data
for i := 0; i < 10; i++ {
_ = streamClient.SendMsg(req)
_ = streamClient.RecvMsg(reply)
}
// close client and server stream
_ = streamClient.CloseSend()
mockClStr.Desc.ServerStreams = false
_ = streamClient.RecvMsg(reply)
// added retry because span end is called in separate go routine
var span *testtrace.Span
for retry := 0; retry < 5; retry++ {
span, ok = sr.Get(name)
if ok {
break
}
time.Sleep(time.Second * 1)
}
require.True(t, ok, "missing span %s", name)
expectedAttr := map[kv.Key]kv.Value{
semconv.RPCSystemKey: kv.StringValue("grpc"),
semconv.RPCServiceKey: kv.StringValue("github.com.serviceName"),
semconv.RPCMethodKey: kv.StringValue("bar"),
semconv.NetPeerIPKey: kv.StringValue("fake"),
semconv.NetPeerPortKey: kv.StringValue("connection"),
}
assert.Equal(t, expectedAttr, span.Attributes())
events := span.Events()
require.Len(t, events, 20)
for i := 0; i < 20; i += 2 {
msgID := i/2 + 1
validate := func(eventName string, attrs map[kv.Key]kv.Value) {
for k, v := range attrs {
if k == semconv.RPCMessageTypeKey && v.AsString() != eventName {
t.Errorf("invalid event on index: %d expecting %s event, receive %s event", i, eventName, v.AsString())
}
if k == semconv.RPCMessageIDKey && v != kv.IntValue(msgID) {
t.Errorf("invalid id for message event expected %d received %d", msgID, v.AsInt32())
}
}
}
validate("SENT", events[i].Attributes)
validate("RECEIVED", events[i+1].Attributes)
}
// ensure CloseSend can be subsequently called
_ = streamClient.CloseSend()
}
func TestServerInterceptorError(t *testing.T) {
sr := NewSpanRecorder()
tp := testtrace.NewProvider(testtrace.WithSpanRecorder(sr))
tracer := tp.Tracer("grpctrace/Server")
usi := UnaryServerInterceptor(tracer)
deniedErr := status.Error(codes.PermissionDenied, "PERMISSION_DENIED_TEXT")
handler := func(_ context.Context, _ interface{}) (interface{}, error) {
return nil, deniedErr
}
_, err := usi(context.Background(), &mockProtoMessage{}, &grpc.UnaryServerInfo{}, handler)
require.Error(t, err)
assert.Equal(t, err, deniedErr)
span, ok := sr.Get("")
if !ok {
t.Fatalf("failed to export error span")
}
assert.Equal(t, span.StatusCode(), codes.PermissionDenied)
assert.Contains(t, deniedErr.Error(), span.StatusMessage())
assert.Len(t, span.Events(), 2)
assert.Equal(t, map[kv.Key]kv.Value{
kv.Key("message.type"): kv.StringValue("SENT"),
kv.Key("message.id"): kv.IntValue(1),
kv.Key("message.uncompressed_size"): kv.IntValue(26),
}, span.Events()[1].Attributes)
}
func TestParseFullMethod(t *testing.T) {
tests := []struct {
fullMethod string
name string
attr []kv.KeyValue
}{
{
fullMethod: "/grpc.test.EchoService/Echo",
name: "grpc.test.EchoService/Echo",
attr: []kv.KeyValue{
semconv.RPCServiceKey.String("grpc.test.EchoService"),
semconv.RPCMethodKey.String("Echo"),
},
}, {
fullMethod: "/com.example.ExampleRmiService/exampleMethod",
name: "com.example.ExampleRmiService/exampleMethod",
attr: []kv.KeyValue{
semconv.RPCServiceKey.String("com.example.ExampleRmiService"),
semconv.RPCMethodKey.String("exampleMethod"),
},
}, {
fullMethod: "/MyCalcService.Calculator/Add",
name: "MyCalcService.Calculator/Add",
attr: []kv.KeyValue{
semconv.RPCServiceKey.String("MyCalcService.Calculator"),
semconv.RPCMethodKey.String("Add"),
},
}, {
fullMethod: "/MyServiceReference.ICalculator/Add",
name: "MyServiceReference.ICalculator/Add",
attr: []kv.KeyValue{
semconv.RPCServiceKey.String("MyServiceReference.ICalculator"),
semconv.RPCMethodKey.String("Add"),
},
}, {
fullMethod: "/MyServiceWithNoPackage/theMethod",
name: "MyServiceWithNoPackage/theMethod",
attr: []kv.KeyValue{
semconv.RPCServiceKey.String("MyServiceWithNoPackage"),
semconv.RPCMethodKey.String("theMethod"),
},
}, {
fullMethod: "/pkg.srv",
name: "pkg.srv",
attr: []kv.KeyValue(nil),
}, {
fullMethod: "/pkg.srv/",
name: "pkg.srv/",
attr: []kv.KeyValue{
semconv.RPCServiceKey.String("pkg.srv"),
},
},
}
for _, test := range tests {
n, a := parseFullMethod(test.fullMethod)
assert.Equal(t, test.name, n)
assert.Equal(t, test.attr, a)
}
}