gstreamer_tracing/tracer/
imp.rs1use 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 "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}