-
Notifications
You must be signed in to change notification settings - Fork 4.5k
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
stats/opentelemetry: separate out interceptors for tracing and metrics #8063
base: master
Are you sure you want to change the base?
stats/opentelemetry: separate out interceptors for tracing and metrics #8063
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #8063 +/- ##
==========================================
- Coverage 82.32% 82.30% -0.03%
==========================================
Files 392 392
Lines 39140 39207 +67
==========================================
+ Hits 32222 32269 +47
- Misses 5597 5611 +14
- Partials 1321 1327 +6
🚀 New features to boost your workflow:
|
69df069
to
71804b4
Compare
@janardhanvissa its not clear what is the intention of this refactor. The follow up from opentelemetry tracing API PR was to create separate interceptors for metrics and traces. Right now, single interceptor is handling both trace and metrics options. Once we have separate unary and stream interceptor each for tracing and metrics, we don't have to check for options disabled/enabled everytime. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see this discussion https://github.com/grpc/grpc-go/pull/7852/files#r1909469701 and modify accordingly.
type clientMetricsStatsHandler struct { | ||
*clientStatsHandler | ||
} | ||
type clientTracingStatsHandler struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new line in between struct declaration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
method: h.determineMethod(method, opts...), | ||
} | ||
ctx = setCallInfo(ctx, ci) | ||
if h.options.MetricsOptions.pluginOption != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this metadata part is not applicable for tracing. It should only be in metrics interceptor. Please remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
ctx = setCallInfo(ctx, ci) | ||
|
||
if h.options.MetricsOptions.pluginOption != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same. this metadata part is not applicable for tracing. It should only be in metrics interceptor. Please remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
// perCallTraces records per call trace spans. | ||
func (h *clientTracingStatsHandler) perCallTraces(_ context.Context, err error, _ time.Time, _ *callInfo, ts trace.Span) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we don't need time and callInfo, we should not just have them here instead of replacing them with _
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -211,24 +218,41 @@ func (h *serverStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) | |||
|
|||
// HandleRPC implements per RPC tracing and stats implementation. | |||
func (h *serverStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { | |||
if h.options.isMetricsEnabled() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should have separate HandleRPC for metrics and trace statsHandlers instead of having common which requires to check what is enabled etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
type clientTracingStatsHandler struct { | ||
*clientStatsHandler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we extending clientStatsHandler. Let's just keep all stats handler embedding estats.MetricsRecorder
. Increasing levels in hierarchy doesn't help in anyway because metrics and traces stats handler doesn't share common functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think keeping the inheritance (clientMetricsStatsHandler and clientTracingStatsHandler embedding *clientStatsHandler) would be beneficial because it avoids duplicating the common fields and methods in both metrics and tracing handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as i know there is nothing common between metrics and traces so its not providing any help. Which are the common things you found?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, you're right. After further review and refinement, there isn't significant functional commonality between the metrics and tracing handlers themselves. I thought inheritance or embedding thinking there could be shared setup or code reduction, but now I see the benefits of separation for clarity and maintainability are more important here.
stats/opentelemetry/opentelemetry.go
Outdated
return joinDialOptions(grpc.WithChainUnaryInterceptor(csh.unaryInterceptor), grpc.WithChainStreamInterceptor(csh.streamInterceptor), grpc.WithStatsHandler(csh)) | ||
var do []grpc.DialOption | ||
|
||
if o.isMetricsEnabled() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't need to check for MetricsEnabled as that's being added as no-op and check is there in initializeMetrics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
stats/opentelemetry/opentelemetry.go
Outdated
return joinServerOptions(grpc.ChainUnaryInterceptor(ssh.unaryInterceptor), grpc.ChainStreamInterceptor(ssh.streamInterceptor), grpc.StatsHandler(ssh)) | ||
var so []grpc.ServerOption | ||
|
||
if o.isMetricsEnabled() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same. We don't need to check for metrics enabled
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
stats/opentelemetry/opentelemetry.go
Outdated
options: o, | ||
} | ||
tracingHandler.initializeTraces() | ||
do = append(do, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same: one line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
stats/opentelemetry/opentelemetry.go
Outdated
) | ||
} | ||
if o.isTracingEnabled() { | ||
tracingHandler := &clientTracingHandler{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: one line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
stats/opentelemetry/opentelemetry.go
Outdated
var do []grpc.DialOption | ||
|
||
if o.isMetricsEnabled() { | ||
metricsHandler := &clientStatsHandler{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we adding these new changes? We should keep the current part as is and only add the tracing part. Please revert the refactor changes done for clientStatsHandler here
csh := &clientStatsHandler{options: o}
csh.initializeMetrics()
do := joinDialOptions(grpc.WithChainUnaryInterceptor(csh.unaryInterceptor), grpc.WithChainStreamInterceptor(csh.streamInterceptor), grpc.WithStatsHandler(csh))
if o.isTracingEnabled(){
...
do := joinDialOptions(grpc.WithChainUnaryInterceptor(th.unaryInterceptor),.....)
}
return do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
func newServerStatsHandler(options MetricsOptions) metricsRecorderForTest { | ||
return &serverStatsHandler{options: Options{MetricsOptions: options}} | ||
rm := ®istryMetrics{optionalLabels: options.OptionalLabels} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same. We should't need to change anything here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -47,11 +47,13 @@ type metricsRecorderForTest interface { | |||
} | |||
|
|||
func newClientStatsHandler(options MetricsOptions) metricsRecorderForTest { | |||
return &clientStatsHandler{options: Options{MetricsOptions: options}} | |||
rm := ®istryMetrics{optionalLabels: options.OptionalLabels} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't need to change anything here. Please revert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -178,6 +201,14 @@ func (h *serverStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) | |||
// HandleConn exists to satisfy stats.Handler. | |||
func (h *serverStatsHandler) HandleConn(context.Context, stats.ConnStats) {} | |||
|
|||
// TagConn exists to satisfy stats.Handler for tracing. | |||
func (h *serverTracingHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context { | |||
return ctx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: single line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After saving the file it's coming in a newline.
@@ -170,6 +185,14 @@ func (h *serverStatsHandler) streamInterceptor(srv any, ss grpc.ServerStream, _ | |||
return err | |||
} | |||
|
|||
func (h *serverTracingHandler) unaryInterceptor(ctx context.Context, req any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: single line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After saving the file it's coming in a newline.
} | ||
ai := ri.ai | ||
|
||
ctx, _ = h.traceTagRPC(ctx, ai) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can directly pass ri.ai
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -71,10 +76,18 @@ func (h *clientStatsHandler) initializeMetrics() { | |||
rm.registerMetrics(metrics, meter) | |||
} | |||
|
|||
func (h *clientTracingHandler) initializeTraces() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same. Should go to client_tracing.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
func (h *clientTracingHandler) unaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { | ||
ci := &callInfo{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like we are setting callInfo again here. Since, tracingHandler is added after clientStatsHandler, the callInfo would have already been created. Similar to what we are doing with attempt info, we should try fetching here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
func (h *clientTracingHandler) streamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { | ||
ci := &callInfo{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment about callInfo as unaryInterceptor
of tracingHandler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
ai := ri.ai | ||
|
||
ctx, _ = h.traceTagRPC(ctx, ai) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can pass ri.ai
directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
ai := ri.ai | ||
|
||
ctx, _ = h.traceTagRPC(ctx, ai) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't ignore the attempt info because that contains the trace span. We should set it again in the context using setRPCInfo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// Fetches rpcInfo from context. This handler requires a preceding stats handler | ||
// in the interceptor chain to have already created and set the rpcInfo. | ||
func (h *clientTracingHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) context.Context { | ||
ri := getRPCInfo(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment explaining why we are fetching getRPCInfo
@@ -71,10 +68,12 @@ func (h *clientStatsHandler) initializeMetrics() { | |||
rm.registerMetrics(metrics, meter) | |||
} | |||
|
|||
// unaryInterceptor implements the UnaryClientInterceptor to record metrics for | |||
// unary calls. | |||
func (h *clientStatsHandler) unaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a doc string // unaryInterceptor records metrics for unary RPC calls. It updates the context
// with call info, adds plugin metadata if configured, and tracks call duration.
RELEASE NOTE: None