Skip to content

Commit 3d44d15

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat!: Switch cloudtrace.googleapis.com to telemetry.googleapis.com for tracing API.
PiperOrigin-RevId: 821692627
1 parent 7a1262b commit 3d44d15

File tree

3 files changed

+93
-94
lines changed

3 files changed

+93
-94
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
"google-cloud-trace < 2",
159159
"google-cloud-logging < 4",
160160
"opentelemetry-sdk < 2",
161-
"opentelemetry-exporter-gcp-trace < 2",
161+
"opentelemetry-exporter-otlp-proto-http < 2",
162162
"pydantic >= 2.11.1, < 3",
163163
"typing_extensions",
164164
]

tests/unit/vertex_adk/test_agent_engine_templates_adk.py

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import os
1919
from unittest import mock
2020
from typing import Optional
21-
import dataclasses
2221

2322
from google import auth
2423
import vertexai
@@ -96,27 +95,11 @@ def vertexai_init_mock():
9695

9796

9897
@pytest.fixture
99-
def cloud_trace_exporter_mock():
100-
import sys
101-
import opentelemetry
102-
103-
mock_cloud_trace_exporter = mock.Mock()
104-
105-
opentelemetry.exporter = type(sys)("exporter")
106-
opentelemetry.exporter.cloud_trace = type(sys)("cloud_trace")
107-
opentelemetry.exporter.cloud_trace.CloudTraceSpanExporter = (
108-
mock_cloud_trace_exporter
109-
)
110-
111-
sys.modules["opentelemetry.exporter"] = opentelemetry.exporter
112-
sys.modules["opentelemetry.exporter.cloud_trace"] = (
113-
opentelemetry.exporter.cloud_trace
114-
)
115-
116-
yield mock_cloud_trace_exporter
117-
118-
del sys.modules["opentelemetry.exporter.cloud_trace"]
119-
del sys.modules["opentelemetry.exporter"]
98+
def otlp_span_exporter_mock():
99+
with mock.patch(
100+
"opentelemetry.exporter.otlp.proto.http.trace_exporter.OTLPSpanExporter"
101+
) as otlp_span_exporter_mock:
102+
yield otlp_span_exporter_mock
120103

121104

122105
@pytest.fixture
@@ -523,7 +506,7 @@ def test_custom_instrumentor_enablement(
523506

524507
@mock.patch.dict(os.environ, {"GOOGLE_CLOUD_AGENT_ENGINE_ID": "test_agent_id"})
525508
def test_tracing_setup(
526-
self, trace_provider_mock: mock.Mock, cloud_trace_exporter_mock: mock.Mock
509+
self, trace_provider_mock: mock.Mock, otlp_span_exporter_mock: mock.Mock
527510
):
528511
app = agent_engines.AdkApp(agent=_TEST_AGENT, enable_tracing=True)
529512
app.set_up()
@@ -537,17 +520,9 @@ def test_tracing_setup(
537520
"cloud.resource_id": "//aiplatform.googleapis.com/projects/test-project/locations/us-central1/reasoningEngines/test_agent_id",
538521
}
539522

540-
@dataclasses.dataclass
541-
class RegexMatchingAll:
542-
keys: set[str]
543-
544-
def __eq__(self, regex: object) -> bool:
545-
return isinstance(regex, str) and set(regex.split("|")) == self.keys
546-
547-
cloud_trace_exporter_mock.assert_called_once_with(
548-
project_id=_TEST_PROJECT,
549-
client=mock.ANY,
550-
resource_regex=RegexMatchingAll(keys=set(expected_attributes.keys())),
523+
otlp_span_exporter_mock.assert_called_once_with(
524+
session=mock.ANY,
525+
endpoint="https://telemetry.googleapis.com/v1/traces",
551526
)
552527

553528
assert (
@@ -559,7 +534,6 @@ def __eq__(self, regex: object) -> bool:
559534
def test_enable_tracing(
560535
self,
561536
caplog,
562-
cloud_trace_exporter_mock,
563537
tracer_provider_mock,
564538
simple_span_processor_mock,
565539
):

vertexai/agent_engines/templates/adk.py

Lines changed: 83 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,28 @@ def _warn(msg: str):
215215
_warn._LOGGER.warning(msg) # pyright: ignore[reportFunctionMemberAccess]
216216

217217

218+
def _force_flush_traces():
219+
try:
220+
import opentelemetry.trace
221+
except (ImportError, AttributeError):
222+
_warn(
223+
"Could not force flush traces. opentelemetry-api is not installed. Please call 'pip install google-cloud-aiplatform[agent_engines]'."
224+
)
225+
return None
226+
227+
try:
228+
import opentelemetry.sdk.trace
229+
except (ImportError, AttributeError):
230+
_warn(
231+
"Could not force flush traces. opentelemetry-sdk is not installed. Please call 'pip install google-cloud-aiplatform[agent_engines]'."
232+
)
233+
return None
234+
235+
provider = opentelemetry.trace.get_tracer_provider()
236+
if isinstance(provider, opentelemetry.sdk.trace.TracerProvider):
237+
_ = provider.force_flush()
238+
239+
218240
def _default_instrumentor_builder(
219241
project_id: str,
220242
*,
@@ -288,28 +310,23 @@ def _detect_cloud_resource_id(project_id: str) -> Optional[str]:
288310

289311
if enable_tracing:
290312
try:
291-
import opentelemetry.exporter.cloud_trace
313+
import opentelemetry.exporter.otlp.proto.http.trace_exporter
314+
import google.auth.transport.requests
292315
except (ImportError, AttributeError):
293316
return _warn_missing_dependency(
294-
"opentelemetry-exporter-gcp-trace", needed_for_tracing=True
295-
)
296-
297-
try:
298-
import google.cloud.trace_v2
299-
except (ImportError, AttributeError):
300-
return _warn_missing_dependency(
301-
"google-cloud-trace", needed_for_tracing=True
317+
"opentelemetry-exporter-otlp-proto-http", needed_for_tracing=True
302318
)
303319

304320
import google.auth
305321

306322
credentials, _ = google.auth.default()
307-
span_exporter = opentelemetry.exporter.cloud_trace.CloudTraceSpanExporter(
308-
project_id=project_id,
309-
client=google.cloud.trace_v2.TraceServiceClient(
310-
credentials=credentials.with_quota_project(project_id),
311-
),
312-
resource_regex="|".join(resource.attributes.keys()),
323+
span_exporter = (
324+
opentelemetry.exporter.otlp.proto.http.trace_exporter.OTLPSpanExporter(
325+
session=google.auth.transport.requests.AuthorizedSession(
326+
credentials=credentials
327+
),
328+
endpoint="https://telemetry.googleapis.com/v1/traces",
329+
)
313330
)
314331
span_processor = opentelemetry.sdk.trace.export.BatchSpanProcessor(
315332
span_exporter=span_exporter,
@@ -646,54 +663,17 @@ def set_up(self):
646663
else:
647664
os.environ["ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS"] = "false"
648665

649-
GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY = (
650-
"GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY"
651-
)
652-
653-
def telemetry_enabled() -> Optional[bool]:
654-
return (
655-
os.getenv(GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY, "0").lower()
656-
in ("true", "1")
657-
if GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY in os.environ
658-
else None
659-
)
660-
661-
# Tracing enablement follows truth table:
662-
def tracing_enabled() -> bool:
663-
"""Tracing enablement follows true table:
664-
665-
| enable_tracing | enable_telemetry(env) | tracing_actually_enabled |
666-
|----------------|-----------------------|--------------------------|
667-
| false | false | false |
668-
| false | true | false |
669-
| false | None | false |
670-
| true | false | false |
671-
| true | true | true |
672-
| true | None | true |
673-
| None(default) | false | false |
674-
| None(default) | true | adk_version >= 1.17 |
675-
| None(default) | None | false |
676-
"""
677-
enable_tracing: Optional[bool] = self._tmpl_attrs.get("enable_tracing")
678-
enable_telemetry: Optional[bool] = telemetry_enabled()
679-
680-
return (enable_tracing is True and enable_telemetry is not False) or (
681-
enable_tracing is None
682-
and enable_telemetry is True
683-
and is_version_sufficient("1.17.0")
684-
)
685-
686-
enable_logging = bool(telemetry_enabled())
666+
enable_logging = bool(self._telemetry_enabled())
687667

688668
custom_instrumentor = self._tmpl_attrs.get("instrumentor_builder")
689669

690-
if custom_instrumentor and tracing_enabled():
670+
if custom_instrumentor and self._tracing_enabled():
691671
self._tmpl_attrs["instrumentor"] = custom_instrumentor(project)
692672

693673
if not custom_instrumentor:
694674
self._tmpl_attrs["instrumentor"] = _default_instrumentor_builder(
695675
project,
696-
enable_tracing=tracing_enabled(),
676+
enable_tracing=self._tracing_enabled(),
697677
enable_logging=enable_logging,
698678
)
699679

@@ -847,9 +827,13 @@ async def async_stream_query(
847827
**kwargs,
848828
)
849829

850-
async for event in events_async:
851-
# Yield the event data as a dictionary
852-
yield _utils.dump_event_for_json(event)
830+
try:
831+
async for event in events_async:
832+
# Yield the event data as a dictionary
833+
yield _utils.dump_event_for_json(event)
834+
finally:
835+
if self._tracing_enabled():
836+
_force_flush_traces()
853837

854838
async def streaming_agent_run_with_events(self, request_json: str):
855839
"""Streams responses asynchronously from the ADK application.
@@ -920,6 +904,8 @@ async def streaming_agent_run_with_events(self, request_json: str):
920904
user_id=request.user_id,
921905
session_id=session.id,
922906
)
907+
if self._tracing_enabled():
908+
_force_flush_traces()
923909

924910
async def async_get_session(
925911
self,
@@ -1105,3 +1091,42 @@ def register_operations(self) -> Dict[str, List[str]]:
11051091
"streaming_agent_run_with_events",
11061092
],
11071093
}
1094+
1095+
def _telemetry_enabled(self) -> Optional[bool]:
1096+
import os
1097+
1098+
GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY = (
1099+
"GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY"
1100+
)
1101+
1102+
return (
1103+
os.getenv(GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY, "0").lower()
1104+
in ("true", "1")
1105+
if GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY in os.environ
1106+
else None
1107+
)
1108+
1109+
# Tracing enablement follows truth table:
1110+
def _tracing_enabled(self) -> bool:
1111+
"""Tracing enablement follows true table:
1112+
1113+
| enable_tracing | enable_telemetry(env) | tracing_actually_enabled |
1114+
|----------------|-----------------------|--------------------------|
1115+
| false | false | false |
1116+
| false | true | false |
1117+
| false | None | false |
1118+
| true | false | false |
1119+
| true | true | true |
1120+
| true | None | true |
1121+
| None(default) | false | false |
1122+
| None(default) | true | adk_version >= 1.17 |
1123+
| None(default) | None | false |
1124+
"""
1125+
enable_tracing: Optional[bool] = self._tmpl_attrs.get("enable_tracing")
1126+
enable_telemetry: Optional[bool] = self._telemetry_enabled()
1127+
1128+
return (enable_tracing is True and enable_telemetry is not False) or (
1129+
enable_tracing is None
1130+
and enable_telemetry is True
1131+
and is_version_sufficient("1.17.0")
1132+
)

0 commit comments

Comments
 (0)