Skip to main content

cf_rustracing/
lib.rs

1//! [OpenTracing][opentracing] API for Rust
2//!
3//! # Examples
4//!
5//! ```
6//! use cf_rustracing::sampler::AllSampler;
7//! use cf_rustracing::tag::Tag;
8//! use cf_rustracing::Tracer;
9//! use std::thread;
10//! use std::time::Duration;
11//!
12//! # #[tokio::main]
13//! # async fn main() {
14//! // Creates a tracer
15//! let (tracer, mut span_rx) = Tracer::new(AllSampler);
16//! {
17//!     // Starts "parent" span
18//!     let parent_span = tracer.span("parent").start_with_state(());
19//!     thread::sleep(Duration::from_millis(10));
20//!     {
21//!         // Starts "child" span
22//!         let mut child_span = tracer
23//!             .span("child_span")
24//!             .child_of(&parent_span)
25//!             .tag(Tag::new("key", "value"))
26//!             .start_with_state(());
27//!
28//!         child_span.log(|log| {
29//!             log.error().message("a log message");
30//!         });
31//!     } // The "child" span dropped and will be sent to `span_rx`
32//! } // The "parent" span dropped and will be sent to `span_rx`
33//!
34//! println!("# SPAN: {:?}", span_rx.recv().await);
35//! println!("# SPAN: {:?}", span_rx.recv().await);
36//! # }
37//! ```
38//!
39//! As an actual usage example of the crate and an implmentation of the [OpenTracing] API,
40//! it may be helpful to looking at [rustracing_jaeger] crate.
41//!
42//! # References
43//!
44//! - [The OpenTracing Semantic Specification (v1.1)][specification]
45//!
46//! [opentracing]: http://opentracing.io/
47//! [specification]: https://github.com/opentracing/specification/blob/master/specification.md
48//! [rustracing_jaeger]: https://github.com/sile/rustracing_jaeger
49#![warn(missing_docs)]
50#![allow(clippy::new_ret_no_self)]
51#[macro_use]
52extern crate trackable;
53
54pub use crate::error::{Error, ErrorKind};
55pub use crate::tracer::Tracer;
56
57pub mod carrier;
58pub mod convert;
59pub mod log;
60pub mod sampler;
61pub mod span;
62pub mod tag;
63
64mod error;
65mod tracer;
66
67/// This crate specific `Result` type.
68pub type Result<T> = std::result::Result<T, Error>;
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::sampler::AllSampler;
74    use crate::span::{FinishedSpan, RoutingMetadata, Span};
75    use crate::tag::{StdTag, Tag, TagValue};
76    use std::sync::atomic::{AtomicI64, Ordering};
77    use std::sync::Arc;
78    use std::thread;
79    use std::time::Duration;
80
81    #[derive(Debug)]
82    struct TestRouting {
83        zone_id: u64,
84        account_id: u64,
85    }
86
87    impl RoutingMetadata for TestRouting {
88        fn group_key(&self) -> String {
89            format!("{}|{}", self.zone_id, self.account_id)
90        }
91
92        fn encode(&self) -> String {
93            format!("zone={};account={}", self.zone_id, self.account_id)
94        }
95    }
96
97    #[tokio::test]
98    async fn it_works() {
99        let (tracer, mut span_rx) = Tracer::new(AllSampler);
100        {
101            let span = tracer.span("it_works").start_with_state(());
102            let mut child = span.child("child", |options| options.start_with_state(()));
103            child.set_tags(|| StdTag::peer_addr("127.0.0.1:80".parse().unwrap()));
104        }
105
106        let span = span_rx.recv().await.unwrap();
107        assert_eq!(span.operation_name(), "child");
108
109        let span = span_rx.recv().await.unwrap();
110        assert_eq!(span.operation_name(), "it_works");
111    }
112
113    #[tokio::test]
114    async fn example_code_works() {
115        // Creates a tracer
116        let (tracer, mut span_rx) = Tracer::new(AllSampler);
117        {
118            // Starts "parent" span
119            let parent_span = tracer.span("parent").start_with_state(());
120            thread::sleep(Duration::from_millis(10));
121            {
122                // Starts "child" span
123                let mut child_span = tracer
124                    .span("child_span")
125                    .child_of(&parent_span)
126                    .tag(Tag::new("key", "value"))
127                    .start_with_state(());
128
129                child_span.log(|log| {
130                    log.error().message("a log message");
131                });
132            } // The "child" span dropped and will be sent to `span_rx`
133        } // The "parent" span dropped and will be sent to `span_rx`
134
135        let span = span_rx.recv().await.unwrap();
136        assert_eq!(span.operation_name(), "child_span");
137
138        let span = span_rx.recv().await.unwrap();
139        assert_eq!(span.operation_name(), "parent");
140    }
141
142    #[test]
143    fn finish_callback() {
144        let span_counter = AtomicI64::new(1);
145        let finish_cb = move |span: &mut Span<()>| {
146            let call_num = span_counter.fetch_add(1, Ordering::Relaxed);
147            span.set_tag(|| Tag::new("callback-call", call_num));
148        };
149
150        let (tracer, mut span_rx) = Tracer::new(AllSampler);
151        {
152            let parent_span = tracer
153                .span("parent")
154                .finish_callback(finish_cb)
155                .start_with_state(());
156
157            let child_with_cb = parent_span.child("child_with_cb", |s| s.start_with_state(()));
158            let mut child_no_cb = parent_span.child("child_no_cb", |s| s.start_with_state(()));
159            child_no_cb.take_finish_callback();
160
161            drop(child_with_cb);
162            drop(child_no_cb);
163            drop(parent_span);
164        }
165
166        let find_span_counter = |s: &FinishedSpan<()>| match s
167            .tags()
168            .iter()
169            .find(|t| t.name() == "callback-call")?
170            .value()
171        {
172            TagValue::Integer(v) => Some(*v),
173            v => panic!("invalid `callback-call` tag value: {v:?}"),
174        };
175
176        let child_with_cb = span_rx.try_recv().unwrap();
177        assert_eq!(child_with_cb.operation_name(), "child_with_cb");
178        assert_eq!(find_span_counter(&child_with_cb), Some(1));
179
180        let child_no_cb = span_rx.try_recv().unwrap();
181        assert_eq!(child_no_cb.operation_name(), "child_no_cb");
182        assert_eq!(find_span_counter(&child_no_cb), None);
183
184        let parent_span = span_rx.try_recv().unwrap();
185        assert_eq!(parent_span.operation_name(), "parent");
186        assert_eq!(find_span_counter(&parent_span), Some(2));
187    }
188
189    #[test]
190    fn discarded_span_is_not_received() {
191        let (tracer, mut span_rx) = Tracer::new(AllSampler);
192        {
193            let parent = tracer.span("parent").start_with_state(());
194            let mut discarded = parent.child("discarded", |s| s.start_with_state(()));
195            let sibling = parent.child("sibling", |s| s.start_with_state(()));
196
197            discarded.discard();
198            discarded.set_tag(|| Tag::new("key", "value"));
199            discarded.log(|log| {
200                log.std().message("ignored");
201            });
202            assert!(!discarded.is_sampled());
203            assert!(discarded.context().is_none());
204
205            drop(discarded);
206            drop(sibling);
207            drop(parent);
208        }
209
210        assert_eq!(span_rx.try_recv().unwrap().operation_name(), "sibling");
211        assert_eq!(span_rx.try_recv().unwrap().operation_name(), "parent");
212        assert!(span_rx.try_recv().is_err());
213    }
214
215    #[test]
216    fn discarded_span_does_not_run_finish_callback() {
217        let calls = Arc::new(AtomicI64::new(0));
218        let (tracer, mut span_rx) = Tracer::new(AllSampler);
219        {
220            let calls = Arc::clone(&calls);
221            let mut span = tracer
222                .span("span")
223                .finish_callback(move |_: &mut Span<()>| {
224                    calls.fetch_add(1, Ordering::Relaxed);
225                })
226                .start_with_state(());
227
228            span.discard();
229            span.discard();
230        }
231
232        assert_eq!(calls.load(Ordering::Relaxed), 0);
233        assert!(span_rx.try_recv().is_err());
234    }
235
236    #[allow(dead_code)]
237    fn span_can_be_shared() {
238        fn trait_check<T: Send + Sync>() {}
239        trait_check::<Span<()>>();
240    }
241
242    #[test]
243    fn routing_inherited_by_children() {
244        let routing = Arc::new(TestRouting {
245            zone_id: 7,
246            account_id: 99,
247        });
248        let expected_group_key = routing.group_key();
249        let expected_encoded = routing.encode();
250
251        let (tracer, mut span_rx) = Tracer::new(AllSampler);
252        {
253            // Routing is set once, at construction, on the root only.
254            let parent = tracer
255                .span("parent")
256                .routing(routing.clone())
257                .start_with_state(());
258            let child = parent.child("child", |s| s.start_with_state(()));
259            let grandchild = child.child("grandchild", |s| s.start_with_state(()));
260
261            drop(grandchild);
262            drop(child);
263            drop(parent);
264        }
265
266        // Every span in the tree carries the same routing, without it being
267        // passed to each child.
268        for expected in ["grandchild", "child", "parent"] {
269            let span = span_rx.try_recv().unwrap();
270            assert_eq!(span.operation_name(), expected);
271
272            let r = span
273                .routing()
274                .unwrap_or_else(|| panic!("{expected} should inherit routing"));
275            assert_eq!(r.group_key(), expected_group_key);
276            assert_eq!(r.encode(), expected_encoded);
277        }
278    }
279
280    #[test]
281    fn routing_last_set_wins() {
282        let second = Arc::new(TestRouting {
283            zone_id: 2,
284            account_id: 2,
285        });
286        let expected_group_key = second.group_key();
287        let expected_encoded = second.encode();
288
289        let (tracer, mut span_rx) = Tracer::new(AllSampler);
290        {
291            // If the builder is called more than once, the last value wins.
292            let span = tracer
293                .span("span")
294                .routing(Arc::new(TestRouting {
295                    zone_id: 1,
296                    account_id: 1,
297                }))
298                .routing(second.clone())
299                .start_with_state(());
300            drop(span);
301        }
302
303        let span = span_rx.try_recv().unwrap();
304        let r = span.routing().expect("routing should be set");
305        assert_eq!(r.group_key(), expected_group_key);
306        assert_eq!(r.encode(), expected_encoded);
307    }
308}