From 49d6788478406e40fcd9f0c0e9e6ccf06c376618 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Thu, 29 Oct 2026 21:48:39 +0300 Subject: [PATCH] Cap sentry-sdk below 3 and remove the 3.x code paths --- .github/workflows/_checks.yml | 19 ------ benchmarks/README.md | 6 +- benchmarks/micro.py | 51 +++++---------- benchmarks/repro_sentry_txn.py | 6 +- benchmarks/sentry_scenarios.py | 63 ++++++++----------- docs/integrations/fastapi.md | 12 ++-- docs/integrations/fastmcp.md | 10 +-- docs/integrations/litestar.md | 13 ++-- docs/introduction/configuration.md | 31 ++++----- .../bootstrappers/litestar_bootstrapper.py | 5 +- .../instruments/sentry_instrument.py | 16 ++--- pyproject.toml | 6 +- 12 files changed, 78 insertions(+), 160 deletions(-) diff --git a/.github/workflows/_checks.yml b/.github/workflows/_checks.yml index d47abb8..71d7f2d 100644 --- a/.github/workflows/_checks.yml +++ b/.github/workflows/_checks.yml @@ -65,25 +65,6 @@ jobs: - run: uv python pin ${{ matrix.python-version }} - run: just install - run: just test-ci - sentry-majors: - # The pytest jobs resolve whichever sentry-sdk major is newest; this runs the suite on each one. - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - sentry-sdk: ["sentry-sdk<3", "sentry-sdk==3.0.0b2"] - steps: - - uses: actions/checkout@v6 - - uses: extractions/setup-just@v4 - - uses: astral-sh/setup-uv@v8.2.0 - with: - enable-cache: true - cache-dependency-glob: "**/pyproject.toml" - - run: uv python install 3.14 - - run: uv python pin 3.14 - - run: just install - - run: uv pip install "${{ matrix.sentry-sdk }}" - - run: just test free-threaded: runs-on: ubuntu-latest strategy: diff --git a/benchmarks/README.md b/benchmarks/README.md index 728ee71..648f39f 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -721,11 +721,7 @@ scenario there, `full_all_tuned` included, is reachable through `FastAPIConfig`. [sentry-python#7400](https://github.com/getsentry/sentry-python/issues/7400) and imports nothing from this directory. -Every number here was measured on sentry-sdk 2.67.1. The scripts also run on 3.x, which builds no -transaction while tracing is off, so the patched scenarios, `errors_only_txn_endpoint_style` and -`repro_sentry_txn.py --patched` exist only on 2.x. On 3.x, `micro.py` times `continue_trace` and -the root `start_span` in place of the `Transaction` calls. 3.x also leaves `LoggingIntegration` out -of its default integrations, so its `errors_only` records no log breadcrumbs. +Every number here was measured on sentry-sdk 2.67.1. The scripts run on sentry-sdk 2.x only. Numbers are machine-specific and move a few percent run to run; the ratios and the ordering are the durable part. Everything here was measured in one session on one idle machine, which is the diff --git a/benchmarks/micro.py b/benchmarks/micro.py index 3e91640..ae7a852 100644 --- a/benchmarks/micro.py +++ b/benchmarks/micro.py @@ -1,13 +1,10 @@ """Per-operation costs of the pieces sentry-sdk runs on every request. Attributes the per-request total from `run.py` to individual calls, which is what makes the -upstream issues actionable: on sentry-sdk 2.x, `Transaction(...)` and the Mersenne Twister seed -inside it are the two largest items, and neither is reachable from a configuration knob. 3.x builds -no transaction while tracing is off, so there it times `continue_trace` and the root `start_span` -that the ASGI integration calls on every request instead. +upstream issues actionable: `Transaction(...)` and the Mersenne Twister seed inside it are the +two largest items, and neither is reachable from a configuration knob. """ -import importlib import statistics import timeit import typing @@ -17,10 +14,9 @@ import sentry_scenarios import sentry_sdk from sentry_sdk.scope import Scope +from sentry_sdk.tracing import Transaction from sentry_sdk.tracing_utils import Baggage, PropagationContext, _generate_sample_rand -from lite_bootstrap.instruments.sentry_instrument import IS_SENTRY_SDK_2 - HEADERS: typing.Final = { "host": "testserver", @@ -29,7 +25,6 @@ "connection": "keep-alive", } TRACE_ID: typing.Final = uuid.uuid4().hex -SDK2_TRACING: typing.Final[typing.Any] = importlib.import_module("sentry_sdk.tracing") if IS_SENTRY_SDK_2 else None def timed(label: str, stmt: typing.Callable[[], object], number: int = 20000, repeat: int = 5) -> None: @@ -39,35 +34,15 @@ def timed(label: str, stmt: typing.Callable[[], object], number: int = 20000, re print(f"{label:<46} {best:>8.2f} us (best) {median:>8.2f} us (med)") -def time_sdk2_transaction(isolation_scope: typing.Any, current_scope: typing.Any) -> None: # noqa: ANN401 - def new_transaction() -> object: - return SDK2_TRACING.Transaction(op="http.server", name="GET /ping", source="route") - - def start_and_finish() -> None: - with current_scope.start_transaction(new_transaction(), custom_sampling_context={"asgi_scope": {}}): - pass - - timed( - "scope.continue_trace(headers)", - lambda: isolation_scope.continue_trace(HEADERS, op="http.server", name="GET /ping", source="route"), - ) - timed("Transaction(op, name, source)", new_transaction) - timed("start_transaction(txn) + exit (tracing off)", start_and_finish, number=5000) - - -def time_sdk3_span() -> None: - def start_and_end() -> None: - with sentry_sdk.start_span(name="GET /ping", parent_span=None): - pass - - timed("continue_trace(headers)", lambda: sentry_sdk.continue_trace(HEADERS)) - timed("start_span(root) + exit (tracing off)", start_and_end, number=5000) - - def main() -> None: sentry_sdk.init(**sentry_scenarios.base_kwargs(max_breadcrumbs=15, attach_stacktrace=True)) scope = Scope.get_isolation_scope() + def start_and_finish() -> None: + transaction = Transaction(op="http.server", name="GET /ping", source="route") + with sentry_sdk.start_transaction(transaction, custom_sampling_context={"asgi_scope": {}}): + pass + def isolation() -> None: with sentry_sdk.isolation_scope(): pass @@ -79,10 +54,12 @@ def isolation() -> None: timed("PropagationContext.from_incoming_data(headers)", lambda: PropagationContext.from_incoming_data(HEADERS)) timed("Baggage.from_incoming_header(None)", lambda: Baggage.from_incoming_header(None)) timed("scope.generate_propagation_context(headers)", lambda: scope.generate_propagation_context(HEADERS)) - if IS_SENTRY_SDK_2: - time_sdk2_transaction(scope, sentry_sdk.get_current_scope()) - else: - time_sdk3_span() + timed( + "scope.continue_trace(headers)", + lambda: scope.continue_trace(HEADERS, op="http.server", name="GET /ping", source="route"), + ) + timed("Transaction(op, name, source)", lambda: Transaction(op="http.server", name="GET /ping", source="route")) + timed("start_transaction(txn) + exit (tracing off)", start_and_finish, number=5000) timed("isolation_scope() enter/exit", isolation) timed("scope.fork()", scope.fork) timed("get_client()", sentry_sdk.get_client) diff --git a/benchmarks/repro_sentry_txn.py b/benchmarks/repro_sentry_txn.py index 8ddf887..ac4cd17 100644 --- a/benchmarks/repro_sentry_txn.py +++ b/benchmarks/repro_sentry_txn.py @@ -7,8 +7,6 @@ python repro_sentry_txn.py --off # SDK never initialised python repro_sentry_txn.py # SDK as shipped python repro_sentry_txn.py --patched # skip the Transaction, keep trace propagation - -`--patched` runs on sentry-sdk 2.x only: 3.x builds no transaction while tracing is off. """ import asyncio @@ -57,7 +55,7 @@ def setup(*, patched: bool) -> None: # tracing is DISABLED: traces_sample_rate and traces_sampler are both unset sentry_sdk.init(dsn="https://public@o0.ingest.sentry.io/0", transport=NullTransport) if patched: - setattr(Scope, "continue_trace", continue_trace_without_transaction) # noqa: B010 + Scope.continue_trace = continue_trace_without_transaction # ty: ignore[invalid-assignment] async def drive(app: typing.Callable[..., typing.Awaitable[None]], requests: int) -> float: @@ -78,8 +76,6 @@ async def send(message: dict[str, typing.Any]) -> None: def main() -> None: if "--patched" in sys.argv: - if not sentry_sdk.VERSION.startswith("2."): - sys.exit(f"--patched needs sentry-sdk 2.x; {sentry_sdk.VERSION} builds no transaction to skip") mode = "patched" elif "--off" in sys.argv: mode = "off" diff --git a/benchmarks/sentry_scenarios.py b/benchmarks/sentry_scenarios.py index 570d3c0..e836f03 100644 --- a/benchmarks/sentry_scenarios.py +++ b/benchmarks/sentry_scenarios.py @@ -5,16 +5,14 @@ can be undone. The patched scenarios are not configurations a user can reach - they simulate an upstream -fix so its value can be measured before proposing it. They, and `errors_only_txn_endpoint_style`, -exist only on sentry-sdk 2.x: 3.x builds no transaction while tracing is off and has no -`transaction_style`. +fix so its value can be measured before proposing it. """ -import importlib import logging import typing import sentry_sdk +import sentry_sdk.tracing from driver import DSN, TRANSPORT from fastapi import FastAPI from sentry_sdk.integrations.dedupe import DedupeIntegration @@ -27,10 +25,6 @@ from sentry_sdk.integrations.threading import ThreadingIntegration from sentry_sdk.scope import Scope -from lite_bootstrap.instruments.sentry_instrument import IS_SENTRY_SDK_2 - - -SDK2_TRACING: typing.Final = importlib.import_module("sentry_sdk.tracing") if IS_SENTRY_SDK_2 else None logger = logging.getLogger("bench") @@ -66,14 +60,8 @@ def _no_transaction_integrations() -> list[typing.Any]: def _errors_and_breadcrumbs_only(*, breadcrumbs: bool) -> LoggingIntegration: - """Errors become events and nothing goes to Sentry Logs; 3.0 renamed the arguments for that.""" - breadcrumb_level = logging.INFO if breadcrumbs else None - kwargs: dict[str, typing.Any] = ( - {"level": breadcrumb_level, "sentry_logs_level": None} - if IS_SENTRY_SDK_2 - else {"breadcrumb_level": breadcrumb_level, "event_level": logging.ERROR, "level": None} - ) - return LoggingIntegration(**kwargs) + """Errors become events and nothing goes to Sentry Logs.""" + return LoggingIntegration(level=logging.INFO if breadcrumbs else None, sentry_logs_level=None) SCENARIOS: dict[str, typing.Callable[[], dict[str, typing.Any]] | None] = { @@ -155,7 +143,7 @@ def _errors_and_breadcrumbs_only(*, breadcrumbs: bool) -> LoggingIntegration: def _patch_lazy_sample_rand() -> None: """`Transaction.__init__` derives sample_rand from the trace id even when tracing is off.""" - setattr(SDK2_TRACING, "_generate_sample_rand", lambda trace_id, **kwargs: 0.5) # noqa: ARG005, B010 + sentry_sdk.tracing._generate_sample_rand = lambda trace_id, **kwargs: 0.5 # ty: ignore[invalid-assignment] # noqa: ARG005 def _patch_skip_transaction() -> None: @@ -166,30 +154,29 @@ def continue_trace( ) -> None: self.generate_propagation_context(environ_or_headers) - setattr(Scope, "continue_trace", continue_trace) # noqa: B010 + Scope.continue_trace = continue_trace # ty: ignore[invalid-assignment] -PATCHES: dict[str, list[typing.Callable[[], None]]] = {} +SCENARIOS["errors_only_txn_endpoint_style"] = lambda: base_kwargs( + max_breadcrumbs=15, + attach_stacktrace=True, + integrations=[ + StarletteIntegration(transaction_style="endpoint"), + FastApiIntegration(transaction_style="endpoint"), + ], +) -if IS_SENTRY_SDK_2: - endpoint_style: dict[str, typing.Any] = {"transaction_style": "endpoint"} - SCENARIOS["errors_only_txn_endpoint_style"] = lambda: base_kwargs( - max_breadcrumbs=15, - attach_stacktrace=True, - integrations=[StarletteIntegration(**endpoint_style), FastApiIntegration(**endpoint_style)], - ) - PATCHES.update( - { - "errors_only_lazy_sample_rand": [_patch_lazy_sample_rand], - "errors_only_skip_txn": [_patch_skip_transaction], - "errors_only_skip_txn_no_sessions": [_patch_skip_transaction], - "errors_only_logging_lean_skip_txn": [_patch_skip_transaction], - } - ) - SCENARIOS["errors_only_lazy_sample_rand"] = SCENARIOS["errors_only"] - SCENARIOS["errors_only_skip_txn"] = SCENARIOS["errors_only"] - SCENARIOS["errors_only_skip_txn_no_sessions"] = SCENARIOS["errors_only_no_sessions"] - SCENARIOS["errors_only_logging_lean_skip_txn"] = SCENARIOS["errors_only_logging_lean"] +PATCHES: dict[str, list[typing.Callable[[], None]]] = { + "errors_only_lazy_sample_rand": [_patch_lazy_sample_rand], + "errors_only_skip_txn": [_patch_skip_transaction], + "errors_only_skip_txn_no_sessions": [_patch_skip_transaction], + "errors_only_logging_lean_skip_txn": [_patch_skip_transaction], +} + +SCENARIOS["errors_only_lazy_sample_rand"] = SCENARIOS["errors_only"] +SCENARIOS["errors_only_skip_txn"] = SCENARIOS["errors_only"] +SCENARIOS["errors_only_skip_txn_no_sessions"] = SCENARIOS["errors_only_no_sessions"] +SCENARIOS["errors_only_logging_lean_skip_txn"] = SCENARIOS["errors_only_logging_lean"] def setup(scenario: str) -> None: diff --git a/docs/integrations/fastapi.md b/docs/integrations/fastapi.md index 243fa95..b20a42b 100644 --- a/docs/integrations/fastapi.md +++ b/docs/integrations/fastapi.md @@ -94,12 +94,12 @@ event either way. Measured in-process on a do-nothing handler, over the same app without Sentry: -| Sentry setup | sentry-sdk 2.73.0 | 3.0.0b2 | -|---|---:|---:| -| lean capture, the default | +6.1 µs | +5.1 µs | -| the same, request carries a sampled `sentry-trace` | +11.5 µs | +10.9 µs | -| `sentry_lean_error_capture=False` | +57.1 µs | +47.0 µs | -| the same, request carries a sampled `sentry-trace` | +59.2 µs | +113.2 µs | +| Sentry setup | sentry-sdk 2.73.0 | +|---|---:| +| lean capture, the default | +6.1 µs | +| the same, request carries a sampled `sentry-trace` | +11.5 µs | +| `sentry_lean_error_capture=False` | +57.1 µs | +| the same, request carries a sampled `sentry-trace` | +59.2 µs | sentry-sdk records installed integrations for the whole process, and `disabled_integrations` cannot remove one that an earlier `sentry_sdk.init` installed. That matters only with diff --git a/docs/integrations/fastmcp.md b/docs/integrations/fastmcp.md index 8b21989..cc94be8 100644 --- a/docs/integrations/fastmcp.md +++ b/docs/integrations/fastmcp.md @@ -85,11 +85,11 @@ attached, with lean capture on or off and with `MCPIntegration` on or off. Measured in-process per `tools/call` over stateless JSON HTTP, over the same app without Sentry: -| Sentry setup | sentry-sdk 2.73.0 | 3.0.0b2 | -|---|---:|---:| -| lean capture, the default | +20.6 µs | +15.9 µs | -| `sentry_lean_error_capture=False` | +82.2 µs | +65.0 µs | -| the same, request carries a sampled `sentry-trace` | +82.3 µs | +216.4 µs | +| Sentry setup | sentry-sdk 2.73.0 | +|---|---:| +| lean capture, the default | +20.6 µs | +| `sentry_lean_error_capture=False` | +82.2 µs | +| the same, request carries a sampled `sentry-trace` | +82.3 µs | A tool call costs about 620 µs before Sentry, and rows differ by up to 15% between runs. diff --git a/docs/integrations/litestar.md b/docs/integrations/litestar.md index 6bee667..1143752 100644 --- a/docs/integrations/litestar.md +++ b/docs/integrations/litestar.md @@ -113,8 +113,7 @@ sentry-sdk's `LitestarIntegration`. Tracing counts as off when `sentry_traces_sa and `sentry_additional_params` sets none of `traces_sample_rate`, `traces_sampler` and `enable_tracing`. With tracing off, the integration still builds a transaction and a span per middleware for every request and then discards them -([getsentry/sentry-python#7400](https://github.com/getsentry/sentry-python/issues/7400)). On -sentry-sdk 3.0, an incoming `sentry-trace` header marked as sampled makes it record a real span. +([getsentry/sentry-python#7400](https://github.com/getsentry/sentry-python/issues/7400)). `sentry_lean_error_capture` picks the path: @@ -143,11 +142,11 @@ answer with a 404 is not reported. The integration reports every exception excep Measured in-process on a do-nothing handler, over the same app without Sentry: -| Sentry setup | sentry-sdk 2.73.0 | 3.0.0b2 | -|---|---:|---:| -| lean capture, the default | +7.3 µs | +8.3 µs | -| `sentry_lean_error_capture=False` | +70.7 µs | +59.5 µs | -| the same, request carries a sampled `sentry-trace` | +74.4 µs | +147.4 µs | +| Sentry setup | sentry-sdk 2.73.0 | +|---|---:| +| lean capture, the default | +7.3 µs | +| `sentry_lean_error_capture=False` | +70.7 µs | +| the same, request carries a sampled `sentry-trace` | +74.4 µs | sentry-sdk records installed integrations for the whole process, and `disabled_integrations` cannot remove one that an earlier `sentry_sdk.init` installed. That matters only with diff --git a/docs/introduction/configuration.md b/docs/introduction/configuration.md index 5ede1fb..591fd9c 100644 --- a/docs/introduction/configuration.md +++ b/docs/introduction/configuration.md @@ -51,18 +51,13 @@ Litestar, FastAPI and FastMCP while Sentry tracing is off; see [Performance](per ### Sentry logging integration -lite-bootstrap supports sentry-sdk 2.70 and later, on both the 2.x and 3.x majors. Unless -`sentry_integrations` already contains a `LoggingIntegration`, it appends one that turns `ERROR` -records into events, records breadcrumbs from `sentry_logging_breadcrumb_level` up and sends no -Sentry Logs. 3.0 renamed the integration's arguments, so the call differs per major: - -- 2.x: `LoggingIntegration(level=sentry_logging_breadcrumb_level, sentry_logs_level=None)`, with the - event level left at its `ERROR` default. -- 3.x: `LoggingIntegration(breadcrumb_level=sentry_logging_breadcrumb_level, event_level=logging.ERROR, level=None)`. - On 3.x `level` means Sentry Logs, and `event_level` defaults to `None`, so without it a logged - error creates no event. - -On 2.x, turning `sentry_logs_level` off is free. lite-bootstrap never sets `enable_logs`, so Sentry +lite-bootstrap supports sentry-sdk 2.70 and later 2.x releases. sentry-sdk 3.0 is not supported +yet. Unless `sentry_integrations` already contains a `LoggingIntegration`, lite-bootstrap appends +`LoggingIntegration(level=sentry_logging_breadcrumb_level, sentry_logs_level=None)`. It turns +`ERROR` records into events, records breadcrumbs from `sentry_logging_breadcrumb_level` up and sends +no Sentry Logs. + +Turning `sentry_logs_level` off is free. lite-bootstrap never sets `enable_logs`, so Sentry Logs is off, but `SentryLogsHandler.emit` formats the record *before* it checks whether logs are enabled ([getsentry/sentry-python#7402](https://github.com/getsentry/sentry-python/issues/7402)) - it formats every `INFO`+ record and discards the result. @@ -92,8 +87,8 @@ SQLAlchemy, asyncpg, redis, httpx, aiohttp, MCP and others. lite-bootstrap passe With tracing off, those integrations still open a span and record a breadcrumb for every SQL statement, Redis command or outgoing HTTP request. `SqlalchemyIntegration` measured about 64 µs per -statement on sentry-sdk 2.70, and `RedisIntegration` +27.1 µs per FastStream publish on 2.73.0 and -+9.5 µs on 3.0.0b2 ([FastStream](https://github.com/modern-python/lite-bootstrap/blob/main/benchmarks/README.md#8d-faststream)). The default integrations stay: +statement on sentry-sdk 2.70, and `RedisIntegration` +27.1 µs per FastStream publish on 2.73.0 +([FastStream](https://github.com/modern-python/lite-bootstrap/blob/main/benchmarks/README.md#8d-faststream)). The default integrations stay: dedupe, excepthook, threading, stdlib, atexit, modules, argv and lite-bootstrap's own `LoggingIntegration`. @@ -116,12 +111,8 @@ default as a whole, so keys you leave out take sentry-sdk's own defaults, which change one key, spread the default: `{**DEFAULT_DATA_COLLECTION, "user_info": True}`. Read more about the option [here](https://docs.sentry.io/platforms/python/data-management/data-collected/). -`send_default_pii` has no effect on 2.x while `data_collection` is set, and on 3.x it is gone. 3.0 -also raises `TypeError` at `sentry_sdk.init` for any option it removed, so passing one of these -through `sentry_additional_params` fails at bootstrap on 3.x, `enable_tracing`, `enable_logs` and -`include_local_variables` among them. sentry-sdk's -[migration guide](https://github.com/getsentry/sentry-python/blob/master/MIGRATION_GUIDE.md) lists -every removed option. +`send_default_pii` has no effect while `data_collection` is set, because sentry-sdk lets +`data_collection` take precedence. ## Prometheus diff --git a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py index a912c27..3b5acc5 100644 --- a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py @@ -74,9 +74,6 @@ from sentry_sdk.integrations.litestar import LitestarIntegration, retrieve_user_from_scope from sentry_sdk.utils import transaction_from_function - # 3.x narrowed ignore_logger to Sentry Logs and moved its 2.x meaning to ignore_logger_for_events. - ignore_logger_for_events = getattr(sentry_logging, "ignore_logger_for_events", sentry_logging.ignore_logger) - if import_checker.is_opentelemetry_installed: from opentelemetry.metrics import get_meter_provider from opentelemetry.trace import get_tracer_provider @@ -437,7 +434,7 @@ def bootstrap(self) -> None: return warn_about_sentry_integrations_installed_earlier([LitestarIntegration]) # LitestarIntegration ignores this logger too: Litestar logs every exception it handles. - ignore_logger_for_events("litestar") + sentry_logging.ignore_logger("litestar") application_config: typing.Final = self.bootstrap_config.application_config application_config.middleware.insert(0, LitestarSentryCaptureMiddleware()) application_config.after_exception.append(capture_litestar_exception_after_handling) diff --git a/lite_bootstrap/instruments/sentry_instrument.py b/lite_bootstrap/instruments/sentry_instrument.py index 2dcf8bf..3670426 100644 --- a/lite_bootstrap/instruments/sentry_instrument.py +++ b/lite_bootstrap/instruments/sentry_instrument.py @@ -19,8 +19,6 @@ from sentry_sdk.integrations.logging import LoggingIntegration from sentry_sdk.utils import event_from_exception - IS_SENTRY_SDK_2: typing.Final = sentry_sdk.VERSION.startswith("2.") - _PII_KEY_DENYLIST: typing.Final = {"mode": "denylist", "terms": ["forwarded", "-ip", "remote-", "via", "-user"]} DEFAULT_DATA_COLLECTION: typing.Final[dict[str, typing.Any]] = { @@ -128,9 +126,10 @@ def capture_request_exception( handled: bool, enrich: typing.Callable[["sentry_types.Event"], "sentry_types.Event"] = lambda event: event, ) -> None: - client_options: typing.Final = {"client_options": sentry_sdk.get_client().options} if IS_SENTRY_SDK_2 else {} event, hint = event_from_exception( - exception, mechanism={"type": mechanism_type, "handled": handled}, **client_options + exception, + client_options=sentry_sdk.get_client().options, + mechanism={"type": mechanism_type, "handled": handled}, ) sentry_sdk.capture_event(enrich(event), hint=hint) @@ -223,13 +222,8 @@ def _build_integrations(self) -> list["Integration"]: if not config.sentry_default_integrations: self._warn_breadcrumb_level_ignored("sentry_default_integrations is False") return config.sentry_integrations - breadcrumb_level = config.sentry_logging_breadcrumb_level - logging_integration_kwargs: dict[str, typing.Any] = ( - {"level": breadcrumb_level, "sentry_logs_level": None} - if IS_SENTRY_SDK_2 - else {"breadcrumb_level": breadcrumb_level, "event_level": logging.ERROR, "level": None} - ) - return [*config.sentry_integrations, LoggingIntegration(**logging_integration_kwargs)] + logging_integration = LoggingIntegration(level=config.sentry_logging_breadcrumb_level, sentry_logs_level=None) + return [*config.sentry_integrations, logging_integration] def _build_init_params(self) -> dict[str, typing.Any]: config = self.bootstrap_config diff --git a/pyproject.toml b/pyproject.toml index d27cfb1..59fe217 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,9 +44,9 @@ orjson = [ "orjson>=3.8.0", ] sentry = [ - # >=2.70 for the top-level `data_collection` option, which 3.x keeps and SentryInstrument - # passes on both majors. 3.x is supported: LoggingIntegration's arguments are chosen per major. - "sentry-sdk>=2.70,<4", + # >=2.70 for the top-level `data_collection` option, which SentryInstrument passes. + # <3 until sentry-sdk 3.0 is out of beta: it renames LoggingIntegration's arguments. + "sentry-sdk>=2.70,<3", ] pyroscope = [ # >=0.7.2 for pyroscope.shutdown() (PyroscopeInstrument.teardown); 0.7.1 and below carry