Skip to content

Commit 9eb932e

Browse files
authored
chore(co): handle view methods (#15268)
## Description We extend support in Code Origin for Spans to handle view methods, in addition to functions.
1 parent 38a7e70 commit 9eb932e

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

ddtrace/debugging/_origin/span.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from time import monotonic_ns
66
from types import FrameType
77
from types import FunctionType
8+
from types import MethodType
89
import typing as t
910
import uuid
1011

@@ -210,8 +211,11 @@ class SpanCodeOriginProcessorEntry:
210211
_lock = Lock()
211212

212213
@classmethod
213-
def instrument_view(cls, f):
214+
def instrument_view(cls, f: t.Union[FunctionType, MethodType]) -> None:
215+
if isinstance(f, MethodType):
216+
f = t.cast(FunctionType, f.__func__)
214217
if not _isinstance(f, FunctionType):
218+
log.warning("Cannot instrument view %r: not a function", f)
215219
return
216220

217221
with cls._lock:

ddtrace/debugging/_products/code_origin/span.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import enum
22
from functools import partial
3+
from types import FunctionType
4+
from types import MethodType
35
import typing as t
46

57
import ddtrace.internal.core as core
@@ -32,7 +34,7 @@ def start():
3234
# We need to instrument the entrypoints on boot because this is the only
3335
# time the tracer will notify us of entrypoints being registered.
3436
@partial(core.on, "service_entrypoint.patch")
35-
def _(f: t.Callable) -> None:
37+
def _(f: t.Union[FunctionType, MethodType]) -> None:
3638
from ddtrace.debugging._origin.span import SpanCodeOriginProcessorEntry
3739

3840
SpanCodeOriginProcessorEntry.instrument_view(f)

tests/debugging/origin/test_span.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,31 @@ def entry_call():
169169
assert _exit.get_tag("_dd.code_origin.type") is None
170170
assert _exit.get_tag("_dd.code_origin.frames.0.file") is None
171171
assert _exit.get_tag("_dd.code_origin.frames.0.line") is None
172+
173+
def test_span_origin_entry_method(self):
174+
class App:
175+
def entry_call(self):
176+
pass
177+
178+
app = App()
179+
180+
core.dispatch("service_entrypoint.patch", (app.entry_call,))
181+
182+
with self.tracer.trace("entry"):
183+
app.entry_call()
184+
with self.tracer.trace("middle"):
185+
with self.tracer.trace("exit", span_type=SpanTypes.HTTP):
186+
pass
187+
188+
self.assert_span_count(3)
189+
entry, *_ = self.get_spans()
190+
191+
# Check for the expected tags on the entry span
192+
assert entry.get_tag("_dd.code_origin.type") == "entry"
193+
assert entry.get_tag("_dd.code_origin.frames.0.file") == str(Path(__file__).resolve())
194+
assert entry.get_tag("_dd.code_origin.frames.0.line") == str(App.entry_call.__code__.co_firstlineno)
195+
assert entry.get_tag("_dd.code_origin.frames.0.type") == __name__
196+
assert (
197+
entry.get_tag("_dd.code_origin.frames.0.method")
198+
== "SpanProbeTestCase.test_span_origin_entry_method.<locals>.App.entry_call"
199+
)

0 commit comments

Comments
 (0)