Expand description
Rasant is a lightweight, high performance and flexible Rust library for structured logging, inspired by the likes of zap and zerolog.
It offers nanosecond precision, stackable logging and outstanding performance: on modern systems, Rasant can process and dispatch logs to multiple sinks in tens of nanoseconds, being normally bottlenecked by I/O operations. Can’t wait that long? There’s built-in async support!
§Main Features
- Minimal dependencies.
- Thread safe.
- Blazing fast performance, with zero allocations on most operations.
- Leveled, structured contextual logging with nanosecond precision.
- Simple API, with support for stacked logging.
- Log filters, including log sampling, with support for third-party implementations.
- Highly configurable log sinks:
- General I/O (files, stdout, stderr…) in text, JSON and CBOR format.
- systemd’s journald.
- syslog RFC 3164 (classic BSD) and RFC 5424 (2009) protocols.
- Support for third-party sink implementations.
- Dynamic async logging with constant lock time.
§Examples
§Basic Logging
Loggers can be easily initialized using sink defaults, and accessed via methods…
use rasant;
use rasant::Value;
let mut log = rasant::Logger::new();
log.add_sink(rasant::sink::stderr::default()).set_level(rasant::Level::Info);
log.set("program_name", "test");
log.info("hello world!");
log.warn_with("here's some context", [("line", Value::from(7))]);
log.debug("and i'm ignored :(");…or the much nicer macro API:
use rasant as r;
let mut log = r::Logger::new();
log.add_sink(r::sink::stderr::default()).set_level(r::Level::Info);
r::set!(log, program_name = "test");
r::info!(log, "hello world!");
r::warn!(log, "here's some context", line = 7);
r::debug!(log, "and i'm ignored :(");2026-04-03 17:16:03.773 +0200 [INF] hello world! program_name="test"
2026-04-03 17:16:03.773 +0200 [WRN] here's some context program_name="test" line=7§Attributes
Rasant supports multiple attribute types (a.k.a Values): single Scalars, lists and maps.
use rasant as r;
let mut log = r::Logger::new();
log.add_sink(r::sink::stderr::default()).set_level(r::Level::Info);
r::info!(log, "a single", value = 123.456);
let simple_list = [1, 2, 3, 4];
r::info!(log, "lists can be simple", list = r::list!(simple_list));
r::info!(log, "or have mixed types", list = r::list!("string!", 123.456, 789012 as usize));
r::info!(log, "and so can maps!", map = r::map!("key #1" => 123, 456 => 789.012));2026-05-04 03:58:41.189 +0200 [INF] a single value=123.456
2026-05-04 03:58:41.189 +0200 [INF] lists can be simple list=[1, 2, 3, 4]
2026-05-04 03:58:41.189 +0200 [INF] or have mixed types list=["string!", 123.456, 0xc0a14]
2026-05-04 03:58:41.189 +0200 [INF] and so can maps! map={"key #1": 123, 456: 789.012}§Stacking
All Loggers can be cheaply cloned, inheriting all settings from its parents - including levels, sinks, filters and fixed attributes, allowing for very flexible setups.
For example, to have all errors (or higher) within a thread logged to
stderr:
use rasant as r;
use std::thread;
let mut log = r::Logger::new();
log.add_sink(r::sink::stdout::default()).set_level(r::Level::Info);
r::info!(log, "main logs to stdout only");
let mut thread_log = log.clone();
thread::spawn(move || {
thread_log.add_sink(r::sink::stderr::default()).set_level(r::Level::Error);
r::set!(thread_log, thread_id = thread::current().id());
r::info!(thread_log, "this will not log anything");
r::fatal!(thread_log, "but this will log to both stdout and stderr");
});§Configuring Sinks
Sinks can be configured to tweak multiple parameters, including time and overall output format.
use rasant as r;
let mut log = r::Logger::new();
log.set_level(r::Level::Info).add_sink(
r::sink::stdout::new(r::sink::stdout::StdoutConfig {
formatter_cfg: r::FormatterConfig {
format: r::OutputFormat::Json,
time_format: r::TimeFormat::UtcNanosDateTime,
..r::FormatterConfig::default()
},
..r::sink::stdout::StdoutConfig::default()
})
);
r::info!(log, "hello!");{"time":"2026-04-03 16:03:04.481888522","level":"info","message":"hello!"}§Asynchronous Logging
Loggers can dynamically enable/disable async writes.
When in async mode, log operations have a slightly longer (as details are copied into a queue) but fixed lock time, making it ideal f.ex. for writing to slow storage without compromising overall performance.
use rasant as r;
let mut log = r::Logger::new();
log.set_level(r::Level::Info).add_sink(r::sink::stdout::default());
r::info!(log, "this is writen synchronously");
log.set_async(true);
r::info!(log, "and these write");
r::warn!(log, "asynchronously, but");
r::info!(log, "in order!");§Filtering
Loggers can apply configurable runtime filters on log operations. Supported filters include:
- Multiple log levels.
- Log message contents.
- Log attributes presence.
- Log attribute value contents.
- Several log output sampling criteria.
Note that filters are evaluated at logging time, and will introduce (minimal) latency, regardless of loggers having async mode enabled.
use rasant as r;
use std::time::Duration;
// Log a maximum of 10 Debug, Warning and Fatal updates per second, to keep SREs happy.
let mut log = r::Logger::new();
log
.add_sink(r::sink::stdout::default())
.set_all_levels()
.add_filter(
r::filter::level::In::new(
r::filter::level::InConfig {
levels: [r::Level::Debug, r::Level::Warning, r::Level::Fatal],
}))
.add_filter(
r::filter::sample::Burst::new(
r::filter::sample::BurstConfig {
period: Duration::from_millis(1000),
max_updates: 10,
}));
r::info!(log, "this will not log");
r::debug!(log, "but");
r::fatal!(log, "these");
r::warn!(log, "will");§Concepts
Rasant is a structured logging library: it logs messages with a set of associated key-value pairs, in formats (f.ex. JSON) which are intended to be easily machine-readable.
§Methodology
Rasant is built around individual loggerlogging instances and sinks, which are configurable destinations for log updates. When a log operation is performed, its level is compared to the one defined for the logger and, if applicable, the log is written to all of its sinks.
Once a sink is added to a logger, it cannot be removed nor modified.
§Sinks
Sinkss are configurable destinations for log updates, of which loggers can have an arbitrary number of instances associated with. When a log operation is performed, loggers dispatch log writes to each of its sinks, in sequence.
§Third-Party Sinks
If Rasant’s built-in sinks don’t fulfill your needs, these can be easily
constructed as external modules, by implementing the sink::Sink trait. See
tests/ for examples.
In general sinks are expected to be stateless and efficient. If your new sink code fulfills these requirements, and don’t introduce unnecessary dependencies, consider contributing it!
§Attributes
§Types
Attributes are the defining quality of a structured logging system, expressed
as key-value pairs. In Rasant, keys are &str, and values are a
dedicated type, supporting different configuration of basic scalar
types.
Scalars are the base unit for attribute values, mapping to basic data types: integers, floats and strings.Valueis a structured collection ofScalars.
Rasant supports three basic Value types:
Value::Scalaris a singleScalar, and the most commonly used type.Value::Listis an ordered set ofScalars.Value::Mapis an ordered set of key-valueScalars.
§Scope
Attributes can be set for loggers as a whole, affecting all log operations, or for individual log writes, which can optionally provide extra attributes with additional information.
Upon key collisions, attribute values for the log call take precedence and override logger settings, but without modifying it.
§Memory Management
Rasant keeps all items associated with a logger (keys, attribute values, their scalars and all strings) in a group of owned vector arrays. No other heap allocation is ever performed.
These vectors will grow in size when needed - but never resize down. In practice, this means that after just a few log calls vectors will grow to the size required for normal operation, at which point all Rasant operations become effectively zero-allocation.
The vectored nature of logger storage also makes cloning and dropping these extremely efficient.
§Cloning and Stacking
Loggers can be cheaply cloned, extended and dropped. When a logger is cloned, it inherits all settings from the original, including levels, filters, sinks (owned + inherited) and attributes.
This allows for very flexible logging setups. New loggers can just be extensions of an original with extra arguments, have newly defined sinks, log levels, filters and/or async modes - or all of the above.
In general, programs using Rasant will instantiate a single root logger via
Logger::new(), and spawn nested clones as required.
§Asynchronous Operation
By default, log operations lock until writes are propagated to all sinks associated with a given logger.
To improve performance when slow and/or a high number of sinks is involved, Rasant supports dynamic asynchronous logging.
Loggers can be switched to asynchronous mode via Logger::set_async(). When
enabled, log operations defer writes by pushing them into a processing queue,
and return immediately.
Rasant will spawn a single thread to handle all asynchronous write operations, and close it automatically once no async loggers are present, and all their deferred writes have flushed.
§Log Filters
Loggers supports optional, configurable filters for log updates. These are evaluated, in order, on every log operation, blocking sink writes unless the configured criteria for all filters is met. Multiple filters can be stacked to combine their behavior.
Filters are evaluated after normal level checks, so their output is affected
by each logger’s level. When using level-based filters (f.ex. levels),
consider enabling Logger::set_all_levels() to avoid unexpected interactions.
Note that filters are evaluated at logging time, even for loggers in asynchronous mode; as a result, every filter will introduce additional latency on all log operations for that logger.
§Third-Party Filters
If Rasant’s built-in filters don’t fulfill your needs, these can be easily
constructed as external modules, by implementing the filter::Filter trait. See
tests/ for examples.
In general filters are expected to be lightweight and efficient, given that they are evaluated for every log update processed by loggers. If your new filter code fulfills these requirements, and don’t introduce unnecessary dependencies, consider contributing it!
§Error Handling
For performance’s sake, very few operations in Rasant’s public API return errors, and will panic upon failures instead.
Pretty much all errors related to logging are unrecoverable anyway - these will either happen at initialization time, or when trying to write to a sink.
§Repository
This project is currently hosted at GitHub.
§License
Rasant is distrubuted under the MIT license.
Modules§
Macros§
- debug
- Logs an update with debug level.
- error
- Logs an update with error level.
- fatal
- Logs an update with fatal level.
- info
- Logs an update with info level.
- list
- Creates a List from types which can be cast to a collection of Scalars.
- map
- Creates a Map from key/value types which can be cast to collections of Scalars.
- panic
- Logs an update with panic level.
- set
- Sets a number of attributes for a given logger.
- trace
- Logs an update with trace level.
- warn
- Logs an update with warning level.
Structs§
- Attributes
Iter - A {key,
Value,Metadata} iterator for attribute maps. - Attributes
KeyIter - A key iterator for attribute maps.
- Attributes
KeyValue Iter - A {key,
Value} iterator for attribute maps. - Formatter
Config - Configuration struct for output formatting.
- Logger
- Base logger structure for Rasant.
Enums§
- Attribute
Metadata Field - Attribute metadata flags for attributes.
- Level
- Log level definition.
- Output
Format - Supported log output format for all
sinks. - Scalar
- Scalar definitions for all log operations. Scalars are the basic data units for Rasant, representing a single type.
- Time
Format ntime::Format, re-exported for convenience. Defines a format forTimestampstring serialization.- Value
- Value definition for all log operations.
These are associated with a single
&strkey in attribute maps for loggers.
Traits§
- Attribute
Metadata Impl - Attribute metadata implementation.
Type Aliases§
- Attribute
Metadata - Metadata associated with {key: attribute} pairs.