Skip to content

Commit 7f01372

Browse files
authored
chore(ourlogs): Use new transport (#4317)
We've added a more efficient transport for logs handling, use that. Solves LOGS-60
1 parent 970a350 commit 7f01372

File tree

3 files changed

+73
-58
lines changed

3 files changed

+73
-58
lines changed

sentry_sdk/_log_batcher.py

+47-28
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Optional, List, Callable, TYPE_CHECKING, Any
66

77
from sentry_sdk.utils import format_timestamp, safe_repr
8-
from sentry_sdk.envelope import Envelope
8+
from sentry_sdk.envelope import Envelope, Item, PayloadRef
99

1010
if TYPE_CHECKING:
1111
from sentry_sdk._types import Log
@@ -97,34 +97,36 @@ def flush(self):
9797
self._flush()
9898

9999
@staticmethod
100-
def _log_to_otel(log):
100+
def _log_to_transport_format(log):
101101
# type: (Log) -> Any
102-
def format_attribute(key, val):
103-
# type: (str, int | float | str | bool) -> Any
102+
def format_attribute(val):
103+
# type: (int | float | str | bool) -> Any
104104
if isinstance(val, bool):
105-
return {"key": key, "value": {"boolValue": val}}
105+
return {"value": val, "type": "boolean"}
106106
if isinstance(val, int):
107-
return {"key": key, "value": {"intValue": str(val)}}
107+
return {"value": val, "type": "integer"}
108108
if isinstance(val, float):
109-
return {"key": key, "value": {"doubleValue": val}}
109+
return {"value": val, "type": "double"}
110110
if isinstance(val, str):
111-
return {"key": key, "value": {"stringValue": val}}
112-
return {"key": key, "value": {"stringValue": safe_repr(val)}}
113-
114-
otel_log = {
115-
"severityText": log["severity_text"],
116-
"severityNumber": log["severity_number"],
117-
"body": {"stringValue": log["body"]},
118-
"timeUnixNano": str(log["time_unix_nano"]),
119-
"attributes": [
120-
format_attribute(k, v) for (k, v) in log["attributes"].items()
121-
],
111+
return {"value": val, "type": "string"}
112+
return {"value": safe_repr(val), "type": "string"}
113+
114+
if "sentry.severity_number" not in log["attributes"]:
115+
log["attributes"]["sentry.severity_number"] = log["severity_number"]
116+
if "sentry.severity_text" not in log["attributes"]:
117+
log["attributes"]["sentry.severity_text"] = log["severity_text"]
118+
119+
res = {
120+
"timestamp": int(log["time_unix_nano"]) / 1.0e9,
121+
"trace_id": log.get("trace_id", "00000000-0000-0000-0000-000000000000"),
122+
"level": str(log["severity_text"]),
123+
"body": str(log["body"]),
124+
"attributes": {
125+
k: format_attribute(v) for (k, v) in log["attributes"].items()
126+
},
122127
}
123128

124-
if "trace_id" in log:
125-
otel_log["traceId"] = log["trace_id"]
126-
127-
return otel_log
129+
return res
128130

129131
def _flush(self):
130132
# type: (...) -> Optional[Envelope]
@@ -133,10 +135,27 @@ def _flush(self):
133135
headers={"sent_at": format_timestamp(datetime.now(timezone.utc))}
134136
)
135137
with self._lock:
136-
for log in self._log_buffer:
137-
envelope.add_log(self._log_to_otel(log))
138+
if len(self._log_buffer) == 0:
139+
return None
140+
141+
envelope.add_item(
142+
Item(
143+
type="log",
144+
content_type="application/vnd.sentry.items.log+json",
145+
headers={
146+
"item_count": len(self._log_buffer),
147+
},
148+
payload=PayloadRef(
149+
json={
150+
"items": [
151+
self._log_to_transport_format(log)
152+
for log in self._log_buffer
153+
]
154+
}
155+
),
156+
)
157+
)
138158
self._log_buffer.clear()
139-
if envelope.items:
140-
self._capture_func(envelope)
141-
return envelope
142-
return None
159+
160+
self._capture_func(envelope)
161+
return envelope

sentry_sdk/envelope.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,6 @@ def add_sessions(
106106
# type: (...) -> None
107107
self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions"))
108108

109-
def add_log(
110-
self, log # type: Any
111-
):
112-
# type: (...) -> None
113-
self.add_item(Item(payload=PayloadRef(json=log), type="otel_log"))
114-
115109
def add_item(
116110
self, item # type: Item
117111
):
@@ -278,7 +272,7 @@ def data_category(self):
278272
return "transaction"
279273
elif ty == "event":
280274
return "error"
281-
elif ty == "otel_log":
275+
elif ty == "log":
282276
return "log"
283277
elif ty == "client_report":
284278
return "internal"

tests/test_logs.py

+25-23
Original file line numberDiff line numberDiff line change
@@ -19,42 +19,44 @@
1919

2020

2121
def otel_attributes_to_dict(otel_attrs):
22-
# type: (List[Mapping[str, Any]]) -> Mapping[str, Any]
22+
# type: (Mapping[str, Any]) -> Mapping[str, Any]
2323
def _convert_attr(attr):
2424
# type: (Mapping[str, Union[str, float, bool]]) -> Any
25-
if "boolValue" in attr:
26-
return bool(attr["boolValue"])
27-
if "doubleValue" in attr:
28-
return float(attr["doubleValue"])
29-
if "intValue" in attr:
30-
return int(attr["intValue"])
31-
if attr["stringValue"].startswith("{"):
25+
if attr["type"] == "boolean":
26+
return attr["value"]
27+
if attr["type"] == "double":
28+
return attr["value"]
29+
if attr["type"] == "integer":
30+
return attr["value"]
31+
if attr["value"].startswith("{"):
3232
try:
3333
return json.loads(attr["stringValue"])
3434
except ValueError:
3535
pass
36-
return str(attr["stringValue"])
36+
return str(attr["value"])
3737

38-
return {item["key"]: _convert_attr(item["value"]) for item in otel_attrs}
38+
return {k: _convert_attr(v) for (k, v) in otel_attrs.items()}
3939

4040

4141
def envelopes_to_logs(envelopes: List[Envelope]) -> List[Log]:
4242
res = [] # type: List[Log]
4343
for envelope in envelopes:
4444
for item in envelope.items:
45-
if item.type == "otel_log":
46-
log_json = item.payload.json
47-
log = {
48-
"severity_text": log_json["severityText"],
49-
"severity_number": log_json["severityNumber"],
50-
"body": log_json["body"]["stringValue"],
51-
"attributes": otel_attributes_to_dict(log_json["attributes"]),
52-
"time_unix_nano": int(log_json["timeUnixNano"]),
53-
"trace_id": None,
54-
} # type: Log
55-
if "traceId" in log_json:
56-
log["trace_id"] = log_json["traceId"]
57-
res.append(log)
45+
if item.type == "log":
46+
for log_json in item.payload.json["items"]:
47+
log = {
48+
"severity_text": log_json["attributes"]["sentry.severity_text"][
49+
"value"
50+
],
51+
"severity_number": int(
52+
log_json["attributes"]["sentry.severity_number"]["value"]
53+
),
54+
"body": log_json["body"],
55+
"attributes": otel_attributes_to_dict(log_json["attributes"]),
56+
"time_unix_nano": int(float(log_json["timestamp"]) * 1e9),
57+
"trace_id": log_json["trace_id"],
58+
} # type: Log
59+
res.append(log)
5860
return res
5961

6062

0 commit comments

Comments
 (0)