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