fastrace/
lib.rs

1// Copyright 2020 TiKV Project Authors. Licensed under Apache-2.0.
2
3//! `fastrace` is a high-performance, ergonomic, library-level timeline tracing library for Rust.
4//!
5//! Unlike most tracing libraries which are primarily designed for instrumenting executables,
6//! `fastrace` also accommodates the need for library instrumentation. It stands out due to
7//! its extreme lightweight and fast performance compared to other tracing libraries. Moreover,
8//! it has zero overhead when not enabled in the executable, making it a worry-free choice for
9//! libraries concerned about unnecessary performance loss.
10//!
11//! # Getting Started
12//!
13//! ## In Libraries
14//!
15//! Libraries should include `fastrace` as a dependency without enabling any extra features.
16//!
17//! ```toml
18//! [dependencies]
19//! fastrace = "0.7"
20//! ```
21//!
22//! Add a [`trace`] attribute to the function you want to trace. In this example, a
23//! [`SpanRecord`] will be collected every time the function is called, if a tracing context
24//! is set up by the caller.
25//!
26//! ```
27//! # struct HttpRequest;
28//! # struct Error;
29//! #[fastrace::trace]
30//! pub fn send_request(req: HttpRequest) -> Result<(), Error> {
31//!     // ...
32//!     # Ok(())
33//! }
34//! ```
35//!
36//! Libraries are able to set up an individual tracing context, regardless of whether
37//! the caller has set up a tracing context or not. This can be achieved by using
38//! [`Span::root()`] to start a new trace and [`Span::set_local_parent()`] to set up a
39//! local context for the current thread.
40//!
41//! The [`func_path!()`] macro can detect the function's full name, which is used as
42//! the name of the root span.
43//!
44//! ```
45//! use fastrace::prelude::*;
46//! # struct HttpRequest;
47//! # struct Error;
48//!
49//! pub fn send_request(req: HttpRequest) -> Result<(), Error> {
50//!     let root = Span::root(func_path!(), SpanContext::random());
51//!     let _guard = root.set_local_parent();
52//!
53//!     // ...
54//!     # Ok(())
55//! }
56//! ```
57//!
58//! ## In Applications
59//!
60//! Applications should include `fastrace` as a dependency with the `enable` feature
61//! set. To disable `fastrace` statically, simply remove the `enable` feature.
62//!
63//! ```toml
64//! [dependencies]
65//! fastrace = { version ="0.7", features = ["enable"] }
66//! ```
67//!
68//! Applications should initialize a [`Reporter`] implementation early in the program's runtime.
69//! Span records generated before the reporter is initialized will be ignored. Before
70//! terminating, [`flush()`] should be called to ensure all collected span records are reported.
71//!
72//! ```
73//! use fastrace::collector::Config;
74//! use fastrace::collector::ConsoleReporter;
75//! use fastrace::prelude::*;
76//!
77//! fn main() {
78//!     fastrace::set_reporter(ConsoleReporter, Config::default());
79//!
80//!     loop {
81//!         let root = Span::root("worker-loop", SpanContext::random());
82//!         let _guard = root.set_local_parent();
83//!
84//!         handle_request();
85//!         # break;
86//!     }
87//!
88//!     fastrace::flush();
89//! }
90//! # fn handle_request() {}
91//! ```
92//!
93//! # Key Concepts
94//!
95//! `fastrace` operates through three types: [`Span`], [`LocalSpan`], and [`Event`], each
96//! representing a different type of tracing record. The macro [`trace`] is available to
97//! manage these types automatically. For [`Future`] instrumentation, necessary utilities
98//! are provided by [`FutureExt`].
99//!
100//! ## Span
101//!
102//! A [`Span`] represents an individual unit of work. It contains:
103//! - A name
104//! - A start timestamp and duration
105//! - A set of key-value properties
106//! - A reference to a parent `Span`
107//!
108//! A new `Span` can be started through [`Span::root()`], requiring the trace id and the
109//! parent span id from a remote source. If there's no remote parent span, the parent span
110//! id is typically set to its default value of zero.
111//!
112//! Once we have the root `Span`, we can create a child `Span` using [`Span::enter_with_parent()`],
113//! thereby establishing the reference relationship between the spans.
114//!
115//! `Span` is thread-safe and can be sent across threads.
116//! ```
117//! use fastrace::collector::Config;
118//! use fastrace::collector::ConsoleReporter;
119//! use fastrace::prelude::*;
120//!
121//! fastrace::set_reporter(ConsoleReporter, Config::default());
122//!
123//! {
124//!     let root_span = Span::root("root", SpanContext::random());
125//!
126//!     {
127//!         let child_span = Span::enter_with_parent("a child span", &root_span);
128//!
129//!         // ...
130//!
131//!         // child_span ends here.
132//!     }
133//!
134//!     // root_span ends here.
135//! }
136//!
137//! fastrace::flush();
138//! ```
139//!
140//! Sometimes, passing a `Span` through a function to create a child `Span` can be inconvenient.
141//! We can employ a thread-local approach to avoid an explicit argument passing in the function.
142//! In fastrace, [`Span::set_local_parent()`] and [`Span::enter_with_local_parent()`] serve this
143//! purpose.
144//!
145//! [`Span::set_local_parent()`] method sets __a local context of the `Span`__ for the current
146//! thread. [`Span::enter_with_local_parent()`] accesses the parent `Span` from the local context
147//! and creates a child `Span` with it.
148//!
149//! ```
150//! use fastrace::prelude::*;
151//!
152//! {
153//!     let root_span = Span::root("root", SpanContext::random());
154//!     let _guard = root_span.set_local_parent();
155//!
156//!     foo();
157//!
158//!     // root_span ends here.
159//! }
160//!
161//! fn foo() {
162//!     // The parent of this span is `root`.
163//!     let _child_span = Span::enter_with_local_parent("a child span");
164//!
165//!     // ...
166//!
167//!     // _child_span ends here.
168//! }
169//! ```
170//!
171//! ## Local Span
172//!
173//! In a clear single-thread execution flow, where we can ensure that the `Span` does
174//! not cross threads, meaning:
175//! - The `Span` is not sent to or shared by other threads
176//! - In asynchronous code, the lifetime of the `Span` doesn't cross an `.await` point
177//!
178//! we can use `LocalSpan` as a substitute for `Span` to effectively reduce overhead
179//! and greatly enhance performance.
180//!
181//! However, there is a precondition: The creation of `LocalSpan` must take place
182//! within __a local context of a `Span`__, which is established by invoking the
183//! [`Span::set_local_parent()`] method.
184//!
185//! If the code spans multiple function calls, this isn't always straightforward to
186//! confirm if the precondition is met. As such, it's recommended to invoke
187//! [`Span::set_local_parent()`] immediately after the creation of `Span`.
188//!
189//! After __a local context of a `Span`__ is set using [`Span::set_local_parent()`],
190//! use [`LocalSpan::enter_with_local_parent()`] to start a `LocalSpan`, which then
191//! becomes the new local parent.
192//!
193//! If no local context is set, the [`LocalSpan::enter_with_local_parent()`] will do nothing.
194//! ```
195//! use fastrace::collector::Config;
196//! use fastrace::collector::ConsoleReporter;
197//! use fastrace::prelude::*;
198//!
199//! fastrace::set_reporter(ConsoleReporter, Config::default());
200//!
201//! {
202//!     let root = Span::root("root", SpanContext::random());
203//!     let _guard = root.set_local_parent();
204//!
205//!     {
206//!         // The parent of this span is `root`.
207//!         let _span1 = LocalSpan::enter_with_local_parent("a child span");
208//!
209//!         foo();
210//!     }
211//! }
212//!
213//! fn foo() {
214//!     // The parent of this span is `span1`.
215//!     let _span2 = LocalSpan::enter_with_local_parent("a child span of child span");
216//! }
217//!
218//! fastrace::flush();
219//! ```
220//!
221//! ## Event
222//!
223//! [`Event`] represents a single point in time where something occurred during the execution of a
224//! program.
225//!
226//! An `Event` can be seen as a log record attached to a span.
227//! ```
228//! use fastrace::collector::Config;
229//! use fastrace::collector::ConsoleReporter;
230//! use fastrace::prelude::*;
231//!
232//! fastrace::set_reporter(ConsoleReporter, Config::default());
233//!
234//! {
235//!     let root = Span::root("root", SpanContext::random());
236//!     let _guard = root.set_local_parent();
237//!
238//!     root.add_event(Event::new("event in root"));
239//!
240//!     {
241//!         let _span1 = LocalSpan::enter_with_local_parent("a child span");
242//!
243//!         LocalSpan::add_event(Event::new("event in span1"));
244//!     }
245//! }
246//!
247//! fastrace::flush();
248//! ```
249//!
250//! ## Macro
251//!
252//! The attribute-macro [`trace`] helps to reduce boilerplate.
253//!
254//! Note: For successful tracing a function using the [`trace`] macro, the function call should
255//! occur within __a local context of a `Span`__.
256//!
257//! For more detailed usage instructions, please refer to [`trace`].
258//!
259//! ```
260//! use fastrace::collector::Config;
261//! use fastrace::collector::ConsoleReporter;
262//! use fastrace::prelude::*;
263//! use pollster::block_on;
264//!
265//! #[trace]
266//! fn do_something(i: u64) {
267//!     std::thread::sleep(std::time::Duration::from_millis(i));
268//! }
269//!
270//! #[trace]
271//! async fn do_something_async(i: u64) {
272//!     futures_timer::Delay::new(std::time::Duration::from_millis(i)).await;
273//! }
274//!
275//! fastrace::set_reporter(ConsoleReporter, Config::default());
276//!
277//! {
278//!     let root = Span::root("root", SpanContext::random());
279//!     let _guard = root.set_local_parent();
280//!
281//!     do_something(100);
282//!
283//!     block_on(
284//!         async {
285//!             do_something_async(100).await;
286//!         }
287//!         .in_span(Span::enter_with_local_parent("aync_job")),
288//!     );
289//! }
290//!
291//! fastrace::flush();
292//! ```
293//!
294//! ## Reporter
295//!
296//! [`Reporter`] is responsible for reporting the span records to a remote agent,
297//! such as Jaeger.
298//!
299//! Executables should initialize a reporter implementation early in the program's
300//! runtime. Span records generated before the reporter is initialized will be ignored.
301//!
302//! For an easy start, `fastrace` offers a [`ConsoleReporter`] that prints span
303//! records to stderr. For more advanced use, crates like `fastrace-jaeger`, `fastrace-datadog`,
304//! and `fastrace-opentelemetry` are available.
305//!
306//! By default, the reporter is triggered every 500 milliseconds. The reporter can also be
307//! triggered manually by calling [`flush()`]. See [`Config`] for customizing the reporting
308//! behavior.
309//!
310//! ```
311//! use std::time::Duration;
312//!
313//! use fastrace::collector::Config;
314//! use fastrace::collector::ConsoleReporter;
315//!
316//! fastrace::set_reporter(
317//!     ConsoleReporter,
318//!     Config::default().report_interval(Duration::from_secs(1)),
319//! );
320//!
321//! fastrace::flush();
322//! ```
323//!
324//! # Performance
325//!
326//! `fastrace` is designed to be fast and lightweight, considering four scenarios:
327//!
328//! - **No Tracing**: If the feature `enable` is not set in the application, `fastrace` will be
329//!   completely optimized away from the final executable binary, achieving zero overhead.
330//!
331//! - **Sample Tracing**: If `enable` is set in the application, but only a small portion of the
332//!   traces are enabled via [`Span::root()`], while the other portions are started with
333//!   placeholders using [`Span::noop()`]. The overhead in this case is very small - merely an
334//!   integer load, comparison, and jump.
335//!
336//! - **Full Tracing with Tail Sampling**: If `enable` is set in the application, and all traces are
337//!   enabled, however, only a select few interesting tracing records (e.g., P99) are reported,
338//!   while normal traces are dismissed by using [`Span::cancel()`] to avoid being reported, the
339//!   overhead of collecting traces is still very small. This could be useful when you are
340//!   interested in examining program's tail latency.
341//!
342//! - **Full Tracing**: If `enable` is set in the application, and all traces are reported,
343//!   `fastrace` performs 10x to 100x faster than other tracing libraries in this case.
344//!
345//!
346//! [`Span`]: crate::Span
347//! [`LocalSpan`]: crate::local::LocalSpan
348//! [`SpanRecord`]: crate::collector::SpanRecord
349//! [`FutureExt`]: crate::future::FutureExt
350//! [`trace`]: crate::trace
351//! [`LocalCollector`]: crate::local::LocalCollector
352//! [`Span::root()`]: crate::Span::root
353//! [`Span::noop()`]: crate::Span::noop
354//! [`Span::cancel()`]: crate::Span::cancel
355//! [`Span::enter_with_parent()`]: crate::Span::enter_with_parent
356//! [`Span::set_local_parent()`]: crate::Span::set_local_parent
357//! [`LocalSpan::enter_with_local_parent()`]: crate::local::LocalSpan::enter_with_local_parent
358//! [`Event`]: crate::Event
359//! [`Reporter`]: crate::collector::Reporter
360//! [`ConsoleReporter`]: crate::collector::ConsoleReporter
361//! [`Config`]: crate::collector::Config
362//! [`Future`]: std::future::Future
363
364// Suppress a false-positive lint from clippy
365#![allow(clippy::needless_doctest_main)]
366#![cfg_attr(not(feature = "enable"), allow(dead_code))]
367#![cfg_attr(not(feature = "enable"), allow(unused_mut))]
368#![cfg_attr(not(feature = "enable"), allow(unused_imports))]
369#![cfg_attr(not(feature = "enable"), allow(unused_variables))]
370#![cfg_attr(target_family = "wasm", allow(dead_code))]
371
372pub mod collector;
373mod event;
374pub mod future;
375pub mod local;
376mod macros;
377mod span;
378#[doc(hidden)]
379pub mod util;
380
381pub use fastrace_macro::trace;
382
383pub use crate::collector::global_collector::flush;
384pub use crate::collector::global_collector::set_reporter;
385pub use crate::event::Event;
386pub use crate::span::Span;
387
388pub mod prelude {
389    //! A "prelude" for crates using `fastrace`.
390    #[doc(no_inline)]
391    pub use crate::collector::SpanContext;
392    #[doc(no_inline)]
393    pub use crate::collector::SpanId;
394    #[doc(no_inline)]
395    pub use crate::collector::SpanRecord;
396    #[doc(no_inline)]
397    pub use crate::collector::TraceId;
398    #[doc(no_inline)]
399    pub use crate::event::Event;
400    #[doc(no_inline)]
401    pub use crate::file_location;
402    #[allow(deprecated)]
403    #[doc(no_inline)]
404    pub use crate::full_name;
405    #[doc(no_inline)]
406    pub use crate::func_name;
407    #[doc(no_inline)]
408    pub use crate::func_path;
409    #[doc(no_inline)]
410    pub use crate::future::FutureExt as _;
411    #[doc(no_inline)]
412    pub use crate::local::LocalSpan;
413    #[doc(no_inline)]
414    pub use crate::span::Span;
415    #[doc(no_inline)]
416    pub use crate::trace;
417}