Skip to content

Commit ec18401

Browse files
committed
Merge branch 'master' into sentry-sdk-2.0
2 parents ddb4a29 + c8e9172 commit ec18401

16 files changed

Lines changed: 706 additions & 74 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
## 1.40.0
4+
5+
### Various fixes & improvements
6+
7+
- Enable metrics related settings by default (#2685) by @iambriccardo
8+
- Fix `UnicodeDecodeError` on Python 2 (#2657) by @sentrivana
9+
- Enable DB query source by default (#2629) by @sentrivana
10+
- Fix query source duration check (#2675) by @sentrivana
11+
- Reformat with `black==24.1.0` (#2680) by @sentrivana
12+
- Cleaning up existing code to prepare for new Scopes API (#2611) by @antonpirker
13+
- Moved redis related tests to databases (#2674) by @antonpirker
14+
- Improve `sentry_sdk.trace` type hints (#2633) by @szokeasaurusrex
15+
- Bump `checkouts/data-schemas` from `e9f7d58` to `aa7058c` (#2639) by @dependabot
16+
317
## 1.39.2
418

519
### Various fixes & improvements

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
copyright = "2019-{}, Sentry Team and Contributors".format(datetime.now().year)
2929
author = "Sentry Team and Contributors"
3030

31-
release = "1.39.2"
31+
release = "1.40.0"
3232
version = ".".join(release.split(".")[:2]) # The short X.Y version.
3333

3434

sentry_sdk/client.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
get_default_release,
1616
handle_in_app,
1717
logger,
18+
is_gevent,
1819
)
1920
from sentry_sdk.serializer import serialize
2021
from sentry_sdk.tracing import trace
@@ -229,15 +230,19 @@ def _capture_envelope(envelope):
229230

230231
self.metrics_aggregator = None # type: Optional[MetricsAggregator]
231232
experiments = self.options.get("_experiments", {})
232-
if experiments.get("enable_metrics"):
233-
from sentry_sdk.metrics import MetricsAggregator
234-
235-
self.metrics_aggregator = MetricsAggregator(
236-
capture_func=_capture_envelope,
237-
enable_code_locations=bool(
238-
experiments.get("metric_code_locations")
239-
),
240-
)
233+
if experiments.get("enable_metrics", True):
234+
if is_gevent():
235+
logger.warning("Metrics currently not supported with gevent.")
236+
237+
else:
238+
from sentry_sdk.metrics import MetricsAggregator
239+
240+
self.metrics_aggregator = MetricsAggregator(
241+
capture_func=_capture_envelope,
242+
enable_code_locations=bool(
243+
experiments.get("metric_code_locations", True)
244+
),
245+
)
241246

242247
max_request_body_size = ("always", "never", "small", "medium")
243248
if self.options["max_request_body_size"] not in max_request_body_size:

sentry_sdk/consts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def __init__(
303303
max_value_length=DEFAULT_MAX_VALUE_LENGTH, # type: int
304304
enable_backpressure_handling=True, # type: bool
305305
error_sampler=None, # type: Optional[Callable[[Event, Hint], Union[float, bool]]]
306-
enable_db_query_source=False, # type: bool
306+
enable_db_query_source=True, # type: bool
307307
db_query_source_threshold_ms=100, # type: int
308308
spotlight=None, # type: Optional[Union[bool, str]]
309309
):
@@ -329,4 +329,4 @@ def _get_default_options():
329329
del _get_default_options
330330

331331

332-
VERSION = "1.39.2"
332+
VERSION = "1.40.0"

sentry_sdk/integrations/gcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def event_processor(event, hint):
157157
final_time = datetime.now(timezone.utc)
158158
time_diff = final_time - initial_time
159159

160-
execution_duration_in_millis = time_diff.microseconds / MILLIS_TO_SECONDS
160+
execution_duration_in_millis = time_diff / timedelta(milliseconds=1)
161161

162162
extra = event.setdefault("extra", {})
163163
extra["google cloud functions"] = {

sentry_sdk/metrics.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,11 @@ def _get_aggregator_and_update_tags(key, tags):
718718
if transaction_name:
719719
updated_tags.setdefault("transaction", transaction_name)
720720
if scope._span is not None:
721-
sample_rate = experiments.get("metrics_summary_sample_rate") or 0.0
721+
sample_rate = experiments.get("metrics_summary_sample_rate")
722+
# We default the sample rate of metrics summaries to 1.0 only when the sample rate is `None` since we
723+
# want to honor the user's decision if they pass a valid float.
724+
if sample_rate is None:
725+
sample_rate = 1.0
722726
should_summarize_metric_callback = experiments.get(
723727
"should_summarize_metric"
724728
)

sentry_sdk/tracing_utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import sys
66
from collections.abc import Mapping
7+
from datetime import timedelta
78
from functools import wraps
89
from urllib.parse import quote, unquote
910

@@ -176,13 +177,13 @@ def add_query_source(hub, span):
176177
if span.timestamp is None or span.start_timestamp is None:
177178
return
178179

179-
should_add_query_source = client.options.get("enable_db_query_source", False)
180+
should_add_query_source = client.options.get("enable_db_query_source", True)
180181
if not should_add_query_source:
181182
return
182183

183184
duration = span.timestamp - span.start_timestamp
184185
threshold = client.options.get("db_query_source_threshold_ms", 0)
185-
slow_query = duration.microseconds > threshold * 1000
186+
slow_query = duration / timedelta(milliseconds=1) > threshold
186187

187188
if not slow_query:
188189
return

sentry_sdk/utils.py

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,13 @@ def __init__(self, value, metadata):
345345
self.value = value
346346
self.metadata = metadata
347347

348+
def __eq__(self, other):
349+
# type: (Any) -> bool
350+
if not isinstance(other, AnnotatedValue):
351+
return False
352+
353+
return self.value == other.value and self.metadata == other.metadata
354+
348355
@classmethod
349356
def removed_because_raw_data(cls):
350357
# type: () -> AnnotatedValue
@@ -1052,6 +1059,24 @@ def _is_in_project_root(abs_path, project_root):
10521059
return False
10531060

10541061

1062+
def _truncate_by_bytes(string, max_bytes):
1063+
# type: (str, int) -> str
1064+
"""
1065+
Truncate a UTF-8-encodable string to the last full codepoint so that it fits in max_bytes.
1066+
"""
1067+
truncated = string.encode("utf-8")[: max_bytes - 3].decode("utf-8", errors="ignore")
1068+
1069+
return truncated + "..."
1070+
1071+
1072+
def _get_size_in_bytes(value):
1073+
# type: (str) -> Optional[int]
1074+
try:
1075+
return len(value.encode("utf-8"))
1076+
except (UnicodeEncodeError, UnicodeDecodeError):
1077+
return None
1078+
1079+
10551080
def strip_string(value, max_length=None):
10561081
# type: (str, Optional[int]) -> Union[AnnotatedValue, str]
10571082
if not value:
@@ -1060,17 +1085,25 @@ def strip_string(value, max_length=None):
10601085
if max_length is None:
10611086
max_length = DEFAULT_MAX_VALUE_LENGTH
10621087

1063-
length = len(value.encode("utf-8"))
1088+
byte_size = _get_size_in_bytes(value)
1089+
text_size = len(value)
10641090

1065-
if length > max_length:
1066-
return AnnotatedValue(
1067-
value=value[: max_length - 3] + "...",
1068-
metadata={
1069-
"len": length,
1070-
"rem": [["!limit", "x", max_length - 3, max_length]],
1071-
},
1072-
)
1073-
return value
1091+
if byte_size is not None and byte_size > max_length:
1092+
# truncate to max_length bytes, preserving code points
1093+
truncated_value = _truncate_by_bytes(value, max_length)
1094+
elif text_size is not None and text_size > max_length:
1095+
# fallback to truncating by string length
1096+
truncated_value = value[: max_length - 3] + "..."
1097+
else:
1098+
return value
1099+
1100+
return AnnotatedValue(
1101+
value=truncated_value,
1102+
metadata={
1103+
"len": byte_size or text_size,
1104+
"rem": [["!limit", "x", max_length - 3, max_length]],
1105+
},
1106+
)
10741107

10751108

10761109
def parse_version(version):
@@ -1616,3 +1649,18 @@ def nanosecond_time():
16161649
def now():
16171650
# type: () -> float
16181651
return time.perf_counter()
1652+
1653+
1654+
try:
1655+
from gevent.monkey import is_module_patched
1656+
except ImportError:
1657+
1658+
def is_module_patched(*args, **kwargs):
1659+
# type: (*Any, **Any) -> bool
1660+
# unable to import from gevent means no modules have been patched
1661+
return False
1662+
1663+
1664+
def is_gevent():
1665+
# type: () -> bool
1666+
return is_module_patched("threading") or is_module_patched("_thread")

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_file_text(file_name):
2121

2222
setup(
2323
name="sentry-sdk",
24-
version="1.39.2",
24+
version="1.40.0",
2525
author="Sentry Team and Contributors",
2626
author_email="hello@sentry.io",
2727
url="https://github.com/getsentry/sentry-python",

0 commit comments

Comments
 (0)