Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(di): trigger probes #10942

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
26 changes: 13 additions & 13 deletions ddtrace/debugging/_origin/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path
import sys
from threading import current_thread
from time import monotonic_ns
from types import FrameType
from types import FunctionType
import typing as t
Expand Down Expand Up @@ -153,12 +154,20 @@ def __enter__(self):
s.set_tag_str("_dd.code_origin.frames.0.type", location.module)
s.set_tag_str("_dd.code_origin.frames.0.method", location.name)

return self

def _close_signal(self, retval=None, exc_info=(None, None, None)):
root = ddtrace.tracer.current_root_span()
span = ddtrace.tracer.current_span()
if root is None or span is None:
return

# Check if we have any level 2 debugging sessions running for the
# current trace
if any(s.level >= 2 for s in Session.from_trace()):
P403n1x87 marked this conversation as resolved.
Show resolved Hide resolved
# Create a snapshot
snapshot = Snapshot(
probe=location.probe,
probe=self.location.probe,
frame=self.__frame__,
thread=current_thread(),
trace_context=root,
Expand All @@ -172,20 +181,11 @@ def __enter__(self):
span.set_tag_str("_dd.code_origin.frames.0.snapshot_id", snapshot.uuid)

self.set("snapshot", snapshot)
self.set("start_time", compat.monotonic_ns())

return self

def _close_signal(self, retval=None, exc_info=(None, None, None)):
try:
snapshot: Snapshot = t.cast(Snapshot, self.get("snapshot"))
except KeyError:
# No snapshot was created
return
self.set("start_time", monotonic_ns())
P403n1x87 marked this conversation as resolved.
Show resolved Hide resolved

snapshot.do_exit(retval, exc_info, compat.monotonic_ns() - self.get("start_time"))
snapshot.do_exit(retval, exc_info, monotonic_ns() - self.get("start_time"))

self.collector.push(snapshot)
self.collector.push(snapshot)

def __return__(self, retval):
self._close_signal(retval=retval)
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/debugging/_probe/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

DEFAULT_PROBE_RATE = 5000.0
DEFAULT_SNAPSHOT_PROBE_RATE = 1.0
DEFAULT_TRIGGER_PROBE_RATE = 1.0
DEFAULT_TRIGGER_PROBE_RATE = 1.0 / 60.0 # 1 per minute
DEFAULT_PROBE_CONDITION_ERROR_RATE = 1.0 / 60 / 5


Expand Down
9 changes: 7 additions & 2 deletions ddtrace/debugging/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@

SessionId = str

DEFAULT_SESSION_LEVEL = 1


def _sessions_from_debug_tag(debug_tag: str) -> t.Generator["Session", None, None]:
for session in debug_tag.split("."):
ident, _, level = session.partition(":")
yield Session(ident=ident, level=int(level or 0))
yield Session(ident=ident, level=int(level or DEFAULT_SESSION_LEVEL))


def _sessions_to_debug_tag(sessions: t.Iterable["Session"]) -> str:
# TODO: Validate tag length
return ".".join(f"{session.ident}:{session.level}" for session in sessions)
return ".".join(
(f"{session.ident}:{session.level}" if session.level != DEFAULT_SESSION_LEVEL else session.ident)
for session in sessions
)


@dataclass
Expand Down
20 changes: 20 additions & 0 deletions ddtrace/debugging/_signal/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ def _rate_limit_exceeded(self) -> bool:

return exceeded

def _session_check(self) -> bool:
# Check that we emit signals from probes with a session ID only if the
# session is active. If the probe has no session ID, or the session ID
# is active, we can proceed with the signal emission.
session_id = self.probe.tags.get("sessionId")
if session_id is not None:
session = Session.lookup(session_id)
if session is None or session.level == 0:
return False
return True

@property
def session(self):
session_id = self.probe.tags.get("sessionId")
Expand All @@ -150,6 +161,9 @@ def line(self, scope: Mapping[str, Any]) -> None:
pass

def do_enter(self) -> None:
if not self._session_check():
return

if self._timing is not ProbeEvalTiming.ENTRY:
return

Expand All @@ -163,6 +177,9 @@ def do_enter(self) -> None:
self.enter(scope)

def do_exit(self, retval: Any, exc_info: ExcInfoType, duration: int) -> None:
if not self._session_check():
return

if self.state is not SignalState.NONE:
# The signal has already been handled and move to a final state
return
Expand Down Expand Up @@ -192,6 +209,9 @@ def do_exit(self, retval: Any, exc_info: ExcInfoType, duration: int) -> None:
self.state = SignalState.DONE

def do_line(self, global_limiter: Optional[RateLimiter] = None) -> None:
if not self._session_check():
return

frame = self.frame
scope = ChainMap(frame.f_locals, frame.f_globals)

Expand Down
6 changes: 3 additions & 3 deletions ddtrace/debugging/_signal/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def enter(self, scope: t.Mapping[str, t.Any]) -> None:
self._link_session()

def exit(self, retval: t.Any, exc_info: ExcInfoType, duration: float, scope: t.Mapping[str, t.Any]) -> None:
session = self.session
if session is not None:
session.unlink_from_trace(self.trace_context)
# DEV: We do not unlink on exit explicitly here. We let the weak
# reference to the context object do the job.
pass

def line(self, scope: t.Mapping[str, t.Any]):
self._link_session()
Expand Down
22 changes: 11 additions & 11 deletions ddtrace/propagation/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -976,24 +976,24 @@ class HTTPPropagator(object):
def _extract_configured_contexts_avail(normalized_headers):
contexts = []
styles_w_ctx = []
for prop_style in config._propagation_style_extract:
propagator = _PROP_STYLES[prop_style]
context = propagator._extract(normalized_headers)
# baggage is handled separately
if prop_style == _PROPAGATION_STYLE_BAGGAGE:
continue
if context:
contexts.append(context)
styles_w_ctx.append(prop_style)
if config._propagation_style_extract is not None:
for prop_style in config._propagation_style_extract:
if prop_style == _PROPAGATION_STYLE_BAGGAGE:
continue
propagator = _PROP_STYLES[prop_style]
context = propagator._extract(normalized_headers)
if context:
contexts.append(context)
styles_w_ctx.append(prop_style)
return contexts, styles_w_ctx

@staticmethod
def _resolve_contexts(contexts, styles_w_ctx, normalized_headers):
primary_context = contexts[0]
links = []

for context in contexts[1:]:
style_w_ctx = styles_w_ctx[contexts.index(context)]
for i, context in enumerate(contexts[1:], 1):
style_w_ctx = styles_w_ctx[i]
# encoding expects at least trace_id and span_id
if context.span_id and context.trace_id and context.trace_id != primary_context.trace_id:
links.append(
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/settings/code_origin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SpanCodeOriginConfig(En):
enabled = En.v(
bool,
"enabled",
default=True,
default=False,
help_type="Boolean",
help="Enable code origin for spans",
)
Expand Down
5 changes: 2 additions & 3 deletions tests/debugging/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

@pytest.fixture
def stuff():
was_loaded = False
if "tests.submod.stuff" in sys.modules:
was_loaded = True
was_loaded = "tests.submod.stuff" in sys.modules
if was_loaded:
del sys.modules["tests.submod.stuff"]

__import__("tests.submod.stuff")
Expand Down
15 changes: 14 additions & 1 deletion tests/debugging/signal/test_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

import mock

from ddtrace.debugging._probe.model import ProbeEvalTiming
from ddtrace.debugging._signal.collector import SignalCollector
from ddtrace.debugging._signal.log import LogSignal
from ddtrace.debugging._signal.model import SignalState
from ddtrace.debugging._signal.snapshot import Snapshot
from tests.debugging.utils import create_log_function_probe
from tests.debugging.utils import create_snapshot_line_probe


Expand Down Expand Up @@ -45,7 +47,18 @@ def has_message(self):

c = 0
for i in range(10):
mocked_signal = MockLogSignal(mock.Mock(), sys._getframe(), threading.current_thread())
mocked_signal = MockLogSignal(
create_log_function_probe(
probe_id="test",
template=None,
segments=[],
module="foo",
func_qname="bar",
evaluate_at=ProbeEvalTiming.ENTRY,
),
sys._getframe(),
threading.current_thread(),
)
mocked_signal.do_enter()

assert mocked_signal.enter_call_count == 1
Expand Down
14 changes: 11 additions & 3 deletions tests/debugging/test_session.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import pytest

from ddtrace.debugging._session import _sessions_from_debug_tag
from ddtrace.debugging._session import _sessions_to_debug_tag


def test_tag_identity():
@pytest.mark.parametrize(
"a,b",
[
("session1:1.session2:2", "session1.session2:2"),
("session1:0.session2:2", "session1:0.session2:2"),
P403n1x87 marked this conversation as resolved.
Show resolved Hide resolved
],
)
def test_tag_identity(a, b):
"""
Test that the combination of _sessions_from_debug_tag and
_sessions_to_debug_tag is the identity function.
"""
debug_tag = "session1:1,session2:2"
assert _sessions_to_debug_tag(_sessions_from_debug_tag(debug_tag)) == debug_tag
assert _sessions_to_debug_tag(_sessions_from_debug_tag(a)) == b
1 change: 1 addition & 0 deletions tests/debugging/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def trigger_probe_defaults(f):
def _wrapper(*args, **kwargs):
kwargs.setdefault("session_id", str(uuid.uuid4))
kwargs.setdefault("level", 0)
kwargs.setdefault("rate", DEFAULT_PROBE_RATE)
return f(*args, **kwargs)

return _wrapper
Expand Down
4 changes: 0 additions & 4 deletions tests/tracer/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ def test_eq(ctx1, ctx2):
Context(trace_id=123, span_id=321, dd_origin="synthetics", sampling_priority=2),
Context(trace_id=1234, span_id=321, dd_origin="synthetics", sampling_priority=2),
),
(
Context(trace_id=123, span_id=321, dd_origin="synthetics", sampling_priority=2),
Context(trace_id=123, span_id=3210, dd_origin="synthetics", sampling_priority=2),
),
(
Context(trace_id=123, span_id=321, dd_origin="synthetics", sampling_priority=2),
Context(trace_id=123, span_id=321, dd_origin="synthetics1", sampling_priority=2),
Expand Down
Loading