Skip to content

Commit 4984c7f

Browse files
authored
Merge branch 'potel-base' into ivana/fix-db-op
2 parents 7f77b7b + 6d622b2 commit 4984c7f

File tree

25 files changed

+1206
-400
lines changed

25 files changed

+1206
-400
lines changed

CHANGELOG.md

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## 3.0.0a2
4+
5+
We're excited to announce that version 3.0 of the Sentry Python SDK is now
6+
available. This release is the result of a long-term effort to use OpenTelemetry
7+
under the hood for tracing. This switch opens the door for us to leverage the
8+
full power of OpenTelemetry, so stay tuned for more integrations and features
9+
in future releases.
10+
11+
Looking to upgrade from Sentry SDK 2.x to 3.x? See the
12+
[full list of changes](MIGRATION_GUIDE.md) for a comprehensive overview
13+
of what's changed. Looking for a more digestible summary? See the
14+
[migration guide in the docs](https://docs.sentry.io/platforms/python/migration/2.x-to-3.x)
15+
with the most common migration patterns.
16+
17+
⚠️ This is a pre-release. If you feel like taking it for a spin, we'd be grateful
18+
for your feedback. How was the migration? Is everything working as expected? Is
19+
*nothing* working as expected? Something in between? Please let us know
20+
[on GitHub](https://github.com/getsentry/sentry-python/discussions/3936) or
21+
[on Discord](https://discord.com/invite/Ww9hbqr).
22+
323
## 3.0.0a1
424

525
We're excited to announce that version 3.0 of the Sentry Python SDK is now
@@ -18,7 +38,46 @@ with the most common migration patterns.
1838
for your feedback. How was the migration? Is everything working as expected? Is
1939
*nothing* working as expected? Something in between? Please let us know
2040
[on GitHub](https://github.com/getsentry/sentry-python/discussions/3936) or
21-
[on Discord](https://discord.gg/wdNEHETs87).
41+
[on Discord](https://discord.com/invite/Ww9hbqr).
42+
43+
## 2.30.0
44+
45+
### Various fixes & improvements
46+
47+
- **New beta feature:** Sentry logs for Loguru (#4445) by @sentrivana
48+
49+
We can now capture Loguru logs and send them to Sentry.
50+
51+
```python
52+
import sentry_sdk
53+
from sentry_sdk.integrations.loguru import LoguruIntegration
54+
55+
# Setup Sentry SDK to send Loguru log messages with a level of "error" or higher to Sentry
56+
sentry_sdk.init(
57+
_experiments={
58+
"enable_logs": True,
59+
},
60+
integrations=[
61+
LoguruIntegration(sentry_logs_level=logging.ERROR),
62+
]
63+
)
64+
```
65+
66+
- fix(logs): Don't gate user behind `send_default_pii` (#4453) by @AbhiPrasad
67+
- fix(logging): Strip log `record.name` for more robust matching (#4411) by @romaingd-spi
68+
- Migrate to modern threading interface (#4452) by @emmanuel-ferdman
69+
- ref: Remove `_capture_experimental_log` `scope` parameter (#4424) by @szokeasaurusrex
70+
- feat(logs): Add user attributes to logs (#4423) by @szokeasaurusrex
71+
- fix: fix ARQ integration error (#4427) (#4428) by @ninoseki
72+
- fix(grpc): Fix AttributeError when instrumenting with OTel (#4405) by @sentrivana
73+
- fix(redis): Use `command_queue` instead of `command_stack` if available (#4404) by @sentrivana
74+
- fix: Handle invalid `SENTRY_DEBUG` values properly (#4400) by @szokeasaurusrex
75+
- Increase test coverage (#4393) by @mgaligniana
76+
- tests(logs): avoid failures when running with integrations enabled (#4388) by @rominf
77+
- Fix CI, adapt to new redis-py release (#4431) by @sentrivana
78+
- tests: Regenerate toxgen (#4403) by @sentrivana
79+
- tests: Regenerate tox.ini & fix CI (#4435) by @sentrivana
80+
- build(deps): bump codecov/codecov-action from 5.4.2 to 5.4.3 (#4397) by @dependabot
2281

2382
## 2.29.1
2483

@@ -142,7 +201,7 @@ for your feedback. How was the migration? Is everything working as expected? Is
142201
sentry_sdk.init(
143202
dsn="...",
144203
_experiments={
145-
"enable_sentry_logs": True
204+
"enable_logs": True
146205
}
147206
integrations=[
148207
LoggingIntegration(sentry_logs_level=logging.ERROR),

MIGRATION_GUIDE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
1616
- `sentry_sdk.start_transaction`/`sentry_sdk.start_span` no longer takes the following arguments: `span`, `parent_sampled`, `trace_id`, `span_id` or `parent_span_id`.
1717
- You can no longer change the sampled status of a span with `span.sampled = False` after starting it.
1818
- The `Span()` constructor does not accept a `hub` parameter anymore.
19+
- The `sentry_sdk.Scope()` constructor no longer accepts a `client` parameter.
1920
- `Span.finish()` does not accept a `hub` parameter anymore.
2021
- `Span.finish()` no longer returns the `event_id` if the event is sent to sentry.
2122
- Some integrations were creating spans with `op` `db`. This was changed to `db.query`.
@@ -33,6 +34,7 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
3334
- The `SentrySpanProcessor` and `SentryPropagator` are exported from `sentry_sdk.opentelemetry` instead of `sentry_sdk.integrations.opentelemetry`.
3435
- PyMongo breadcrumb type was changed from `db` to `query`.
3536
- The integration-specific content of the `sampling_context` argument of `traces_sampler` and `profiles_sampler` now looks different.
37+
3638
- The Celery integration doesn't add the `celery_job` dictionary anymore. Instead, the individual keys are now available as:
3739

3840
| Dictionary keys | Sampling context key | Example |
@@ -135,7 +137,6 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
135137
| `gcp_event.query_string` | `url.query` |
136138
| `gcp_event.headers` | `http.request.header.{header}` |
137139

138-
139140
### Removed
140141

141142
- Dropped support for Python 3.6.
@@ -170,7 +171,7 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
170171
- `span_id`
171172
- `parent_span_id`: you can supply a `parent_span` instead
172173
- The `Scope.transaction` property has been removed. To obtain the root span (previously transaction), use `Scope.root_span`. To set the root span's (transaction's) name, use `Scope.set_transaction_name()`.
173-
- The `Scope.span =` setter has been removed.
174+
- The `Scope.span =` setter has been removed. Please use the new `span.activate()` api instead if you want to activate a new span manually instead of using the `start_span` context manager.
174175
- Passing a list or `None` for `failed_request_status_codes` in the Starlette integration is no longer supported. Pass a set of integers instead.
175176
- The `span` argument of `Scope.trace_propagation_meta` is no longer supported.
176177
- Setting `Scope.user` directly is no longer supported. Use `Scope.set_user()` instead.

docs/conf.py

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

36-
release = "3.0.0a1"
36+
release = "3.0.0a2"
3737
version = ".".join(release.split(".")[:2]) # The short X.Y version.
3838

3939

scripts/populate_tox/tox.jinja

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ deps =
269269
# Redis
270270
redis: fakeredis!=1.7.4
271271
redis: pytest<8.0.0
272-
py3.7-redis: fakeredis!=2.26.0 # https://github.com/cunla/fakeredis-py/issues/341
272+
{py3.7,py3.8}-redis: fakeredis<2.26.0
273273
{py3.7,py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-redis: pytest-asyncio
274274
redis-v3: redis~=3.0
275275
redis-v4: redis~=4.0
@@ -297,6 +297,7 @@ deps =
297297
# Sanic
298298
sanic: websockets<11.0
299299
sanic: aiohttp
300+
{py3.7,py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-sanic: tracerite<1.1.2
300301
sanic-v{24.6}: sanic_testing
301302
sanic-latest: sanic_testing
302303
sanic-v0.8: sanic~=0.8.0

sentry_sdk/client.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from importlib import import_module
88
from typing import TYPE_CHECKING, List, Dict, cast, overload
99

10+
import sentry_sdk
1011
from sentry_sdk._compat import check_uwsgi_thread_support
1112
from sentry_sdk.utils import (
1213
AnnotatedValue,
@@ -190,8 +191,8 @@ def capture_event(self, *args, **kwargs):
190191
# type: (*Any, **Any) -> Optional[str]
191192
return None
192193

193-
def _capture_experimental_log(self, scope, log):
194-
# type: (Scope, Log) -> None
194+
def _capture_experimental_log(self, log):
195+
# type: (Log) -> None
195196
pass
196197

197198
def capture_session(self, *args, **kwargs):
@@ -846,12 +847,14 @@ def capture_event(
846847

847848
return return_value
848849

849-
def _capture_experimental_log(self, current_scope, log):
850-
# type: (Scope, Log) -> None
850+
def _capture_experimental_log(self, log):
851+
# type: (Log) -> None
851852
logs_enabled = self.options["_experiments"].get("enable_logs", False)
852853
if not logs_enabled:
853854
return
854-
isolation_scope = current_scope.get_isolation_scope()
855+
856+
current_scope = sentry_sdk.get_current_scope()
857+
isolation_scope = sentry_sdk.get_isolation_scope()
855858

856859
log["attributes"]["sentry.sdk.name"] = SDK_INFO["name"]
857860
log["attributes"]["sentry.sdk.version"] = SDK_INFO["version"]
@@ -880,6 +883,21 @@ def _capture_experimental_log(self, current_scope, log):
880883
elif propagation_context is not None:
881884
log["trace_id"] = propagation_context.trace_id
882885

886+
# The user, if present, is always set on the isolation scope.
887+
if isolation_scope._user is not None:
888+
for log_attribute, user_attribute in (
889+
("user.id", "id"),
890+
("user.name", "username"),
891+
("user.email", "email"),
892+
):
893+
if (
894+
user_attribute in isolation_scope._user
895+
and log_attribute not in log["attributes"]
896+
):
897+
log["attributes"][log_attribute] = isolation_scope._user[
898+
user_attribute
899+
]
900+
883901
# If debug is enabled, log the log to the console
884902
debug = self.options.get("debug", False)
885903
if debug:

sentry_sdk/consts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,4 +1063,4 @@ def _get_default_options():
10631063
del _get_default_options
10641064

10651065

1066-
VERSION = "3.0.0a1"
1066+
VERSION = "3.0.0a2"

sentry_sdk/integrations/logging.py

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import sentry_sdk
77
from sentry_sdk.client import BaseClient
8+
from sentry_sdk.logger import _log_level_to_otel
89
from sentry_sdk.utils import (
910
safe_repr,
1011
to_string,
@@ -14,7 +15,7 @@
1415
)
1516
from sentry_sdk.integrations import Integration
1617

17-
from typing import TYPE_CHECKING, Tuple
18+
from typing import TYPE_CHECKING
1819

1920
if TYPE_CHECKING:
2021
from collections.abc import MutableMapping
@@ -36,6 +37,16 @@
3637
logging.CRITICAL: "fatal", # CRITICAL is same as FATAL
3738
}
3839

40+
# Map logging level numbers to corresponding OTel level numbers
41+
SEVERITY_TO_OTEL_SEVERITY = {
42+
logging.CRITICAL: 21, # fatal
43+
logging.ERROR: 17, # error
44+
logging.WARNING: 13, # warn
45+
logging.INFO: 9, # info
46+
logging.DEBUG: 5, # debug
47+
}
48+
49+
3950
# Capturing events from those loggers causes recursion errors. We cannot allow
4051
# the user to unconditionally create events from those loggers under any
4152
# circumstances.
@@ -124,7 +135,10 @@ def sentry_patched_callhandlers(self, record):
124135
# the integration. Otherwise we have a high chance of getting
125136
# into a recursion error when the integration is resolved
126137
# (this also is slower).
127-
if ignored_loggers is not None and record.name not in ignored_loggers:
138+
if (
139+
ignored_loggers is not None
140+
and record.name.strip() not in ignored_loggers
141+
):
128142
integration = sentry_sdk.get_client().get_integration(
129143
LoggingIntegration
130144
)
@@ -169,7 +183,7 @@ def _can_record(self, record):
169183
# type: (LogRecord) -> bool
170184
"""Prevents ignored loggers from recording"""
171185
for logger in _IGNORED_LOGGERS:
172-
if fnmatch(record.name, logger):
186+
if fnmatch(record.name.strip(), logger):
173187
return False
174188
return True
175189

@@ -317,21 +331,6 @@ def _breadcrumb_from_record(self, record):
317331
}
318332

319333

320-
def _python_level_to_otel(record_level):
321-
# type: (int) -> Tuple[int, str]
322-
for py_level, otel_severity_number, otel_severity_text in [
323-
(50, 21, "fatal"),
324-
(40, 17, "error"),
325-
(30, 13, "warn"),
326-
(20, 9, "info"),
327-
(10, 5, "debug"),
328-
(5, 1, "trace"),
329-
]:
330-
if record_level >= py_level:
331-
return otel_severity_number, otel_severity_text
332-
return 0, "default"
333-
334-
335334
class SentryLogsHandler(_BaseHandler):
336335
"""
337336
A logging handler that records Sentry logs for each Python log record.
@@ -357,8 +356,9 @@ def emit(self, record):
357356

358357
def _capture_log_from_record(self, client, record):
359358
# type: (BaseClient, LogRecord) -> None
360-
scope = sentry_sdk.get_current_scope()
361-
otel_severity_number, otel_severity_text = _python_level_to_otel(record.levelno)
359+
otel_severity_number, otel_severity_text = _log_level_to_otel(
360+
record.levelno, SEVERITY_TO_OTEL_SEVERITY
361+
)
362362
project_root = client.options["project_root"]
363363
attrs = self._extra_from_record(record) # type: Any
364364
attrs["sentry.origin"] = "auto.logger.log"
@@ -369,12 +369,16 @@ def _capture_log_from_record(self, client, record):
369369
for i, arg in enumerate(record.args):
370370
attrs[f"sentry.message.parameter.{i}"] = (
371371
arg
372-
if isinstance(arg, str)
373-
or isinstance(arg, float)
374-
or isinstance(arg, int)
375-
or isinstance(arg, bool)
372+
if isinstance(arg, (str, float, int, bool))
376373
else safe_repr(arg)
377374
)
375+
elif isinstance(record.args, dict):
376+
for key, value in record.args.items():
377+
attrs[f"sentry.message.parameter.{key}"] = (
378+
value
379+
if isinstance(value, (str, float, int, bool))
380+
else safe_repr(value)
381+
)
378382
if record.lineno:
379383
attrs["code.line.number"] = record.lineno
380384
if record.pathname:
@@ -399,7 +403,6 @@ def _capture_log_from_record(self, client, record):
399403

400404
# noinspection PyProtectedMember
401405
client._capture_experimental_log(
402-
scope,
403406
{
404407
"severity_text": otel_severity_text,
405408
"severity_number": otel_severity_number,

0 commit comments

Comments
 (0)