Repository navigation
fix(gapic-generator): default client_options.api_endpoint to transport host for OpenTelemetry spans #18869
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
base: main
Are you sure you want to change the base?
fix(gapic-generator): default client_options.api_endpoint to transport host for OpenTelemetry spans #18869
Changes from all commits
ad65d4c
81e79e2
8ac1e81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,13 @@ | |
| {% block content %} | ||
|
|
||
| import abc | ||
| import copy | ||
| from typing import {% if service.any_extended_operations_methods %}Any, {% endif %}Awaitable, Callable, Dict, Optional, Sequence, Union | ||
|
|
||
| {% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %} | ||
| from {{package_path}} import gapic_version as package_version | ||
| from {{package_path}}._compat import ( | ||
| _observability, | ||
| ASYNC_WRAP_METHOD_SUPPORTS_KIND as _ASYNC_WRAP_METHOD_SUPPORTS_KIND, | ||
| WRAP_METHOD_SUPPORTS_TRACING as _WRAP_METHOD_SUPPORTS_TRACING, | ||
| ) | ||
|
|
@@ -162,6 +164,24 @@ class {{ service.name }}Transport(abc.ABC): | |
| host += ':443' | ||
| self._host = host | ||
|
|
||
| # If the transport was instantiated directly with client_options=None | ||
| # while OpenTelemetry tracing is enabled via environment variable, | ||
| # create a ClientOptions instance with the resolved transport host so | ||
| # method and wire spans can record server.address and server.port. | ||
| if ( | ||
| client_options is None | ||
| and _observability is not None | ||
| and _observability.is_otel_capabilities_enabled(None) | ||
| ): | ||
| client_options = client_options_lib.ClientOptions(api_endpoint=self._host) | ||
| # And here we check for the presence of an api_endpoint attr OR key, | ||
| # shallow-copying before populating to avoid mutating caller-owned state. | ||
| elif isinstance(client_options, client_options_lib.ClientOptions) and not client_options.api_endpoint: | ||
| client_options = copy.copy(client_options) | ||
| client_options.api_endpoint = self._host | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It feels a little weird to be writing to client_options, which is usually user-provided configuration. I could see that confusing things in the future, if we don't know the source of certain data. We usually prefer user-provided configuration over defaults It's probably ok since this is an internal copy. But this does give me pause, and maybe there's a better way to handle this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. QUESTION I agree with your instinct here — overwriting The only reason we routed What do you think of this approach instead?
Those functions have lean signatures right now, so adding
Thoughts? |
||
| elif isinstance(client_options, dict) and not client_options.get("api_endpoint"): | ||
| client_options = copy.copy(client_options) | ||
| client_options["api_endpoint"] = self._host | ||
| self._client_options = client_options | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: It's a little surprising that we can store this field as either a dict or a ClientOptions instance. I'd recommend turning the dict representation into ClientOptions before this, which would simplify the conditional above, since you'd know the exact type
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RESOLVED Good call — normalizing |
||
| self._wrapped_methods: Dict[Callable, Callable] = {} | ||
|
|
||
|
|
||
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.
There's a an edge case here: the host argument is only used to build new channels. But grpc transports can be passed in custom channels, which may point at a different host than the one here. So we may be reporting different data than the channel actually sends.
Is it possible to add some logic to the grpc transport, to disable this override when a channel is passed in directly? (_ignore_credentials seems to already be doing something similar)
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.
But depending on how this data is consumed later, maybe this isn't a big problem