Expand description
minitrace
is a high-performance, ergonomic, library-level timeline tracing library for Rust.
Unlike most tracing libraries which are primarily designed for instrumenting executables,
minitrace
also accommodates the need for library instrumentation. It stands out due to
its extreme lightweight and fast performance compared to other tracing libraries. Moreover,
it has zero overhead when not enabled in the executable, making it a worry-free choice for
libraries concerned about unnecessary performance loss.
Getting Started
Libraries
Libraries should include minitrace
as a dependency without enabling any extra features.
[dependencies]
minitrace = "0.5"
Libraries can attach their spans to the caller’s span (if available) via the API boundary.
use minitrace::prelude::*;
struct Connection {
// ...
}
impl Connection {
#[trace]
pub fn query(sql: &str) -> Result<QueryResult, Error> {
// ...
}
}
Libraries can also create a new trace individually to record their work.
use minitrace::prelude::*;
pub fn send_request(req: HttpRequest) -> Result<(), Error> {
let root = Span::root("send_request", SpanContext::random());
let _guard = root.set_local_parent();
// ...
}
Executables
Executables should include minitrace
as a dependency with the enable
feature
set. To disable minitrace
statically, simply don’t set the enable
feature.
[dependencies]
minitrace = { version = "0.5", features = ["enable"] }
Executables should initialize a reporter implementation early in the program’s runtime. Span records generated before the implementation is initialized will be ignored. Before terminating, the reporter should be flushed to ensure all span records are reported.
use minitrace::collector::Config;
use minitrace::collector::ConsoleReporter;
fn main() {
minitrace::set_reporter(ConsoleReporter, Config::default());
// ...
minitrace::flush();
}
Key Concepts
minitrace
operates through three types: Span
, LocalSpan
, and Event
, each
representing a different type of tracing record. The macro trace
is available to
manage these types automatically. For Future
instrumentation, necessary utilities
are provided by FutureExt
.
Span
A Span
represents an individual unit of work. It contains:
- A name
- A start timestamp and duration
- A set of key-value properties
- A reference to a parent
Span
A new Span
can be started through Span::root()
, requiring the trace id and the
parent span id from a remote source. If there’s no remote parent span, the parent span
id is typically set to its default value of zero.
Span::enter_with_parent()
starts a child span given a parent span.
Span
is thread-safe and can be sent across threads.
use minitrace::collector::Config;
use minitrace::collector::ConsoleReporter;
use minitrace::prelude::*;
minitrace::set_reporter(ConsoleReporter, Config::default());
{
let root_span = Span::root("root", SpanContext::random());
{
let child_span = Span::enter_with_parent("a child span", &root_span);
// ...
// child_span ends here.
}
// root_span ends here.
}
minitrace::flush();
Local Span
A Span
can be efficiently replaced with a LocalSpan
, reducing overhead
significantly, provided it is not intended for sending to other threads.
Before starting a LocalSpan
, a scope of parent span should be set using
Span::set_local_parent()
. Use LocalSpan::enter_with_local_parent()
to start
a LocalSpan
, which then becomes the new local parent.
If no local parent is set, the enter_with_local_parent()
will do nothing.
use minitrace::collector::Config;
use minitrace::collector::ConsoleReporter;
use minitrace::prelude::*;
minitrace::set_reporter(ConsoleReporter, Config::default());
{
let root = Span::root("root", SpanContext::random());
{
let _guard = root.set_local_parent();
// The parent of this span is `root`.
let _span1 = LocalSpan::enter_with_local_parent("a child span");
foo();
}
}
fn foo() {
// The parent of this span is `span1`.
let _span2 = LocalSpan::enter_with_local_parent("a child span of child span");
}
minitrace::flush();
Event
Event
represents a single point in time where something occurred during the execution of a program.
An Event
can be seen as a log record attached to a span.
use minitrace::collector::Config;
use minitrace::collector::ConsoleReporter;
use minitrace::prelude::*;
minitrace::set_reporter(ConsoleReporter, Config::default());
{
let root = Span::root("root", SpanContext::random());
Event::add_to_parent("event in root", &root, || []);
{
let _guard = root.set_local_parent();
let _span1 = LocalSpan::enter_with_local_parent("a child span");
Event::add_to_local_parent("event in span1", || [("key".into(), "value".into())]);
}
}
minitrace::flush();
Macro
The attribute-macro trace
helps to reduce boilerplate. However, the function annotated
by the trace
always requires a local parent in the context, otherwise, no span will be
recorded.
use futures::executor::block_on;
use minitrace::collector::Config;
use minitrace::collector::ConsoleReporter;
use minitrace::prelude::*;
#[trace]
fn do_something(i: u64) {
std::thread::sleep(std::time::Duration::from_millis(i));
}
#[trace]
async fn do_something_async(i: u64) {
futures_timer::Delay::new(std::time::Duration::from_millis(i)).await;
}
minitrace::set_reporter(ConsoleReporter, Config::default());
{
let root = Span::root("root", SpanContext::random());
let _guard = root.set_local_parent();
do_something(100);
block_on(
async {
do_something_async(100).await;
}
.in_span(Span::enter_with_local_parent("aync_job")),
);
}
minitrace::flush();
Reporter
Reporter
is responsible for reporting the span records to a remote agent,
such as Jaeger.
Executables should initialize a reporter implementation early in the program’s runtime. Span records generated before the implementation is initialized will be ignored.
For an easy start, minitrace
offers a ConsoleReporter
that prints span
records to stderr. For more advanced use, crates like minitrace-jaeger
, minitrace-datadog
,
and minitrace-opentelemetry
are available.
By default, the reporter is triggered every 500 milliseconds. The reporter can also be
triggered manually by calling flush()
. See Config
for customizing the reporting behavior.
use std::time::Duration;
use minitrace::collector::Config;
use minitrace::collector::ConsoleReporter;
minitrace::set_reporter(
ConsoleReporter,
Config::default().batch_report_interval(Duration::from_secs(1)),
);
minitrace::flush();
Performance
minitrace
is designed to be fast and lightweight, considering four scenarios:
-
No Tracing:
minitrace
is not included as dependency in the executable, while the libraries has been intrumented. In this case, it will be completely removed from libraries, causing zero overhead. -
Sample Tracing:
minitrace
is enabled in the executable, but only a small portion of the traces are enabled viaSpan::root()
, while the other portion start with placeholders bySpan::noop()
. The overhead in this case is very small - merely an integer load, comparison, and jump. -
Full Tracing with Tail Sampling:
minitrace
is enabled in the executable, and all traces are enabled. However, only a select few abnormal tracing records (e.g., P99) are reported. Normal traces can be dismissed by usingSpan::cancel()
to avoid reporting. This could be useful when you are interested in examining program’s tail latency. -
Full Tracing:
minitrace
is enabled in the executable, and all traces are enabled. All tracing records are reported.minitrace
performs 10x to 100x faster than other tracing libraries in this case.
Modules
- Collector and the collected spans.
- This module provides tools to trace a
Future
. - Non thread-safe span with low overhead.
- A “prelude” for crates using
minitrace
.
Structs
- An event that represents a single point in time during the execution of a span.
- A thread-safe span.
Functions
- Flushes all pending span records to the reporter immediately.
- Sets the reporter and its configuration for the current application.
Attribute Macros
- An attribute macro designed to eliminate boilerplate code.