Skip to content

Commit fc5db4f

Browse files
authored
ref(otel): Remove experimental autoinstrumentation (#3239)
1 parent 6bb2081 commit fc5db4f

File tree

5 files changed

+25
-331
lines changed

5 files changed

+25
-331
lines changed

sentry_sdk/integrations/opentelemetry/distro.py

Lines changed: 0 additions & 66 deletions
This file was deleted.

sentry_sdk/integrations/opentelemetry/integration.py

Lines changed: 24 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,26 @@
44
removed at any time without prior notice.
55
"""
66

7-
import sys
8-
from importlib import import_module
9-
107
from sentry_sdk.integrations import DidNotEnable, Integration
11-
from sentry_sdk.integrations.opentelemetry.distro import _SentryDistro
12-
from sentry_sdk.utils import logger, _get_installed_modules
13-
from sentry_sdk._types import TYPE_CHECKING
8+
from sentry_sdk.integrations.opentelemetry.propagator import SentryPropagator
9+
from sentry_sdk.integrations.opentelemetry.span_processor import SentrySpanProcessor
10+
from sentry_sdk.utils import logger
1411

1512
try:
16-
from opentelemetry.instrumentation.auto_instrumentation._load import (
17-
_load_instrumentors,
18-
)
13+
from opentelemetry import trace
14+
from opentelemetry.propagate import set_global_textmap
15+
from opentelemetry.sdk.trace import TracerProvider
1916
except ImportError:
2017
raise DidNotEnable("opentelemetry not installed")
2118

22-
if TYPE_CHECKING:
23-
from typing import Dict
19+
try:
20+
from opentelemetry.instrumentation.django import DjangoInstrumentor # type: ignore[import-not-found]
21+
except ImportError:
22+
DjangoInstrumentor = None
2423

2524

26-
CLASSES_TO_INSTRUMENT = {
27-
# A mapping of packages to their entry point class that will be instrumented.
28-
# This is used to post-instrument any classes that were imported before OTel
29-
# instrumentation took place.
30-
"fastapi": "fastapi.FastAPI",
31-
"flask": "flask.Flask",
32-
# XXX Add a mapping for all instrumentors that patch by replacing a class
25+
CONFIGURABLE_INSTRUMENTATIONS = {
26+
DjangoInstrumentor: {"is_sql_commentor_enabled": True},
3327
}
3428

3529

@@ -44,123 +38,21 @@ def setup_once():
4438
"Use at your own risk."
4539
)
4640

47-
original_classes = _record_unpatched_classes()
48-
49-
try:
50-
distro = _SentryDistro()
51-
distro.configure()
52-
# XXX This does some initial checks before loading instrumentations
53-
# (checks OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, checks version
54-
# compat). If we don't want this in the future, we can implement our
55-
# own _load_instrumentors (it anyway just iterates over
56-
# opentelemetry_instrumentor entry points).
57-
_load_instrumentors(distro)
58-
except Exception:
59-
logger.exception("[OTel] Failed to auto-initialize OpenTelemetry")
60-
61-
# XXX: Consider whether this is ok to keep and make default.
62-
# The alternative is asking folks to follow specific import order for
63-
# some integrations (sentry_sdk.init before you even import Flask, for
64-
# instance).
65-
try:
66-
_patch_remaining_classes(original_classes)
67-
except Exception:
68-
logger.exception(
69-
"[OTel] Failed to post-patch instrumented classes. "
70-
"You might have to make sure sentry_sdk.init() is called before importing anything else."
71-
)
41+
_setup_sentry_tracing()
42+
# _setup_instrumentors()
7243

7344
logger.debug("[OTel] Finished setting up OpenTelemetry integration")
7445

7546

76-
def _record_unpatched_classes():
77-
# type: () -> Dict[str, type]
78-
"""
79-
Keep references to classes that are about to be instrumented.
80-
81-
Used to search for unpatched classes after the instrumentation has run so
82-
that they can be patched manually.
83-
"""
84-
installed_packages = _get_installed_modules()
85-
86-
original_classes = {}
87-
88-
for package, orig_path in CLASSES_TO_INSTRUMENT.items():
89-
if package in installed_packages:
90-
try:
91-
original_cls = _import_by_path(orig_path)
92-
except (AttributeError, ImportError):
93-
logger.debug("[OTel] Failed to import %s", orig_path)
94-
continue
95-
96-
original_classes[package] = original_cls
97-
98-
return original_classes
99-
100-
101-
def _patch_remaining_classes(original_classes):
102-
# type: (Dict[str, type]) -> None
103-
"""
104-
Best-effort attempt to patch any uninstrumented classes in sys.modules.
105-
106-
This enables us to not care about the order of imports and sentry_sdk.init()
107-
in user code. If e.g. the Flask class had been imported before sentry_sdk
108-
was init()ed (and therefore before the OTel instrumentation ran), it would
109-
not be instrumented. This function goes over remaining uninstrumented
110-
occurrences of the class in sys.modules and replaces them with the
111-
instrumented class.
112-
113-
Since this is looking for exact matches, it will not work in some scenarios
114-
(e.g. if someone is not using the specific class explicitly, but rather
115-
inheriting from it). In those cases it's still necessary to sentry_sdk.init()
116-
before importing anything that's supposed to be instrumented.
117-
"""
118-
# check which classes have actually been instrumented
119-
instrumented_classes = {}
120-
121-
for package in list(original_classes.keys()):
122-
original_path = CLASSES_TO_INSTRUMENT[package]
123-
124-
try:
125-
cls = _import_by_path(original_path)
126-
except (AttributeError, ImportError):
127-
logger.debug(
128-
"[OTel] Failed to check if class has been instrumented: %s",
129-
original_path,
130-
)
131-
del original_classes[package]
132-
continue
133-
134-
if not cls.__module__.startswith("opentelemetry."):
135-
del original_classes[package]
136-
continue
137-
138-
instrumented_classes[package] = cls
139-
140-
if not instrumented_classes:
141-
return
142-
143-
# replace occurrences of the original unpatched class in sys.modules
144-
for module_name, module in sys.modules.copy().items():
145-
if (
146-
module_name.startswith("sentry_sdk")
147-
or module_name in sys.builtin_module_names
148-
):
149-
continue
150-
151-
for package, original_cls in original_classes.items():
152-
for var_name, var in vars(module).copy().items():
153-
if var == original_cls:
154-
logger.debug(
155-
"[OTel] Additionally patching %s from %s",
156-
original_cls,
157-
module_name,
158-
)
159-
160-
setattr(module, var_name, instrumented_classes[package])
47+
def _setup_sentry_tracing():
48+
# type: () -> None
49+
provider = TracerProvider()
50+
provider.add_span_processor(SentrySpanProcessor())
51+
trace.set_tracer_provider(provider)
52+
set_global_textmap(SentryPropagator())
16153

16254

163-
def _import_by_path(path):
164-
# type: (str) -> type
165-
parts = path.rsplit(".", maxsplit=1)
166-
return getattr(import_module(parts[0]), parts[-1])
55+
def _setup_instrumentors():
56+
# type: () -> None
57+
for instrumentor, kwargs in CONFIGURABLE_INSTRUMENTATIONS.items():
58+
instrumentor().instrument(**kwargs)

setup.py

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -65,61 +65,7 @@ def get_file_text(file_name):
6565
"loguru": ["loguru>=0.5"],
6666
"openai": ["openai>=1.0.0", "tiktoken>=0.3.0"],
6767
"opentelemetry": ["opentelemetry-distro>=0.35b0"],
68-
"opentelemetry-experimental": [
69-
# There's an umbrella package called
70-
# opentelemetry-contrib-instrumentations that installs all
71-
# available instrumentation packages, however it's broken in recent
72-
# versions (after 0.41b0), see
73-
# https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2053
74-
"opentelemetry-instrumentation-aio-pika==0.46b0",
75-
"opentelemetry-instrumentation-aiohttp-client==0.46b0",
76-
# "opentelemetry-instrumentation-aiohttp-server==0.46b0", # broken package
77-
"opentelemetry-instrumentation-aiopg==0.46b0",
78-
"opentelemetry-instrumentation-asgi==0.46b0",
79-
"opentelemetry-instrumentation-asyncio==0.46b0",
80-
"opentelemetry-instrumentation-asyncpg==0.46b0",
81-
"opentelemetry-instrumentation-aws-lambda==0.46b0",
82-
"opentelemetry-instrumentation-boto==0.46b0",
83-
"opentelemetry-instrumentation-boto3sqs==0.46b0",
84-
"opentelemetry-instrumentation-botocore==0.46b0",
85-
"opentelemetry-instrumentation-cassandra==0.46b0",
86-
"opentelemetry-instrumentation-celery==0.46b0",
87-
"opentelemetry-instrumentation-confluent-kafka==0.46b0",
88-
"opentelemetry-instrumentation-dbapi==0.46b0",
89-
"opentelemetry-instrumentation-django==0.46b0",
90-
"opentelemetry-instrumentation-elasticsearch==0.46b0",
91-
"opentelemetry-instrumentation-falcon==0.46b0",
92-
"opentelemetry-instrumentation-fastapi==0.46b0",
93-
"opentelemetry-instrumentation-flask==0.46b0",
94-
"opentelemetry-instrumentation-grpc==0.46b0",
95-
"opentelemetry-instrumentation-httpx==0.46b0",
96-
"opentelemetry-instrumentation-jinja2==0.46b0",
97-
"opentelemetry-instrumentation-kafka-python==0.46b0",
98-
"opentelemetry-instrumentation-logging==0.46b0",
99-
"opentelemetry-instrumentation-mysql==0.46b0",
100-
"opentelemetry-instrumentation-mysqlclient==0.46b0",
101-
"opentelemetry-instrumentation-pika==0.46b0",
102-
"opentelemetry-instrumentation-psycopg==0.46b0",
103-
"opentelemetry-instrumentation-psycopg2==0.46b0",
104-
"opentelemetry-instrumentation-pymemcache==0.46b0",
105-
"opentelemetry-instrumentation-pymongo==0.46b0",
106-
"opentelemetry-instrumentation-pymysql==0.46b0",
107-
"opentelemetry-instrumentation-pyramid==0.46b0",
108-
"opentelemetry-instrumentation-redis==0.46b0",
109-
"opentelemetry-instrumentation-remoulade==0.46b0",
110-
"opentelemetry-instrumentation-requests==0.46b0",
111-
"opentelemetry-instrumentation-sklearn==0.46b0",
112-
"opentelemetry-instrumentation-sqlalchemy==0.46b0",
113-
"opentelemetry-instrumentation-sqlite3==0.46b0",
114-
"opentelemetry-instrumentation-starlette==0.46b0",
115-
"opentelemetry-instrumentation-system-metrics==0.46b0",
116-
"opentelemetry-instrumentation-threading==0.46b0",
117-
"opentelemetry-instrumentation-tornado==0.46b0",
118-
"opentelemetry-instrumentation-tortoiseorm==0.46b0",
119-
"opentelemetry-instrumentation-urllib==0.46b0",
120-
"opentelemetry-instrumentation-urllib3==0.46b0",
121-
"opentelemetry-instrumentation-wsgi==0.46b0",
122-
],
68+
"opentelemetry-experimental": ["opentelemetry-distro"],
12369
"pure_eval": ["pure_eval", "executing", "asttokens"],
12470
"pymongo": ["pymongo>=3.1"],
12571
"pyspark": ["pyspark>=2.4.4"],

tests/integrations/opentelemetry/test_experimental.py

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,6 @@
22

33
import pytest
44

5-
try:
6-
from flask import Flask
7-
from fastapi import FastAPI
8-
except ImportError:
9-
pass
10-
11-
12-
try:
13-
import opentelemetry.instrumentation.asyncio # noqa: F401
14-
15-
# We actually expect all OTel instrumentation packages to be available, but
16-
# for simplicity we just check for one here.
17-
instrumentation_packages_installed = True
18-
except ImportError:
19-
instrumentation_packages_installed = False
20-
21-
22-
needs_potel = pytest.mark.skipif(
23-
not instrumentation_packages_installed,
24-
reason="needs OTel instrumentor libraries installed",
25-
)
26-
275

286
@pytest.mark.forked
297
def test_integration_enabled_if_option_is_on(sentry_init, reset_integrations):
@@ -67,57 +45,3 @@ def test_integration_not_enabled_if_option_is_missing(sentry_init, reset_integra
6745
):
6846
sentry_init()
6947
mocked_setup_once.assert_not_called()
70-
71-
72-
@pytest.mark.forked
73-
@needs_potel
74-
def test_instrumentors_applied(sentry_init, reset_integrations):
75-
flask_instrument_mock = MagicMock()
76-
fastapi_instrument_mock = MagicMock()
77-
78-
with patch(
79-
"opentelemetry.instrumentation.flask.FlaskInstrumentor.instrument",
80-
flask_instrument_mock,
81-
):
82-
with patch(
83-
"opentelemetry.instrumentation.fastapi.FastAPIInstrumentor.instrument",
84-
fastapi_instrument_mock,
85-
):
86-
sentry_init(
87-
_experiments={
88-
"otel_powered_performance": True,
89-
},
90-
)
91-
92-
flask_instrument_mock.assert_called_once()
93-
fastapi_instrument_mock.assert_called_once()
94-
95-
96-
@pytest.mark.forked
97-
@needs_potel
98-
def test_post_patching(sentry_init, reset_integrations):
99-
assert not hasattr(
100-
Flask(__name__), "_is_instrumented_by_opentelemetry"
101-
), "Flask is not patched at the start"
102-
assert not hasattr(
103-
FastAPI(), "_is_instrumented_by_opentelemetry"
104-
), "FastAPI is not patched at the start"
105-
106-
sentry_init(
107-
_experiments={
108-
"otel_powered_performance": True,
109-
},
110-
)
111-
112-
flask = Flask(__name__)
113-
fastapi = FastAPI()
114-
115-
assert hasattr(
116-
flask, "_is_instrumented_by_opentelemetry"
117-
), "Flask has been patched after init()"
118-
assert flask._is_instrumented_by_opentelemetry is True
119-
120-
assert hasattr(
121-
fastapi, "_is_instrumented_by_opentelemetry"
122-
), "FastAPI has been patched after init()"
123-
assert fastapi._is_instrumented_by_opentelemetry is True

0 commit comments

Comments
 (0)