Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2c26f42

Browse filesBrowse files
authored
Make profiling work with potel (#3704)
1 parent 8b875c1 commit 2c26f42
Copy full SHA for 2c26f42

File tree

Expand file treeCollapse file tree

4 files changed

+69
-11
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+69
-11
lines changed

‎sentry_sdk/integrations/opentelemetry/potel_span_processor.py

Copy file name to clipboardExpand all lines: sentry_sdk/integrations/opentelemetry/potel_span_processor.py
+50-2Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,22 @@
1212
from opentelemetry.sdk.trace import Span, ReadableSpan, SpanProcessor
1313

1414
from sentry_sdk import capture_event
15+
from sentry_sdk.consts import SPANDATA
1516
from sentry_sdk.tracing import DEFAULT_SPAN_ORIGIN
17+
from sentry_sdk.utils import get_current_thread_meta
18+
from sentry_sdk.profiler.continuous_profiler import (
19+
try_autostart_continuous_profiler,
20+
get_profiler_id,
21+
)
22+
from sentry_sdk.profiler.transaction_profiler import Profile
1623
from sentry_sdk.integrations.opentelemetry.utils import (
1724
is_sentry_span,
1825
convert_from_otel_timestamp,
1926
extract_span_attributes,
2027
extract_span_data,
2128
extract_transaction_name_source,
2229
get_trace_context,
30+
get_profile_context,
2331
get_sentry_meta,
2432
set_sentry_meta,
2533
)
@@ -54,8 +62,11 @@ def __init__(self):
5462

5563
def on_start(self, span, parent_context=None):
5664
# type: (Span, Optional[Context]) -> None
57-
if not is_sentry_span(span):
58-
self._add_root_span(span, get_current_span(parent_context))
65+
if is_sentry_span(span):
66+
return
67+
68+
self._add_root_span(span, get_current_span(parent_context))
69+
self._start_profile(span)
5970

6071
def on_end(self, span):
6172
# type: (ReadableSpan) -> None
@@ -94,6 +105,32 @@ def _add_root_span(self, span, parent_span):
94105
# root span points to itself
95106
set_sentry_meta(span, "root_span", span)
96107

108+
def _start_profile(self, span):
109+
# type: (Span) -> None
110+
try_autostart_continuous_profiler()
111+
profiler_id = get_profiler_id()
112+
thread_id, thread_name = get_current_thread_meta()
113+
114+
if profiler_id:
115+
span.set_attribute(SPANDATA.PROFILER_ID, profiler_id)
116+
if thread_id:
117+
span.set_attribute(SPANDATA.THREAD_ID, str(thread_id))
118+
if thread_name:
119+
span.set_attribute(SPANDATA.THREAD_NAME, thread_name)
120+
121+
is_root_span = not span.parent or span.parent.is_remote
122+
sampled = span.context and span.context.trace_flags.sampled
123+
124+
if is_root_span and sampled:
125+
# profiler uses time.perf_counter_ns() so we cannot use the
126+
# unix timestamp that is on span.start_time
127+
# setting it to 0 means the profiler will internally measure time on start
128+
profile = Profile(sampled, 0)
129+
# TODO-neel-potel sampling context??
130+
profile._set_initial_sampling_decision(sampling_context={})
131+
profile.__enter__()
132+
set_sentry_meta(span, "profile", profile)
133+
97134
def _flush_root_span(self, span):
98135
# type: (ReadableSpan) -> None
99136
transaction_event = self._root_span_to_transaction_event(span)
@@ -147,6 +184,10 @@ def _root_span_to_transaction_event(self, span):
147184
trace_context = get_trace_context(span, span_data=span_data)
148185
contexts = {"trace": trace_context}
149186

187+
profile_context = get_profile_context(span)
188+
if profile_context:
189+
contexts["profile"] = profile_context
190+
150191
if http_status:
151192
contexts["response"] = {"status_code": http_status}
152193

@@ -162,6 +203,13 @@ def _root_span_to_transaction_event(self, span):
162203
}
163204
)
164205

206+
profile = cast("Optional[Profile]", get_sentry_meta(span, "profile"))
207+
if profile:
208+
profile.__exit__(None, None, None)
209+
if profile.valid():
210+
event["profile"] = profile
211+
set_sentry_meta(span, "profile", None)
212+
165213
return event
166214

167215
def _span_to_json(self, span):

‎sentry_sdk/integrations/opentelemetry/sampler.py

Copy file name to clipboardExpand all lines: sentry_sdk/integrations/opentelemetry/sampler.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def should_sample(
128128
has_traces_sampler = callable(client.options.get("traces_sampler"))
129129
if has_traces_sampler:
130130
# TODO-anton: Make proper sampling_context
131+
# TODO-neel-potel: Make proper sampling_context
131132
sampling_context = {
132133
"transaction_context": {
133134
"name": name,

‎sentry_sdk/integrations/opentelemetry/utils.py

Copy file name to clipboardExpand all lines: sentry_sdk/integrations/opentelemetry/utils.py
+13-1Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import sentry_sdk
1919
from sentry_sdk.utils import Dsn
20-
from sentry_sdk.consts import SPANSTATUS, OP
20+
from sentry_sdk.consts import SPANSTATUS, OP, SPANDATA
2121
from sentry_sdk.tracing import get_span_status_from_http_code, DEFAULT_SPAN_ORIGIN
2222
from sentry_sdk.tracing_utils import Baggage, LOW_QUALITY_TRANSACTION_SOURCES
2323
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute
@@ -432,3 +432,15 @@ def set_sentry_meta(span, key, value):
432432
sentry_meta = getattr(span, "_sentry_meta", {})
433433
sentry_meta[key] = value
434434
span._sentry_meta = sentry_meta
435+
436+
437+
def get_profile_context(span):
438+
# type: (ReadableSpan) -> Optional[dict[str, str]]
439+
if not span.attributes:
440+
return None
441+
442+
profiler_id = cast("Optional[str]", span.attributes.get(SPANDATA.PROFILER_ID))
443+
if profiler_id is None:
444+
return None
445+
446+
return {"profiler_id": profiler_id}

‎sentry_sdk/tracing.py

Copy file name to clipboardExpand all lines: sentry_sdk/tracing.py
+5-8Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,10 +1549,10 @@ def set_thread(self, thread_id, thread_name):
15491549
if thread_name is not None:
15501550
self.set_data(SPANDATA.THREAD_NAME, thread_name)
15511551

1552-
def set_profiler_id(self, profiler_id):
1553-
# type: (Optional[str]) -> None
1554-
if profiler_id is not None:
1555-
self.set_data(SPANDATA.PROFILER_ID, profiler_id)
1552+
def update_active_thread(self):
1553+
# type: () -> None
1554+
thread_id, thread_name = get_current_thread_meta()
1555+
self.set_thread(thread_id, thread_name)
15561556

15571557
def set_http_status(self, http_status):
15581558
# type: (int) -> None
@@ -1576,6 +1576,7 @@ def finish(self, end_timestamp=None):
15761576

15771577
def to_json(self):
15781578
# type: () -> dict[str, Any]
1579+
# TODO-neel-potel for sampling context
15791580
pass
15801581

15811582
def get_trace_context(self):
@@ -1589,10 +1590,6 @@ def get_trace_context(self):
15891590

15901591
return get_trace_context(self._otel_span)
15911592

1592-
def get_profile_context(self):
1593-
# type: () -> Optional[ProfileContext]
1594-
pass
1595-
15961593
def set_context(self, key, value):
15971594
# type: (str, Any) -> None
15981595
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.