Skip to content

Conversation

@westarle
Copy link
Contributor

  • Implement ResponseFuture to wrap the inner future and record status.
  • This records the span after the headers arrive (before the body is read and decoded)
  • We will read the body (for large payloads e.g.) and handling errors (cancelation e.g.) in a future PR

#3766

@westarle westarle changed the title feat: add status handling for gRPC network spans (PR 7) impl(o11y): add status handling for gRPC network spans Nov 14, 2025
@codecov
Copy link

codecov bot commented Nov 14, 2025

Codecov Report

❌ Patch coverage is 87.50000% with 4 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@80b732a). Learn more about missing BASE report.
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
src/gax-internal/src/observability/grpc_tracing.rs 87.50% 4 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3808   +/-   ##
=======================================
  Coverage        ?   96.09%           
=======================================
  Files           ?      146           
  Lines           ?     5713           
  Branches        ?        0           
=======================================
  Hits            ?     5490           
  Misses          ?      223           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@westarle westarle force-pushed the feat/t4-grpc-status-handling branch from 2bf51ac to f81b970 Compare November 14, 2025 16:19
- Implement `ResponseFuture` to wrap the inner future and record status.
- Add `pin-project` and `futures-util` dependencies (moved to workspace).
- Populate `rpc.grpc.status_code`, `otel.status_code`, and `error.type`.
- Use `to_gax_error` to convert `tonic::Status` into `gax::error::Error` for consistent `error.type` strings.
- Default to `OK` status if `grpc-status` header is missing but HTTP response is successful.
- Refactor `ResponseFuture::poll` to use standalone helper functions for clarity.
- Limitation: Only captures status codes in HTTP headers.
@westarle westarle force-pushed the feat/t4-grpc-status-handling branch from f81b970 to 6cf7f3b Compare November 14, 2025 17:08
@westarle westarle marked this pull request as ready for review November 14, 2025 17:08
@westarle westarle requested a review from a team as a code owner November 14, 2025 17:08
Copy link
Collaborator

@coryan coryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, some questions and (maybe) improvements.

Comment on lines +142 to +143
let _guard = this.span.enter();
let result = futures_util::ready!(this.inner.poll(cx));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but I had to read the code of futures_util::ready! to convince myself. Maybe with more experience I will not care, but it is not obvious that this "leaves" the span when the inner future is not ready.

Maybe a small comment?

type Error = S::Error;
// We use `Box<dyn Future...>` (type erasure) here to simplify the type signature.
// Without this, we would need to explicitly name the complex type returned by
// `.instrument()` (and any implementation changes in `call`), which can be verbose and brittle.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are not calling .instrument() can we simplify this code / reduce the overhead?

}

fn record_response_status<ResBody>(span: &tracing::Span, response: &http::Response<ResBody>) {
match tonic::Status::from_header_map(response.headers()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this function get called twice then? One for this layer and once in the real tonic layer? Do you have a sense of how expensive it is for the "success" case? Am I connect in assuming it is fairly fast because it returns immediately (as the header does not exist)?

https://docs.rs/tonic/latest/src/tonic/status.rs.html#465

It may be fine if it is a bit expensive, just wan to document the tradeoff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants