Skip to content

Commit a6a1be3

Browse files
authored
feat(transport): Client Report Support (#1181)
This adds support for client reports to the python SDK. This will cause the SDK to send a report once every 30 seconds or once a minute. After 30 seconds it will attempt to attach the report to a scheduled envelope if there is one, after 60 seconds it will send it as a separate envelope. Attempts of sending are only made as a byproduct of attempted event / envelope sending or an explicit flush.
1 parent 7b48589 commit a6a1be3

File tree

9 files changed

+360
-52
lines changed

9 files changed

+360
-52
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"python.pythonPath": ".venv/bin/python"
2+
"python.pythonPath": ".venv/bin/python",
3+
"python.formatting.provider": "black"
34
}

scripts/init_serverless_sdk.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,23 @@ def extract_and_load_lambda_function_module(self, module_path):
5151
# Supported python versions are 2.7, 3.6, 3.7, 3.8
5252
if py_version >= (3, 5):
5353
import importlib.util
54-
spec = importlib.util.spec_from_file_location(module_name, module_file_path)
54+
55+
spec = importlib.util.spec_from_file_location(
56+
module_name, module_file_path
57+
)
5558
self.lambda_function_module = importlib.util.module_from_spec(spec)
5659
spec.loader.exec_module(self.lambda_function_module)
5760
elif py_version[0] < 3:
5861
import imp
59-
self.lambda_function_module = imp.load_source(module_name, module_file_path)
62+
63+
self.lambda_function_module = imp.load_source(
64+
module_name, module_file_path
65+
)
6066
else:
6167
raise ValueError("Python version %s is not supported." % py_version)
6268
else:
6369
import importlib
70+
6471
self.lambda_function_module = importlib.import_module(module_path)
6572

6673
def get_lambda_handler(self):

sentry_sdk/_types.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,14 @@
3737
NotImplementedType = Any
3838

3939
EventDataCategory = Literal[
40-
"default", "error", "crash", "transaction", "security", "attachment", "session"
40+
"default",
41+
"error",
42+
"crash",
43+
"transaction",
44+
"security",
45+
"attachment",
46+
"session",
47+
"internal",
4148
]
4249
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
4350
EndpointType = Literal["store", "envelope"]

sentry_sdk/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ def _should_capture(
243243
self.options["sample_rate"] < 1.0
244244
and random.random() >= self.options["sample_rate"]
245245
):
246+
# record a lost event if we did not sample this.
247+
if self.transport:
248+
self.transport.record_lost_event("sample_rate", data_category="error")
246249
return False
247250

248251
if self._is_ignored_error(event, hint):

sentry_sdk/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def __init__(
7575
traces_sampler=None, # type: Optional[TracesSampler]
7676
auto_enabling_integrations=True, # type: bool
7777
auto_session_tracking=True, # type: bool
78+
send_client_reports=True, # type: bool
7879
_experiments={}, # type: Experiments # noqa: B006
7980
):
8081
# type: (...) -> None

sentry_sdk/envelope.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import json
33
import mimetypes
44

5-
from sentry_sdk._compat import text_type
5+
from sentry_sdk._compat import text_type, PY2
66
from sentry_sdk._types import MYPY
77
from sentry_sdk.session import Session
88
from sentry_sdk.utils import json_dumps, capture_internal_exceptions
@@ -18,6 +18,14 @@
1818
from sentry_sdk._types import Event, EventDataCategory
1919

2020

21+
def parse_json(data):
22+
# type: (Union[bytes, text_type]) -> Any
23+
# on some python 3 versions this needs to be bytes
24+
if not PY2 and isinstance(data, bytes):
25+
data = data.decode("utf-8", "replace")
26+
return json.loads(data)
27+
28+
2129
class Envelope(object):
2230
def __init__(
2331
self,
@@ -114,7 +122,7 @@ def deserialize_from(
114122
cls, f # type: Any
115123
):
116124
# type: (...) -> Envelope
117-
headers = json.loads(f.readline())
125+
headers = parse_json(f.readline())
118126
items = []
119127
while 1:
120128
item = Item.deserialize_from(f)
@@ -236,6 +244,8 @@ def data_category(self):
236244
return "transaction"
237245
elif ty == "event":
238246
return "error"
247+
elif ty == "client_report":
248+
return "internal"
239249
else:
240250
return "default"
241251

@@ -284,11 +294,11 @@ def deserialize_from(
284294
line = f.readline().rstrip()
285295
if not line:
286296
return None
287-
headers = json.loads(line)
297+
headers = parse_json(line)
288298
length = headers["length"]
289299
payload = f.read(length)
290300
if headers.get("type") in ("event", "transaction"):
291-
rv = cls(headers=headers, payload=PayloadRef(json=json.loads(payload)))
301+
rv = cls(headers=headers, payload=PayloadRef(json=parse_json(payload)))
292302
else:
293303
rv = cls(headers=headers, payload=payload)
294304
f.readline()

sentry_sdk/tracing.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -507,13 +507,22 @@ def finish(self, hub=None):
507507
# This transaction is already finished, ignore.
508508
return None
509509

510+
hub = hub or self.hub or sentry_sdk.Hub.current
511+
client = hub.client
512+
510513
# This is a de facto proxy for checking if sampled = False
511514
if self._span_recorder is None:
512515
logger.debug("Discarding transaction because sampled = False")
513-
return None
514516

515-
hub = hub or self.hub or sentry_sdk.Hub.current
516-
client = hub.client
517+
# This is not entirely accurate because discards here are not
518+
# exclusively based on sample rate but also traces sampler, but
519+
# we handle this the same here.
520+
if client and client.transport:
521+
client.transport.record_lost_event(
522+
"sample_rate", data_category="transaction"
523+
)
524+
525+
return None
517526

518527
if client is None:
519528
# We have no client and therefore nowhere to send this transaction.

0 commit comments

Comments
 (0)