Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import json
import logging
import re
import urllib.parse
from base64 import b64encode
from typing import Any, Mapping, MutableMapping, Optional, Sequence

import google.auth
Expand Down Expand Up @@ -106,15 +106,27 @@
INVALID_LOG_NAME_MESSAGE = "%s is not a valid log name. log name must be <512 characters and only contain characters: A-Za-z0-9/-_."


def _convert_any_value_to_string(value: Any) -> str:
class _GenAiJsonEncoder(json.JSONEncoder):
def default(self, o: Any) -> Any:
if isinstance(o, bytes):
return b64encode(o).decode()
return super().default(o)


def _convert_any_value_to_string(value: Any, debug_location: str) -> str:
if isinstance(value, bool):
return "true" if value else "false"
if isinstance(value, bytes):
return base64.b64encode(value).decode()
if isinstance(value, (int, float, str)):
return str(value)
if isinstance(value, (list, tuple)):
return json.dumps(value)
if isinstance(value, (list, tuple, Mapping)):
return json.dumps(value, separators=(",", ":"), cls=_GenAiJsonEncoder)
logging.warning(
"Unexpected type %s found in %s, this field will not be added to the LogEntry.",
type(value),
debug_location,
)
return ""


Expand Down Expand Up @@ -176,7 +188,9 @@ def _set_payload_in_log_entry(log_entry: LogEntry, body: AnyValue):
else:
log_entry.text_payload = base64.b64encode(body).decode()
elif body is not None:
log_entry.text_payload = _convert_any_value_to_string(body)
log_entry.text_payload = _convert_any_value_to_string(
body, "LogRecord.body"
)


def is_log_id_valid(log_id: str) -> bool:
Expand Down Expand Up @@ -264,7 +278,9 @@ def export(self, batch: Sequence[LogData]):
log_record.severity_number.value # type: ignore[index]
]
log_entry.labels = {
k: _convert_any_value_to_string(v)
k: _convert_any_value_to_string(
v, "LogRecord.Attribute {}".format(k)
)
for k, v in attributes.items()
}
if log_record.event_name:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
"Ref": 164611595.0
},
"labels": {
"boolArray": "[true, false, true, true]",
"boolArray": "[true,false,true,true]",
"float": "25.43231",
"int": "25",
"intArray": "[21, 18, 23, 17]"
"intArray": "[21,18,23,17]"
},
"logName": "projects/fakeproject/logs/test",
"resource": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"entries": [
{
"labels": {
"a": "[{\"key\":\"Ynl0ZXM=\"}]",
"b": "[true,false,false,true]",
"c": "{\"a_dict\":\"abcd\",\"akey\":1234}",
"d": "{\"gen_ai.input.messages\":[{\"role\":\"user\",\"parts\":[{\"type\":\"text\",\"content\":\"Get weather details in New Delhi and San Francisco?\"}]},{\"role\":\"model\",\"parts\":[{\"type\":\"tool_call\",\"arguments\":{\"location\":\"New Delhi\"},\"name\":\"get_current_weather\",\"id\":\"get_current_weather_0\"},{\"type\":\"tool_call\",\"arguments\":{\"location\":\"San Francisco\"},\"name\":\"get_current_weather\",\"id\":\"get_current_weather_1\"}]},{\"role\":\"user\",\"parts\":[{\"type\":\"tool_call_response\",\"response\":{\"content\":\"{\\\"temperature\\\": 35, \\\"unit\\\": \\\"C\\\"}\"},\"id\":\"get_current_weather_0\"},{\"type\":\"tool_call_response\",\"response\":{\"content\":\"{\\\"temperature\\\": 25, \\\"unit\\\": \\\"C\\\"}\"},\"id\":\"get_current_weather_1\"}]}],\"gen_ai.system_instructions\":[{\"type\":\"text\",\"content\":\"You are a clever language model\"}],\"gen_ai.output.messages\":[{\"role\":\"model\",\"parts\":[{\"type\":\"text\",\"content\":\"The current temperature in New Delhi is 35\\u00b0C, and in San Francisco, it is 25\\u00b0C.\"}],\"finish_reason\":\"stop\"}]}"
},
"logName": "projects/fakeproject/logs/test",
"resource": {
"labels": {
"location": "global",
"namespace": "",
"node_id": ""
},
"type": "generic_node"
},
"timestamp": "2025-01-15T21:25:10.997977393Z"
}
],
"partialSuccess": true
}
]
158 changes: 91 additions & 67 deletions opentelemetry-exporter-gcp-logging/tests/test_cloud_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,74 @@

PROJECT_ID = "fakeproject"

GEN_AI_DICT = {
"gen_ai.input.messages": (
{
"role": "user",
"parts": (
{
"type": "text",
"content": "Get weather details in New Delhi and San Francisco?",
},
),
},
{
"role": "model",
"parts": (
{
"type": "tool_call",
"arguments": {"location": "New Delhi"},
"name": "get_current_weather",
"id": "get_current_weather_0",
},
{
"type": "tool_call",
"arguments": {"location": "San Francisco"},
"name": "get_current_weather",
"id": "get_current_weather_1",
},
),
},
{
"role": "user",
"parts": (
{
"type": "tool_call_response",
"response": {
"content": '{"temperature": 35, "unit": "C"}'
},
"id": "get_current_weather_0",
},
{
"type": "tool_call_response",
"response": {
"content": '{"temperature": 25, "unit": "C"}'
},
"id": "get_current_weather_1",
},
),
},
),
"gen_ai.system_instructions": (
{
"type": "text",
"content": "You are a clever language model",
},
),
"gen_ai.output.messages": (
{
"role": "model",
"parts": (
{
"type": "text",
"content": "The current temperature in New Delhi is 35°C, and in San Francisco, it is 25°C.",
},
),
"finish_reason": "stop",
},
),
}


def test_too_large_log_raises_warning(caplog) -> None:
client = LoggingServiceV2Client(credentials=AnonymousCredentials())
Expand Down Expand Up @@ -165,73 +233,7 @@ def test_convert_gen_ai_body(
log_record=LogRecord(
event_name="gen_ai.client.inference.operation.details",
timestamp=1736976310997977393,
body={
"gen_ai.input.messages": (
{
"role": "user",
"parts": (
{
"type": "text",
"content": "Get weather details in New Delhi and San Francisco?",
},
),
},
{
"role": "model",
"parts": (
{
"type": "tool_call",
"arguments": {"location": "New Delhi"},
"name": "get_current_weather",
"id": "get_current_weather_0",
},
{
"type": "tool_call",
"arguments": {"location": "San Francisco"},
"name": "get_current_weather",
"id": "get_current_weather_1",
},
),
},
{
"role": "user",
"parts": (
{
"type": "tool_call_response",
"response": {
"content": '{"temperature": 35, "unit": "C"}'
},
"id": "get_current_weather_0",
},
{
"type": "tool_call_response",
"response": {
"content": '{"temperature": 25, "unit": "C"}'
},
"id": "get_current_weather_1",
},
),
},
),
"gen_ai.system_instructions": (
{
"type": "text",
"content": "You are a clever language model",
},
),
"gen_ai.output.messages": (
{
"role": "model",
"parts": (
{
"type": "text",
"content": "The current temperature in New Delhi is 35°C, and in San Francisco, it is 25°C.",
},
),
"finish_reason": "stop",
},
),
},
body=GEN_AI_DICT,
),
instrumentation_scope=InstrumentationScope("test"),
)
Expand Down Expand Up @@ -303,3 +305,25 @@ def test_convert_various_types_of_bodies(
]
cloudloggingfake.exporter.export(log_data)
assert cloudloggingfake.get_calls() == snapshot_writelogentrycalls


def test_convert_various_types_of_attributes(
cloudloggingfake: CloudLoggingFake,
snapshot_writelogentrycalls: List[WriteLogEntriesCall],
) -> None:
log_data = [
LogData(
log_record=LogRecord(
attributes={
"a": [{"key": b"bytes"}],
"b": [True, False, False, True],
"c": {"a_dict": "abcd", "akey": 1234},
"d": GEN_AI_DICT,
},
timestamp=1736976310997977393,
),
instrumentation_scope=InstrumentationScope("test"),
)
]
cloudloggingfake.exporter.export(log_data)
assert cloudloggingfake.get_calls() == snapshot_writelogentrycalls