Library / SDK
fast/fastrace avatar
fast/fastrace

fastrace: A High Performance Tracing Library for Rust

A tracing library 10~100x faster than others.

1,145 stars57 forksRustApache-2.0

At a glance

What is it?
fastrace is a Rust tracing library built around spans, with a feature flag that keeps the cost near zero inside libraries and switches instrumentation on in the application, a console reporter and an OpenTelemetry exporter, and integrations for futures, axum, poem, reqwest, tonic, tower and tokio-tracing.
Who is it for?
fastrace fits a Rust service that wants span level tracing without the overhead that makes tracing libraries get switched off in production, and that needs its tracing to be visible from inside third party libraries rather than only at the application edges.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Spans First, With a Feature Flag That Decides Who Pays

fastrace is a tracing library, and the project describes it as 100x faster than others, with a benchmark chart in the repository to back the claim. The features list is short: extremely fast, rich features for logging, and compatible with OpenTelemetry.

What the design does with a span is the more interesting part. A span is a unit of work with a beginning and an end, and the library collects span records rather than emitting individual events. That difference is where the performance comes from, and it is also what shapes the API.

The instrumented code looks like this:

rust
#[fastrace::trace]
pub fn send_request(req: HttpRequest) -> Result<(), Error> {
    // ...
}

A SpanRecord will be collected every time the function is called, if the caller sets up a tracing context. That conditional is the key point. If nobody is listening, the attribute is close to free, so a library can be instrumented without imposing a cost on the applications that use it and do not care about tracing.

Libraries can also set up an individual tracing context regardless of whether the caller has already set one, by starting a root span with Span::root() and setting a local context for the current thread with Span::set_local_parent(). The func_path!() macro detects the function's full name, which is used as the name of the root span.

Libraries Depend Plainly, Applications Enable the Feature

The dependency setup is where the performance claim is operationalised, and it is worth reading carefully because it decides who carries the cost.

Libraries should include fastrace as a dependency without enabling any extra features:

toml
[dependencies]
fastrace = "0.7"

Applications should include fastrace as a dependency with the enable feature set, and to disable fastrace statically you remove the enable feature:

toml
[dependencies]
fastrace = { version = "0.7", features = ["enable"] }

So the attribute macro in a library is inert until the final binary turns it on. A dependency graph where a dozen libraries each pull in fastrace without the feature costs almost nothing, and the moment the application crate enables it, the instrumentation is live across the whole graph.

The application side has one more obligation. A Reporter implementation should be initialised early in the program's runtime, and span records generated before the reporter is initialised will be ignored. Before terminating, flush() should be called to ensure all collected span records are reported.

That early-initialisation requirement is the failure mode people hit. A span created in a static initialiser or in the first moments of startup is dropped into a void, and the symptom is a missing span rather than an error, which is annoying to debug.

Root Spans Are the Reporting Boundary

There is one rule in the model that determines whether you see a trace at all, and the README states it in the section about applications.

When the root span is dropped, all of its children spans and itself will be reported at once. Since then, it is recommended to create root spans for short tasks, such as handling a request. Otherwise, an endless trace will never be reported.

The mechanism is straightforward. A span record is not emitted when the span is closed, it is emitted when the root of the tree is closed, and the tree is reported as a whole. That is what makes the collection cheap, since the intermediate spans are just structured data that gets assembled into one record.

The consequence is a design constraint on your application. If you create one root span at the start of the process and keep it alive for the lifetime of the program, nothing is ever reported, because the root never drops. The pattern the README shows is a loop, where each iteration creates a root span for a unit of work:

rust
let root = Span::root("worker-loop", SpanContext::random());
let _guard = root.set_local_parent();

and the loop body handles the request, so each request becomes its own reported tree.

For a server, that means one root span per request or per job. For a worker that pulls from a queue, one root span per item. The rule to remember is that the root span's lifetime should match the lifetime of the thing you want to see in the output.

Two Reporters: Console and OpenTelemetry

Reporting is separated from collection, and fastrace ships the reporters as separate crates.

The console reporter is in the main crate and is what the example uses. The application sets it with a single call:

rust
fastrace::set_reporter(ConsoleReporter, Config::default());

The OpenTelemetry exporter is a separate crate, fastrace-opentelemetry, published on crates.io, and it exports the spans to OpenTelemetry. The point of a separate crate is that the OpenTelemetry dependency, with its SDK, its OTLP exporter and its gRPC transport, is not pulled into applications that only want the console output.

The examples directory shows the range of what is supported: a console-reporter example, an opentelemetry-reporter example, a log-integration example, a sampling example, a fastrace-disabled example and a test-harness-integration example.

Two of those deserve a mention. The sampling example is where you decide how much of the traffic to keep, which is the usual answer to a tracing cost concern in production. And the log-integration example connects fastrace to the log crate, which is what the rich features for logging item in the feature list refers to, so that a span and its log lines appear together rather than in two separate streams.

Integrations Cover the Common Async Stack

Context propagation is where tracing libraries usually fall short, because the context has to cross await points and framework boundaries, and each of those is a place where the context can be lost.

fastrace publishes a crate per integration. fastrace-futures traces a Stream from the futures crate. fastrace-axum traces axum HTTP services and fastrace-poem traces poem HTTP services, so both common Rust web frameworks are covered. fastrace-reqwest traces reqwest HTTP requests, fastrace-tonic traces tonic client and server requests, and fastrace-tower traces tower client and server requests, which covers the layer ecosystem generally.

The purpose of all of them is the same, and the README says it: to handle context propagation automatically. A request handled by axum carries its span into the reqwest call it makes, and into the tonic call, without the handler code having to thread a context object through by hand.

For a team, the practical value of this list is that it means the tracing works across the service boundary in the code as well as in the process, so a trace shows the whole path rather than stopping at each library boundary.

Coming From the tokio-tracing Ecosystem

The migration path is the part of the README that speaks to an existing codebase, and it is handled by a compatibility crate rather than by a rewrite.

If you are using the tokio-tracing ecosystem and want to switch to fastrace for better performance, you can use fastrace-tracing to make the transition easier. The crate provides a compatibility layer that lets you capture spans from libraries instrumented with tokio-tracing in two lines of code:

rust
let subscriber = tracing_subscriber::Registry::default().with(fastrace_tracing::FastraceCompatLayer::new());
tracing::subscriber::set_global_default(subscriber).unwrap();

So the standard tracing subscriber is set up with a fastrace compatibility layer, and the spans that existing tokio-tracing instrumented libraries produce are captured by fastrace rather than by a tracing subscriber. That is what makes an incremental migration possible: you leave the instrumented dependencies alone and change the subscriber.

The README keeps the migration guide under the heading of migrating from tracing, and the resources list also points to a FAQ with a section explaining what library-level tracing means, which is a term the project uses for the capability of incorporating tracing inside libraries rather than restricting it to the application or system level. The FAQ is also where the project answers why it is fast, pointing to an article on the design of a high performance tracing library in Rust and a piece on tracing a key value database with less than five percent performance impact.

Benchmarks, the Workspace Layout and the MSRV

The performance claim is backed by material in the repository rather than left as an adjective. The README links to a benchmark chart image under the benches/media directory, and the repository has a benches/ directory alongside the main workspace. The project also publishes an article about tracing a key value database with less than five percent performance impact, which is a more useful number for a service owner than a synthetic comparison.

The repository is a Cargo workspace. The root Cargo.toml excludes benches and examples from the workspace and includes crates/*, tests-build and tests-integration as members, with resolver 3 and edition 2024. The crates/ directory holds the individual published crates, and the workspace dependency section pins the shared versions, with fastrace and fastrace-macro at 0.7.19.

The dependency set shows the implementation choices. parking_lot and pin-project handle the locking and the projection, rtrb is the ring buffer, rand generates the random span contexts, serde derives the span serialisation, and log is the bridge to the log crate. The OpenTelemetry stack, with opentelemetry at 0.32 and the OTLP exporter, is only in the workspace dependencies for the exporter crate.

The supported Rust version is 1.91.0, and the README says Fastrace is built against the latest stable release and that the current version is not guaranteed to build on earlier versions. That is a recent minimum, so a project on an older toolchain has to upgrade Rust first. The project is Apache-2.0 licensed, publishes no GitHub releases, and the last push was on 2026-10-17.

Editorial conclusion

fastrace fits a Rust service that wants span level tracing without the overhead that makes tracing libraries get switched off in production, and that needs its tracing to be visible from inside third party libraries rather than only at the application edges. It is a poor fit if you are on a Rust toolchain older than 1.91.0, since that is the stated minimum supported version, and a poor fit if you need log level tracing with the full ecosystem of the tokio-tracing stack without a migration period, since that path goes through the fastrace-tracing compatibility layer. Before adopting it, read the FAQ on why it is fast and the library-level tracing explanation, and set the enable feature only in the binary crate. The project is Apache-2.0 licensed, the workspace version is 0.7.19, and the last push was on 2026-10-17.

Frequently asked questions

How do I add fastrace to a Rust project?

Libraries add it without extra features, with `fastrace = "0.7"` in Cargo.toml, and instrument functions with the `#[fastrace::trace]` attribute. Applications add it with the enable feature, using `fastrace = { version = "0.7", features = ["enable"] }`, set a reporter early and call flush() before terminating.

Why are my spans not being reported?

Span records are reported when the root span is dropped, so a root span that lives for the whole program never reports anything. The README recommends creating root spans for short tasks such as handling a request, and notes that span records generated before the reporter is initialised are ignored.

Which integrations does fastrace provide?

Separate crates for futures streams, axum and poem HTTP services, reqwest requests, tonic and tower client and server requests, plus fastrace-tracing as a compatibility layer for the tokio-tracing ecosystem and fastrace-opentelemetry for exporting spans to OpenTelemetry.

How do I migrate from tokio-tracing to fastrace?

Use the fastrace-tracing crate, which provides a compatibility layer. You set up the standard tracing subscriber with the fastrace compat layer, in two lines of code, and spans from libraries instrumented with tokio-tracing are then captured by fastrace.

Which Rust version does fastrace require?

The minimum supported version is 1.91.0, and the README states that the current fastrace version is not guaranteed to build on Rust versions earlier than that. The workspace uses edition 2024 and resolver 3.

Official sources

  1. fast/fastrace on GitHub
  2. Issues
  3. License: Apache-2.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fast-fastrace.svg)](https://hysenlabs.com/projects/fast-fastrace)

Was this useful?