Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 4 additions & 2 deletions opentelemetry-exporter-gcp-logging/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

## Unreleased

Added support for when `bytes` or `list['bytes']` is in `LogRecord.body` and
body is of type Mapping. Update opentelemetry-api/sdk dependencies to 1.3.
- Added support for when a `Mapping[str, bytes]` or `Mapping[str, List[bytes]]` is in `LogRecord.body`.
- Added support for when a `Mapping[str, List[Mapping]]` is in `LogRecord.body`.
- Do not call `logging.warning` when `LogRecord.body` is of None type, instead leave `LogEntry.payload` empty.
- Update opentelemetry-api/sdk dependencies to 1.3.

## Version 1.9.0a0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,19 @@ def _sanitized_body(
# Should not be possible for a non-bytes value to be present. AnyValue requires Sequence be of one type, and above
# we verified the first value is type bytes.
new_body[key] = [
base64.b64encode(v).decode()
base64.b64encode(v).decode() if isinstance(v, bytes) else v
for v in value
if isinstance(v, bytes)
]
elif (
isinstance(value, Sequence)
and len(value) > 0
and isinstance(value[0], Mapping)
):
# Should not be possible for a non-mapping value to be present. AnyValue requires Sequence be of one type, and above
# we verified the first value is type mapping.
new_body[key] = [
_sanitized_body(x) if isinstance(x, Mapping) else x
for x in value
]
elif isinstance(value, bytes):
new_body[key] = base64.b64encode(value).decode()
Expand All @@ -145,8 +155,15 @@ def _sanitized_body(
def _set_payload_in_log_entry(log_entry: LogEntry, body: AnyValue):
struct = Struct()
if isinstance(body, Mapping):
struct.update(_sanitized_body(body))
log_entry.json_payload = struct
sanitized = _sanitized_body(body)
try:
struct.update(sanitized)
log_entry.json_payload = struct
except Exception as exc: # pylint: disable=broad-except
logging.exception(
"Error mapping LogRecord.body to Struct, will write log with empty body: %s",
exc,
)
elif isinstance(body, bytes):
json_str = body.decode("utf-8", errors="replace")
json_dict = json.loads(json_str)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[
{
"entries": [
{
"jsonPayload": {
"my_dict": [
true,
false,
false,
true
]
},
"logName": "projects/fakeproject/logs/test",
"resource": {
"labels": {
"location": "global",
"namespace": "",
"node_id": ""
},
"type": "generic_node"
},
"timestamp": "2025-01-15T21:25:10.997977393Z"
}
],
"partialSuccess": true
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"entries": [
{
"jsonPayload": {
"my_dict": [
{
"key": "Ynl0ZXM="
}
]
},
"logName": "projects/fakeproject/logs/test",
"resource": {
"labels": {
"location": "global",
"namespace": "",
"node_id": ""
},
"type": "generic_node"
},
"timestamp": "2025-01-15T21:25:10.997977393Z"
}
],
"partialSuccess": true
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[
{
"entries": [
{
"jsonPayload": {
"my_dict": [
true,
"str",
1.0,
0.234
]
},
"logName": "projects/fakeproject/logs/test",
"resource": {
"labels": {
"location": "global",
"namespace": "",
"node_id": ""
},
"type": "generic_node"
},
"timestamp": "2025-01-15T21:25:10.997977393Z"
}
],
"partialSuccess": true
}
]
13 changes: 11 additions & 2 deletions opentelemetry-exporter-gcp-logging/tests/test_cloud_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
Be sure to review the changes.
"""
import re
from typing import List, Union
from typing import List, Mapping, Union

import pytest
from fixtures.cloud_logging_fake import CloudLoggingFake, WriteLogEntriesCall
Expand Down Expand Up @@ -159,12 +159,21 @@ def test_convert_non_json_dict_bytes(
pytest.param("A text body", id="str"),
pytest.param(True, id="bool"),
pytest.param(None, id="None"),
pytest.param(
{"my_dict": [{"key": b"bytes"}]}, id="list_of_dicts_with_bytes"
),
pytest.param(
{"my_dict": [True, False, False, True]}, id="list_of_bools"
),
pytest.param(
Comment thread
DylanRussell marked this conversation as resolved.
{"my_dict": [True, "str", 1, 0.234]}, id="list_of_mixed_sequence"
),
],
)
def test_convert_various_types_of_bodies(
cloudloggingfake: CloudLoggingFake,
snapshot_writelogentrycalls: List[WriteLogEntriesCall],
body: Union[str, bool, None],
body: Union[str, bool, None, Mapping],
) -> None:
log_data = [
LogData(
Expand Down