Skip to content

Commit 9c1a22c

Browse files
committed
chore(internal): add native based event system
1 parent c3b26e9 commit 9c1a22c

File tree

13 files changed

+753
-5
lines changed

13 files changed

+753
-5
lines changed

.gitlab/native.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ include:
2121
echo -e "\e[0Ksection_start:`date +%s`:cargo_test[collapsed=true]\r\e[0Kcargo test"
2222
cargo test --no-fail-fast --locked
2323
echo -e "\e[0Ksection_end:`date +%s`:cargo_test\r\e[0K"
24+
- |
25+
echo -e "\e[0Ksection_start:`date +%s`:check_events_pyi[collapsed=true]\r\e[0Kcheck events.pyi"
26+
cargo build
27+
git diff --exit-code ../../ddtrace/internal/events.pyi || (echo "Error: events.pyi is out of date. Run 'cargo build' in src/native/ and commit the changes." && exit 1)
28+
echo -e "\e[0Ksection_end:`date +%s`:check_events_pyi\r\e[0K"

ddtrace/_trace/events.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
Trace-specific events.
3+
4+
This module re-exports trace-related events from the native event system
5+
with domain-specific type annotations.
6+
"""
7+
8+
from ddtrace.internal import events
9+
10+
11+
tracer_span_started = events.tracer_span_started
12+
tracer_span_finished = events.tracer_span_finished
13+
14+
__all__ = [
15+
"tracer_span_started",
16+
"tracer_span_finished",
17+
]

ddtrace/_trace/events.pyi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""
2+
Type stubs for trace-specific events with concrete type annotations.
3+
"""
4+
5+
from .span import Span
6+
from ddtrace.internal.events import Event
7+
8+
# Trace events with concrete types
9+
tracer_span_started: Event[[Span]]
10+
tracer_span_finished: Event[[Span]]

ddtrace/_trace/tracer.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from typing import cast
1717

1818
from ddtrace._trace.context import Context
19+
from ddtrace._trace.events import tracer_span_finished
20+
from ddtrace._trace.events import tracer_span_started
1921
from ddtrace._trace.processor import SpanAggregator
2022
from ddtrace._trace.processor import SpanProcessor
2123
from ddtrace._trace.processor import TopLevelSpanProcessor
@@ -30,7 +32,6 @@
3032
from ddtrace.constants import PID
3133
from ddtrace.constants import VERSION_KEY
3234
from ddtrace.internal import atexit
33-
from ddtrace.internal import core
3435
from ddtrace.internal import debug
3536
from ddtrace.internal import forksafe
3637
from ddtrace.internal import hostname
@@ -205,7 +206,8 @@ def on_start_span(self, func: Callable[[Span], None]) -> Callable[[Span], None]:
205206
:param func: The function to call when starting a span.
206207
The started span will be passed as argument.
207208
"""
208-
core.on("trace.span_start", callback=func)
209+
handler = tracer_span_started.on(func)
210+
setattr(func, "_tracer_span_started_handler", handler)
209211
return func
210212

211213
def deregister_on_start_span(self, func: Callable[[Span], None]) -> Callable[[Span], None]:
@@ -215,7 +217,9 @@ def deregister_on_start_span(self, func: Callable[[Span], None]) -> Callable[[Sp
215217
216218
:param func: The function to stop calling when starting a span.
217219
"""
218-
core.reset_listeners("trace.span_start", callback=func)
220+
handler = getattr(func, "_tracer_span_started_handler", None)
221+
if handler:
222+
tracer_span_started.remove(handler)
219223
return func
220224

221225
def sample(self, span):
@@ -566,7 +570,7 @@ def _start_span(
566570
for p in chain(self._span_processors, SpanProcessor.__processors__, [self._span_aggregator]):
567571
if p:
568572
p.on_span_start(span)
569-
core.dispatch("trace.span_start", (span,))
573+
tracer_span_started.dispatch(span)
570574
return span
571575

572576
start_span = _start_span
@@ -584,7 +588,7 @@ def _on_span_finish(self, span: Span) -> None:
584588
if p:
585589
p.on_span_finish(span)
586590

587-
core.dispatch("trace.span_finish", (span,))
591+
tracer_span_finished.dispatch(span)
588592
log.debug("finishing span - %r (enabled:%s)", span, self.enabled)
589593

590594
def _log_compat(self, level, msg):

ddtrace/internal/events.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Event Hub - High-performance event system for ddtrace
3+
4+
This module provides a clean Python interface to the native event system,
5+
allowing both Python and Rust code to register listeners and fire events
6+
with minimal overhead.
7+
8+
Usage:
9+
from ddtrace.internal.events import trace_span_started
10+
11+
# Register a listener
12+
def my_handler(span):
13+
print(f"Span started: {span}")
14+
15+
handle = trace_span_started.on(my_handler)
16+
17+
# Fire an event
18+
trace_span_started.dispatch(some_span)
19+
20+
# Remove listener
21+
trace_span_started.remove(handle)
22+
23+
# Check if any listeners exist
24+
if trace_span_started.has_listeners():
25+
trace_span_started.dispatch(span)
26+
"""
27+
28+
from ddtrace.internal.native._native import events as _events
29+
30+
31+
# Re-export everything from the native events submodule
32+
__all__ = _events.__all__
33+
34+
# Dynamically add all event objects to this module's namespace
35+
for name in _events.__all__:
36+
locals()[name] = getattr(_events, name)

ddtrace/internal/events.pyi

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# This file is automatically generated during compilation of src/native/
2+
# Do not edit manually - changes will be overwritten
3+
# Generated by build.rs from define_event! macro calls
4+
# Any modifications should be committed to the repository after regeneration
5+
# mypy: disable-error-code="name-defined"
6+
7+
from typing import Callable, ParamSpec, Protocol
8+
9+
P = ParamSpec('P')
10+
11+
class ListenerHandle:
12+
"""Handle for registered event listeners"""
13+
id: int
14+
event_id: str
15+
def __str__(self) -> str: ...
16+
def __repr__(self) -> str: ...
17+
18+
class Event(Protocol[P]):
19+
"""Generic event protocol with strongly typed dispatch and listeners"""
20+
def dispatch(self, *args: P.args, **kwargs: P.kwargs) -> None:
21+
"""Fire the event to all registered listeners"""
22+
...
23+
24+
def on(self, callback: Callable[P, None]) -> ListenerHandle:
25+
"""Register a listener callback for this event"""
26+
...
27+
28+
def remove(self, handle: ListenerHandle) -> bool:
29+
"""Remove a specific listener by handle"""
30+
...
31+
32+
def has_listeners(self) -> bool:
33+
"""Check if any listeners are registered for this event"""
34+
...
35+
36+
# Event instances
37+
38+
tracer_span_started: Event[["ddtrace._trace.span.Span"]] # noqa: F821
39+
tracer_span_finished: Event[["ddtrace._trace.span.Span"]] # noqa: F821

src/native/Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/native/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ profiling = ["dep:datadog-profiling-ffi"]
1616

1717
[dependencies]
1818
anyhow = { version = "1.0", optional = true }
19+
ctor = "0.2"
1920
datadog-crashtracker = { git = "https://github.com/DataDog/libdatadog", rev = "v21.0.0", optional = true }
2021
datadog-ddsketch = { git = "https://github.com/DataDog/libdatadog", rev = "v21.0.0" }
2122
datadog-library-config = { git = "https://github.com/DataDog/libdatadog", rev = "v21.0.0" }
@@ -25,14 +26,18 @@ datadog-profiling-ffi = { git = "https://github.com/DataDog/libdatadog", rev = "
2526
"cbindgen",
2627
] }
2728
ddcommon = { git = "https://github.com/DataDog/libdatadog", rev = "v21.0.0" }
29+
paste = "1.0"
2830
pyo3 = { version = "0.25", features = ["extension-module", "anyhow"] }
2931
tracing = { version = "0.1", default-features = false }
3032

3133
[build-dependencies]
34+
glob = "0.3"
35+
proc-macro2 = "1.0"
3236
pyo3-build-config = "0.25"
3337
build_common = { git = "https://github.com/DataDog/libdatadog", rev = "v21.0.0", features = [
3438
"cbindgen",
3539
] }
40+
syn = { version = "2.0", features = ["full", "extra-traits", "visit"] }
3641

3742
[lib]
3843
name = "_native"

0 commit comments

Comments
 (0)