Skip to main content

gstreamer_tracing/tracer/
imp.rs

1use crate::callsite::GstCallsiteKind;
2use crate::log::span_quark;
3use gst::glib::Properties;
4use gst::{Buffer, FlowError, FlowSuccess, Pad, Tracer, glib, prelude::*, subclass::prelude::*};
5use std::{cell::RefCell, sync::Mutex};
6use tracing::{Callsite, Dispatch, Id, info, span::Attributes};
7
8struct EnteredSpan {
9    id: Id,
10    dispatch: Dispatch,
11}
12
13#[derive(Default)]
14struct Settings {
15    log_level: Option<String>,
16}
17
18#[derive(Properties)]
19#[properties(wrapper_type = super::TracingTracer)]
20pub struct TracingTracer {
21    span_stack: thread_local::ThreadLocal<RefCell<Vec<EnteredSpan>>>,
22    #[property(
23        name = "log-level",
24        get,
25        set = Self::set_log_level,
26        type = Option<String>,
27        member = log_level,
28        blurb = "GStreamer log level to integrate as tracing events",
29    )]
30    settings: Mutex<Settings>,
31}
32
33pub struct SpanPropagationTracer;
34
35unsafe fn propagate_attached_span(parent: &gst::Object, child: &gst::Object) {
36    let quark = *span_quark();
37    if let Some(span) = unsafe { parent.qdata::<tracing::Span>(quark) } {
38        unsafe {
39            child.set_qdata(quark, span.as_ref().clone());
40        }
41    }
42}
43
44impl TracingTracer {
45    fn set_log_level(&self, log_level: Option<String>) {
46        let mut settings = self.settings.lock().unwrap();
47        settings.log_level = log_level.clone();
48        if let Some(ref level) = log_level {
49            info!("Integrating `{level}` GStreamer logs as part of our tracing");
50            crate::integrate_events();
51            gst::log::remove_default_log_function();
52            gst::log::set_threshold_from_string(level, true);
53        }
54    }
55
56    fn push_span(&self, dispatch: Dispatch, attributes: Attributes) {
57        let span_id = dispatch.new_span(&attributes);
58        dispatch.enter(&span_id);
59        self.span_stack
60            .get_or(|| RefCell::new(Vec::new()))
61            .borrow_mut()
62            .push(EnteredSpan {
63                id: span_id,
64                dispatch,
65            })
66    }
67    fn pop_span(&self) -> Option<()> {
68        self.span_stack
69            .get_or(|| RefCell::new(Vec::new()))
70            .borrow_mut()
71            .pop()
72            .map(|span| {
73                span.dispatch.exit(&span.id);
74                span.dispatch.try_close(span.id);
75            })
76    }
77
78    fn pad_pre(&self, name: &'static str, pad: &Pad) {
79        let callsite = crate::callsite::DynamicCallsites::get().callsite_for(
80            tracing::Level::ERROR,
81            name,
82            name,
83            None,
84            None,
85            None,
86            GstCallsiteKind::Span,
87            &["gstpad.state", "gstpad.parent.name"],
88        );
89        let interest = callsite.interest();
90        if interest.is_never() {
91            return;
92        }
93        let meta = callsite.metadata();
94        let dispatch = tracing_core::dispatcher::get_default(move |dispatch| dispatch.clone());
95        if !dispatch.enabled(meta) {
96            return;
97        }
98        let gstpad_flags_value = Some(tracing_core::field::display(pad.pad_flags()));
99        let gstpad_parent = pad.parent_element();
100        let gstpad_parent_name_value = gstpad_parent.map(|p| p.name());
101        let gstpad_parent_name_value = gstpad_parent_name_value.as_ref().map(|n| n.as_str());
102        let fields = meta.fields();
103        let mut fields_iter = fields.into_iter();
104        let values = field_values![fields_iter =>
105            // /!\ /!\ /!\ Must be in the same order as the field list above /!\ /!\ /!\
106            "gstpad.flags" = gstpad_flags_value;
107            "gstpad.parent.name" = gstpad_parent_name_value;
108        ];
109        let valueset = fields.value_set(&values);
110        let attrs = tracing::span::Attributes::new_root(meta, &valueset);
111        self.push_span(dispatch, attrs);
112    }
113}
114
115#[glib::object_subclass]
116impl ObjectSubclass for TracingTracer {
117    const NAME: &'static str = "GstRsTracingTracer";
118    const ALLOW_NAME_CONFLICT: bool = true;
119    type Type = super::TracingTracer;
120    type ParentType = Tracer;
121
122    fn new() -> Self {
123        Self {
124            span_stack: thread_local::ThreadLocal::new(),
125            settings: Mutex::new(Settings::default()),
126        }
127    }
128}
129
130#[glib::derived_properties]
131impl ObjectImpl for TracingTracer {
132    fn constructed(&self) {
133        self.parent_constructed();
134        #[cfg(feature = "v1_30")]
135        self.register_hook(TracerHook::ObjectParentSet);
136        #[cfg(not(feature = "v1_30"))]
137        {
138            self.register_hook(TracerHook::ElementAddPad);
139            self.register_hook(TracerHook::BinAddPost);
140        }
141        self.register_hook(TracerHook::PadPushPost);
142        self.register_hook(TracerHook::PadPushPre);
143        self.register_hook(TracerHook::PadPushListPost);
144        self.register_hook(TracerHook::PadPushListPre);
145        self.register_hook(TracerHook::PadQueryPost);
146        self.register_hook(TracerHook::PadQueryPre);
147        self.register_hook(TracerHook::PadPushEventPost);
148        self.register_hook(TracerHook::PadPushEventPre);
149        self.register_hook(TracerHook::PadPullRangePost);
150        self.register_hook(TracerHook::PadPullRangePre);
151    }
152}
153
154impl GstObjectImpl for TracingTracer {}
155
156impl TracerImpl for TracingTracer {
157    const USE_STRUCTURE_PARAMS: bool = true;
158
159    #[cfg(not(feature = "v1_30"))]
160    fn element_add_pad(&self, _ts: u64, element: &gst::Element, pad: &gst::Pad) {
161        unsafe {
162            propagate_attached_span(element.upcast_ref(), pad.upcast_ref());
163        }
164    }
165
166    #[cfg(not(feature = "v1_30"))]
167    fn bin_add_post(&self, _ts: u64, bin: &gst::Bin, element: &gst::Element, _success: bool) {
168        unsafe {
169            propagate_attached_span(bin.upcast_ref(), element.upcast_ref());
170        }
171    }
172
173    #[cfg(feature = "v1_30")]
174    fn object_parent_set(&self, _ts: u64, obj: &gst::Object, parent: Option<&gst::Object>) {
175        if let Some(parent) = parent {
176            unsafe { propagate_attached_span(parent, obj) }
177        }
178    }
179
180    fn pad_push_pre(&self, _: u64, pad: &Pad, _: &Buffer) {
181        self.pad_pre("pad_push", pad);
182    }
183
184    fn pad_push_list_pre(&self, _: u64, pad: &Pad, _: &gst::BufferList) {
185        self.pad_pre("pad_push_list", pad);
186    }
187
188    fn pad_query_pre(&self, _: u64, pad: &Pad, _: &gst::QueryRef) {
189        self.pad_pre("pad_query", pad);
190    }
191
192    fn pad_push_event_pre(&self, _: u64, pad: &Pad, _: &gst::Event) {
193        self.pad_pre("pad_event", pad);
194    }
195
196    fn pad_pull_range_pre(&self, _: u64, pad: &Pad, _: u64, _: u32) {
197        self.pad_pre("pad_pull_range", pad);
198    }
199
200    fn pad_pull_range_post(&self, _: u64, _: &Pad, _: Result<&Buffer, FlowError>) {
201        self.pop_span();
202    }
203
204    fn pad_push_event_post(&self, _: u64, _: &Pad, _: bool) {
205        self.pop_span();
206    }
207
208    fn pad_push_list_post(&self, _: u64, _: &Pad, _: Result<FlowSuccess, FlowError>) {
209        self.pop_span();
210    }
211
212    fn pad_push_post(&self, _: u64, _: &Pad, _: Result<FlowSuccess, FlowError>) {
213        self.pop_span();
214    }
215
216    fn pad_query_post(&self, _: u64, _: &Pad, _: &gst::QueryRef, _: bool) {
217        self.pop_span();
218    }
219}
220
221impl super::TracingTracerImpl for TracingTracer {}
222
223#[glib::object_subclass]
224impl ObjectSubclass for SpanPropagationTracer {
225    const NAME: &'static str = "GstRsSpanPropagationTracer";
226    const ALLOW_NAME_CONFLICT: bool = true;
227    type Type = super::SpanPropagationTracer;
228    type ParentType = Tracer;
229
230    fn new() -> Self {
231        Self
232    }
233}
234
235impl ObjectImpl for SpanPropagationTracer {
236    fn constructed(&self) {
237        self.parent_constructed();
238        #[cfg(feature = "v1_30")]
239        self.register_hook(TracerHook::ObjectParentSet);
240        #[cfg(not(feature = "v1_30"))]
241        {
242            self.register_hook(TracerHook::ElementAddPad);
243            self.register_hook(TracerHook::BinAddPost);
244        }
245    }
246}
247
248impl GstObjectImpl for SpanPropagationTracer {}
249
250impl TracerImpl for SpanPropagationTracer {
251    #[cfg(not(feature = "v1_30"))]
252    fn element_add_pad(&self, _ts: u64, element: &gst::Element, pad: &gst::Pad) {
253        unsafe {
254            propagate_attached_span(element.upcast_ref(), pad.upcast_ref());
255        }
256    }
257
258    #[cfg(not(feature = "v1_30"))]
259    fn bin_add_post(&self, _ts: u64, bin: &gst::Bin, element: &gst::Element, _success: bool) {
260        unsafe {
261            propagate_attached_span(bin.upcast_ref(), element.upcast_ref());
262        }
263    }
264
265    #[cfg(feature = "v1_30")]
266    fn object_parent_set(&self, _ts: u64, obj: &gst::Object, parent: Option<&gst::Object>) {
267        if let Some(parent) = parent {
268            unsafe { propagate_attached_span(parent, obj) }
269        }
270    }
271}