Added GRPC example
Hi, I have added a basic GRPC example (#96).
I took reference of the Java and Go example and used GRPC::ServerInterceptor for injecting the trace logic. Other parts are mostly copied from Ruby GRPC example (with some modifications), including the .proto file and server/client implementation.
I have 2 parts I am not sure about, which would like to seek some suggestions/guidance:
- How the parent context should be extracted? I saw in the
httpexample it usesOpenTelemetry.propagation.http.extract(env), should I addgrpcutil or just extract it fromcall.metadatain the Interceptor. Also, what metadata should I extract from there? - Should I assign the
grpc statustospan statuswhen error happens?
I found that protobuf seems to have issue with Ruby 2.7 (https://github.com/protocolbuffers/protobuf/issues/7070). I will test further see if it is my configuration or protobuf issue.
Any feedback is welcome, it is my first time contribution. Thank you very much.
Thanks for working on this!
The protobuf issue with Ruby 2.7 has been a blocker for us (Shopify) too. I’ll check internally to see if and how we’re working around it.
Assigning grpc status to span status is required: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md#status
The semantic conventions for gRPC are documented here: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md#grpc
The specification doesn’t describe how propagation is supposed to work with gRPC, but the Go implementation simply sets the keys in the metadata. I think we want to use the text propagators rather than http for that, since the latter (for Ruby) expects Rack formatted keys. @mwear can definitely correct me on that.
Thanks @fbogsany for pointing out the spec.
For the span name, as the grpc lib will underscore the rpc method name (https://github.com/grpc/grpc/blob/v1.28.1/src/ruby/lib/grpc/generic/service.rb#L98), we have to camelize it back.
For the peer attributes, we only have a string containing both the ip address and port so I added a regex to parse it.
Here is an example of current span data:
#<struct OpenTelemetry::SDK::Trace::SpanData
name="helloworld.Greeter/SayHello",
kind=:server,
status=0,
parent_span_id="0000000000000000",
child_count=0,
total_recorded_attributes=4,
total_recorded_events=0,
total_recorded_links=0,
start_timestamp=2020-04-23 19:42:36 +0800,
end_timestamp=2020-04-23 19:42:36 +0800,
attributes=
{"component"=>"grpc",
"rpc.service"=>"Greeter",
"net.peer.ip"=>"::1",
"net.peer.port"=>"60369"},
links=nil,
events=nil,
library_resource=
#<OpenTelemetry::SDK::Resources::Resource:0x00007fb1eab52680
@labels={"name"=>"grpc", "version"=>"semver:1.0"}>,
span_id="f8f0633f420eafa1",
trace_id="4cf5dee9d1d02d7242123d2fed2acd27",
trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x00007fb1eb0e70c8 @flags=1>>
I also assigned the context to be OpenTelemetry.propagation.text.extract(call.metadata), not sure if I am doing it right.
I have studied the Go implementation again and did some adjustments:
-
opentelemetry-gomade thegrpctracea plugin (https://github.com/open-telemetry/opentelemetry-go/tree/master/plugin/grpctrace) which contains the server/client interceptors for different scenarios. I copied the idea and the interceptors are grouped under a single moduleGRPCTraceunder the examplelibfolder which can potentially be extracted out in the future - Understand better about the
contextpropagation, used theOpenTelemetry.propagation.http.injectandOpenTelemetry.propagation.http.extractfor the propagation in the interceptors (referencing Go implementation again)
Two hacks are needed so far because the grpc interceptors does not have enough info exposed (it is still an experimental API) for building the span:
- For client interceptor, it can't access the
peerinfo from thecallsoinstance_variable_getneeds to be used to access underlying data - For server interceptor, the RPC method name is lost (which is available for client interceptor). Right now, I try to reconstruct it from the actual
methodobject
I will continue to explore for StreamServer and StreamClient
@pinglamb are you still working on this?
I can continue to work on this, any comments so far? Thanks!
@pinglamb have any progress with this one?