Skip to content

Commit 74f1389

Browse files
authored
Add temporary support for aiplatform.googleapis.com/ReasoningEngine monitored resource in logging exporter (#445)
* Add temporary support for `aiplatform.googleapis.com/ReasoningEngine` monitored resource in logging exporter * Try not sending the resource_container
1 parent c9ae87a commit 74f1389

File tree

4 files changed

+196
-9
lines changed

4 files changed

+196
-9
lines changed

opentelemetry-exporter-gcp-logging/src/opentelemetry/exporter/cloud_logging/__init__.py

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def _convert_any_value_to_string(value: Any) -> str:
147147

148148
# Be careful not to mutate original body. Make copies of anything that needs to change.
149149
def _sanitized_body(
150-
body: Mapping[str, AnyValue]
150+
body: Mapping[str, AnyValue],
151151
) -> MutableMapping[str, AnyValue]:
152152
new_body: MutableMapping[str, AnyValue] = {}
153153
for key, value in body.items():
@@ -212,6 +212,45 @@ def is_log_id_valid(log_id: str) -> bool:
212212
)
213213

214214

215+
def _get_monitored_resource(
216+
resource: Optional[Resource],
217+
) -> MonitoredResource | None:
218+
if not resource:
219+
return None
220+
221+
# TODO: Remove temporary special case for Vertex Agent Engine
222+
# https://github.com/GoogleCloudPlatform/opentelemetry-operations-python/issues/444
223+
cloud_resource_id = resource.attributes.get("cloud.resource_id")
224+
if isinstance(cloud_resource_id, str) and (
225+
match := re.match(
226+
r"//aiplatform\.googleapis\.com/projects/(?P<project_id>[^/]+)"
227+
r"/locations/(?P<location>[^/]+)"
228+
r"/reasoningEngines/(?P<agent_engine_id>[^/]+)",
229+
cloud_resource_id,
230+
)
231+
):
232+
location = match.group("location")
233+
agent_engine_id = match.group("agent_engine_id")
234+
# https://cloud.google.com/monitoring/api/resources#tag_aiplatform.googleapis.com/ReasoningEngine
235+
return MonitoredResource(
236+
type="aiplatform.googleapis.com/ReasoningEngine",
237+
labels={
238+
# Intentionally omit the project ID
239+
"location": location,
240+
"reasoning_engine_id": agent_engine_id,
241+
},
242+
)
243+
244+
monitored_resource_data = get_monitored_resource(resource)
245+
if not monitored_resource_data:
246+
return None
247+
248+
return MonitoredResource(
249+
type=monitored_resource_data.type,
250+
labels=monitored_resource_data.labels,
251+
)
252+
253+
215254
class CloudLoggingExporter(LogExporter):
216255
def __init__(
217256
self,
@@ -302,14 +341,10 @@ def export(self, batch: Sequence[LogData]):
302341
else:
303342
ts.FromDatetime(now)
304343
log_entry.timestamp = ts
305-
monitored_resource_data = get_monitored_resource(
306-
log_record.resource or Resource({})
307-
)
308-
if monitored_resource_data:
309-
log_entry.resource = MonitoredResource(
310-
type=monitored_resource_data.type,
311-
labels=monitored_resource_data.labels,
312-
)
344+
if monitored_resource := _get_monitored_resource(
345+
log_record.resource
346+
):
347+
log_entry.resource = monitored_resource
313348
log_entry.trace_sampled = (
314349
log_record.trace_flags is not None
315350
and log_record.trace_flags.sampled
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[
2+
{
3+
"entries": [
4+
{
5+
"logName": "projects/fakeproject/logs/test",
6+
"resource": {
7+
"labels": {
8+
"location": "europe-west3",
9+
"reasoning_engine_id": "8477639270431981568"
10+
},
11+
"type": "aiplatform.googleapis.com/ReasoningEngine"
12+
},
13+
"textPayload": "valid agent engine",
14+
"timestamp": "2025-01-15T21:25:10.997977393Z"
15+
},
16+
{
17+
"logName": "projects/fakeproject/logs/test",
18+
"resource": {
19+
"labels": {
20+
"location": "global",
21+
"namespace": "",
22+
"node_id": ""
23+
},
24+
"type": "generic_node"
25+
},
26+
"textPayload": "invalid 1",
27+
"timestamp": "2025-01-15T21:25:10.997977393Z"
28+
},
29+
{
30+
"logName": "projects/fakeproject/logs/test",
31+
"resource": {
32+
"labels": {
33+
"location": "global",
34+
"namespace": "",
35+
"node_id": ""
36+
},
37+
"type": "generic_node"
38+
},
39+
"textPayload": "invalid 2",
40+
"timestamp": "2025-01-15T21:25:10.997977393Z"
41+
},
42+
{
43+
"logName": "projects/fakeproject/logs/test",
44+
"resource": {
45+
"labels": {
46+
"location": "global",
47+
"namespace": "",
48+
"node_id": ""
49+
},
50+
"type": "generic_node"
51+
},
52+
"textPayload": "invalid 3",
53+
"timestamp": "2025-01-15T21:25:10.997977393Z"
54+
}
55+
],
56+
"partialSuccess": true
57+
}
58+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"logging.googleapis.com/labels": {},
4+
"logging.googleapis.com/spanId": "",
5+
"logging.googleapis.com/trace": "",
6+
"logging.googleapis.com/trace_sampled": false,
7+
"message": "valid agent engine",
8+
"severity": "DEFAULT",
9+
"time": "2025-01-15T21:25:10.997977393Z"
10+
},
11+
{
12+
"logging.googleapis.com/labels": {},
13+
"logging.googleapis.com/spanId": "",
14+
"logging.googleapis.com/trace": "",
15+
"logging.googleapis.com/trace_sampled": false,
16+
"message": "invalid 1",
17+
"severity": "DEFAULT",
18+
"time": "2025-01-15T21:25:10.997977393Z"
19+
},
20+
{
21+
"logging.googleapis.com/labels": {},
22+
"logging.googleapis.com/spanId": "",
23+
"logging.googleapis.com/trace": "",
24+
"logging.googleapis.com/trace_sampled": false,
25+
"message": "invalid 2",
26+
"severity": "DEFAULT",
27+
"time": "2025-01-15T21:25:10.997977393Z"
28+
},
29+
{
30+
"logging.googleapis.com/labels": {},
31+
"logging.googleapis.com/spanId": "",
32+
"logging.googleapis.com/trace": "",
33+
"logging.googleapis.com/trace_sampled": false,
34+
"message": "invalid 3",
35+
"severity": "DEFAULT",
36+
"time": "2025-01-15T21:25:10.997977393Z"
37+
}
38+
]

opentelemetry-exporter-gcp-logging/tests/test_cloud_logging.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,62 @@ def test_user_agent(cloudloggingfake: CloudLoggingFake) -> None:
165165
)
166166

167167

168+
def test_agent_engine_monitored_resources(
169+
export_and_assert_snapshot: ExportAndAssertSnapshot,
170+
) -> None:
171+
log_data = [
172+
LogData(
173+
log_record=LogRecord(
174+
body="valid agent engine",
175+
timestamp=1736976310997977393,
176+
resource=Resource(
177+
{
178+
"cloud.resource_id": "//aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines/8477639270431981568"
179+
}
180+
),
181+
),
182+
instrumentation_scope=InstrumentationScope("test"),
183+
),
184+
LogData(
185+
log_record=LogRecord(
186+
body="invalid 1",
187+
timestamp=1736976310997977393,
188+
resource=Resource(
189+
{
190+
"cloud.resource_id": "//aiplatform.googleapis.com/locations/europe-west3/reasoningEngines/8477639270431981568"
191+
}
192+
),
193+
),
194+
instrumentation_scope=InstrumentationScope("test"),
195+
),
196+
LogData(
197+
log_record=LogRecord(
198+
body="invalid 2",
199+
timestamp=1736976310997977393,
200+
resource=Resource(
201+
{
202+
"cloud.resource_id": "//aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines//8477639270431981568"
203+
}
204+
),
205+
),
206+
instrumentation_scope=InstrumentationScope("test"),
207+
),
208+
LogData(
209+
log_record=LogRecord(
210+
body="invalid 3",
211+
timestamp=1736976310997977393,
212+
resource=Resource(
213+
{
214+
"cloud.resource_id": "aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines//8477639270431981568"
215+
}
216+
),
217+
),
218+
instrumentation_scope=InstrumentationScope("test"),
219+
),
220+
]
221+
export_and_assert_snapshot(log_data)
222+
223+
168224
def test_convert_otlp_dict_body(
169225
export_and_assert_snapshot: ExportAndAssertSnapshot,
170226
) -> None:

0 commit comments

Comments
 (0)