4
4
removed at any time without prior notice.
5
5
"""
6
6
7
- import sys
8
- from importlib import import_module
9
-
10
7
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
14
11
15
12
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
19
16
except ImportError :
20
17
raise DidNotEnable ("opentelemetry not installed" )
21
18
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
24
23
25
24
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 },
33
27
}
34
28
35
29
@@ -44,123 +38,21 @@ def setup_once():
44
38
"Use at your own risk."
45
39
)
46
40
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()
72
43
73
44
logger .debug ("[OTel] Finished setting up OpenTelemetry integration" )
74
45
75
46
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 ())
161
53
162
54
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 )
0 commit comments