GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat(api_core): add sync and async gRPC transport attempt logging interceptors by chalmerlowe · Pull Request #19010 · googleapis/google-cloud-python

Conversation

@chalmerlowe

@chalmerlowe chalmerlowe commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Problem / Context

To capture transient wire failures and retries across synchronous and asynchronous gRPC transports without blocking in-flight grpc.Future or grpc.aio.UnaryUnaryCall objects, google.api_core._observability needs gRPC client interceptors that inspect completed RPC attempts and emit structured DEBUG logs correlated with the active Tier 4 OpenTelemetry span.

Bug: b/479827845

Solution

  • Add _GrpcAttemptLoggingInterceptor and _AsyncGrpcAttemptLoggingInterceptor in google/api_core/_observability.py to log failed unary-unary gRPC wire attempts at DEBUG level via log_transport_attempt.
  • Attach post-dispatch status and exception inspection via add_done_callback when supported on the returned gRPC response/call object so grpc.Future and grpc.aio.UnaryUnaryCall execution remains non-blocking.
  • Update get_otel_interceptor and get_otel_async_interceptor to sandwich the attempt logging interceptor between the outer OpenTelemetry tracing interceptor (so the Tier 4 span is active for trace correlation) and the inner _SuppressingClientInterceptor / _AsyncSuppressingClientInterceptor.
  • Add unit tests in tests/unit/test_observability.py covering sync/async response callbacks, direct exceptions, fail-open inspection errors, and interceptor ordering.

Verification

Relying on GitHub Actions CI for full matrix verification, alongside local unit test (unit-3.14) and lint verification.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gemini-code-assist[bot] reacted with eyes emoji
All reactions
  • 👀 1 reaction
gemini-code-assist[bot]
gemini-code-assist Bot reviewed Nov 6, 2026
View reviewed changes

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

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.

Code Review

This pull request introduces synchronous and asynchronous gRPC client interceptors (_GrpcAttemptLoggingInterceptor and _AsyncGrpcAttemptLoggingInterceptor) to log failed wire attempts at the DEBUG level, updating the OpenTelemetry interceptor retrieval functions to incorporate them. The review feedback recommends checking if the gRPC call or future has been cancelled before calling exception() or awaiting code() to avoid raising unhandled CancelledError or AioRpcError exceptions, ensuring robust fail-open behavior.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread packages/google-api-core/google/api_core/_observability.py
Comment on lines +915 to +916
exc_fn = getattr(resp, "exception", None)
if callable(exc_fn) and (resp_exc := exc_fn()) is not None:

@gemini-code-assist gemini-code-assist Bot Nov 6, 2026

Copy link
Copy Markdown
Contributor

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.

medium

When implementing a completion callback for a future-like object, always check if the future was cancelled using cancelled() before calling exception() to avoid raising an unnecessary exception (such as CancelledError or FutureCancelledError).

                cancelled_fn = getattr(resp, "cancelled", None)
                if callable(cancelled_fn) and cancelled_fn():
                    return
                exc_fn = getattr(resp, "exception", None)
                if callable(exc_fn) and (resp_exc := exc_fn()) is not None:
References
  1. When implementing a completion callback for a concurrent.futures.Future, always check if the future was cancelled using future.cancelled() before calling future.exception() to avoid raising an unhandled CancelledError.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@chalmerlowe chalmerlowe Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

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.

Fixed in 42a6423 — broadened the fail-open catch in _inspect_sync_response to except (Exception, asyncio.CancelledError): and added a unit test covering cancelled Future inspection.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread packages/google-api-core/google/api_core/_observability.py

async def _inspect_async_call(target_call: Any) -> None:
try:
code_fn = getattr(target_call, "code", None)

@gemini-code-assist gemini-code-assist Bot Nov 6, 2026

Copy link
Copy Markdown
Contributor

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.

medium

For async gRPC calls, calling await code_fn() on a cancelled call can raise a CancelledError or AioRpcError. To prevent unnecessary exceptions and ensure a clean fail-open behavior, check if the call was cancelled using cancelled() before awaiting its code or details.

                cancelled_fn = getattr(target_call, "cancelled", None)
                if callable(cancelled_fn) and cancelled_fn():
                    return
                code_fn = getattr(target_call, "code", None)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@chalmerlowe chalmerlowe Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

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.

Fixed in 42a6423 — broadened the fail-open catch in _inspect_async_call to except (Exception, asyncio.CancelledError): and added a unit test covering cancelled async Call inspection.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@chalmerlowe
chalmerlowe force-pushed the feat/otel-logging-m3-s3-grpc-attempt-logging branch from 0b90e49 to 42a6423 Compare November 6, 2026 16:17
@chalmerlowe chalmerlowe mentioned this pull request Nov 6, 2026
feat(api_core): implement L2/L3 method error logging and L4 transport attempt logging #19000
Closed
@chalmerlowe
feat(api_core): add sync and async gRPC transport attempt logging int…
0cbf1b7
…erceptors
@chalmerlowe
chalmerlowe force-pushed the feat/otel-logging-m3-s3-grpc-attempt-logging branch from 42a6423 to 0cbf1b7 Compare November 10, 2026 00:17
@chalmerlowe
chalmerlowe force-pushed the feat/otel-logging-m3-s2-logging-helpers branch from 866406e to 56c1e04 Compare November 10, 2026 00:17

This branch has not been deployed

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

Reviewers

1 more reviewer
@gemini-code-assist gemini-code-assist[bot] gemini-code-assist[bot] left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@chalmerlowe
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.