opentelemetry-go-contrib
opentelemetry-go-contrib copied to clipboard
Add gRPC Filter
After the discussion in #2571 and looking up the whole discussion in #512, I changed the direction just to focus on solving the issue #355 and other possible demand to filter the request to trace.
Most of the implementation are from #512. I tried to follow the package structure to match otelhttp.Filter but because of the difference between http.Request and the objects passed to four types of interceptors, some clumsy structs/functions remain to the surface.
Codecov Report
Merging #2572 (0c89101) into main (bfa71cf) will increase coverage by
0.1%. The diff coverage is81.1%.
@@ Coverage Diff @@
## main #2572 +/- ##
=======================================
+ Coverage 74.3% 74.4% +0.1%
=======================================
Files 144 145 +1
Lines 6569 6681 +112
=======================================
+ Hits 4883 4973 +90
- Misses 1543 1561 +18
- Partials 143 147 +4
| Impacted Files | Coverage Δ | |
|---|---|---|
| ...ation/google.golang.org/grpc/otelgrpc/grpctrace.go | 50.9% <0.0%> (-6.6%) |
:arrow_down: |
| ...ion/google.golang.org/grpc/otelgrpc/interceptor.go | 84.2% <66.6%> (-2.4%) |
:arrow_down: |
| ...google.golang.org/grpc/otelgrpc/filters/filters.go | 94.6% <94.6%> (ø) |
@dashpole thanks for the comment.
@Aneurysm9 does it make sense to you to have everything for filter under otelgrpc and otelgrpc/internal to eliminate unnecessary public functions and struct? I'm asking you because you submit otelhttp/filters and also suggested the common object for filters in #512.
@dashpole I moved everything under otelgrpc and now unnecessary public members are eliminated. Could you approve this?
@dashpole Thank you David for the review and approval!
To maintainers: should I change anything else?
@pellared @Aneurysm9 thank you both for the comments and suggestions. I updated the branch and it should look better now.
@Aneurysm9 @dashpole I'm trying to move filters to the subsidiary filters package and keep newXXXInterceptorInfo private in otelgrpc, but that requires either of the followings to achieve the restructuring:
- to expose all fields of
filters.InterceptorInfoto create the object innewXXXInterceptorInfo - to prepare a public constructor method to create the
filters.InterceptorInfoobject
Either way make things uglier, and either of them has pros/cons.
- Option 1:
- Implementation is straight forward, just replacing private field names with public ones
- It unnecessary exposes fields
- Option 2:
- We can keep the fields of
filter.InterceptorInfoprivate - The constructor requires the values of all fields as arguments regardless of actual fields used (eg. for StreamServer, it only uses
ssinfoandtyp, so they need to explicitly fill the other arguments with zero values when calling the constructor.) - The original motivation we keep
newXXXInterceptorInfofunctions private is not to expose the unnecessary functions for users, but the intention will be a bit ruined.
- We can keep the fields of
All filters and InterceptorInfo struct need to be in the same package, i.e. filters because it causes cyclic dependency if we keep InterceptorInfo in otelgrpc, so we need to figure out some ways to construct filters.InterceptorInfo objects out of the package.
@Aneurysm9 Kindly ping for your comments on the visibility of the fields IntercepterInfo, as I can't move forward without your comments. It's been 3 weeks since the beginning of the thread and it would be great if I can make this PR get ready for the merge in this week.
@MrAlias thank you for arranging this PR as the milestone. Could you help accelerate the review? I'm ready to change the code but I can't without the review from @Aneurysm9 but I have no idea how I can get help but for waiting.
@Aneurysm9 could you reply to this comment so that I can move forward to the acceptable change? https://github.com/open-telemetry/opentelemetry-go-contrib/pull/2572#issuecomment-1197595873
Apologies for the delay, I was on vacation.
All filters and
InterceptorInfostruct need to be in the same package, i.e.filtersbecause it causes cyclic dependency if we keepInterceptorInfoinotelgrpc, so we need to figure out some ways to constructfilters.InterceptorInfoobjects out of the package.
Not necessarily. There could be a third package that contains InterceptorInfo. This would be similar to the otelhttp instrumentation that uses the http.Request as an argument to its filters.
It might also be possible to, as @dashpole suggested, use otelgrpc.Filter as the type and filter.* implement that type. I think that would then only have otelgrpc depending on itself, filter depending on otelgrpc, and then user code depending on both, as required.
@Aneurysm9 I appreciate your comment. I hope you had a great vacation.
Not necessarily. There could be a third package that contains
InterceptorInfo. This would be similar to theotelhttpinstrumentation that uses thehttp.Requestas an argument to its filters.
The reason http.Request works in otelhttp is because it exposes its fields. If it's okay for IntercepterInfo to expose its fields as well, I'm +1 to the decision.
Also per declaring otelgrpc.Filter type, filter.* is referring to interceptorInfo's fields as of current implementation, so also from this point of view, InterceptorInfo's fields needs to be public or accessible from public methods.
Given, does it sound good to expose the fields of InterceptorInfo (I think preparing public methods is better)?
@Aneurysm9 I moved all filters under filters package. Those filters are in otelgrpc.Filter type and uses otelgrpc.InterceptorInfo. Also, because InterceptorInfo got smaller than what it was, I removed newXXXX constructors and directly instantiated InterceptorInfo, which are enabled by exposing its fields. This expose was required for fitler_test.go as well.
@Aneurysm9 Kindly ping because it's been a week since your last comment and I updated the code regarding it.
Open Telemetry in Go for grpc is unusable without this PR for our organization.
@Aneurysm9 @MadVikingGod @MrAlias hi, all maintainers, let me know how I can proceed from here. It's been two weeks since @Aneurysm9's last comment and I'm totally stalling.
For interface simplicity, if we don't think about abstraction for future, we can simply use method name as common input for otelgrpc.Filter and eliminate InterceptorInfo itself, as commented in https://github.com/open-telemetry/opentelemetry-go-contrib/pull/512#discussion_r602181665.
Of course, if we are to use context.Context for more filters, we can add it in the current implementation of InterceptorInfo.
type InterceptorInfo struct {
Ctx context.Context
Method string
UnaryServerInfo *grpc.UnaryServerInfo
StreamServerInfo *grpc.StreamServerInfo
Typ InterceptorType
}
Another option is just to keep method name and context when we instantiate InterceptorInfo, to eliminate InterceptorType switch. This enables InterceptorInfo to be simpler:
type InterceptorInfo struct {
Ctx context.Context
// Method should be either of the followings:
// - method passed to unary/stream client interceptor function
// - info.FullMethodName, where info is Unary/StreamServerInfo passed to server interceptor function
Method string
}
@Aneurysm9 Thank you for the approval! Yes, I'm happy to create a follow-up PR for better implementation :)