Traceon
Pronounced trace on
An easy to use log and tracing formatter with a flattened json or pretty output.
It simplifies tracing, which adds context to log messages for better observability especially in async or multithreaded contexts, this documentation will get you up and running but the detailed tracing docs are here.
The only two crates you need in Cargo.toml are:
[]
= "0.1"
= "0.2"
You can write your first human readable trace with:
use ;
Which will give the default output:
To turn the output into json:
builder.json.on;
This example shows all the options you can change with the builder:
use ;
Output
The defaults of the builder are optimized for local development, that is pretty output with just the local time, when moving to production e.g. on a cloud provider, you're likely going to want to turn the time off as they often record stdout and capture their own timestamps:
use TimeFormat;
builder.time.json;
env-filter is used by default at the info level to filter any messages out at the debug or trace level, to change the level you can set an environment variable e.g. RUST_LOG=warn which would filter out info level as well, all the options are detailed here
Examples
#[instrument] macro
You can use the tracing::instrument macro with both async and normal functions to capture the arguments used in each function call:
async
async
06:16:30 INFO result: 15
a: 5
b: 10
span: add
Instrument trait
If you need to add additional context to an async function, you can create a span and instrument it:
use Instrument;
async
async
06:18:55 INFO result: 15
package_name: traceon
span: math_functions
The above package_name comes from the environment variable provided by cargo, which gets it from Cargo.toml at compile time and saves it for runtime:
[]
= "traceon"
Entered spans
This creates a span and returns a guard, as long as that guard is in scope the span will be active:
06:26:21 INFO result: 15
package_name: traceon
span: math
Warning If
add()was anasync fn, holding onto the guard would cause memory leaks and information loss, you must use the aboveinstrumentmacro or trait instead, more details here
Note Just remember don't call
.awaitwhile holding onto a guard
Nested spans
By default the span name will be joined with the characters :: for nested spans:
06:33:57 INFO result: 15
a: 5
b: 10
package_name: traceon
span: math::add
You can set this to overwrite if you prefer:
use SpanFormat;
builder.span.on;
06:36:00 INFO result: 15
a: 5
b: 10
package_name: traceon
span: add
By default all the other fields overwrite if a nested span has the same field name, you can join all fields or specific fields if you prefer (this only effects text values):
use JoinFields;
builder
.join_fields
.on;
let _span_1 = info_span!.entered;
let _span_2 = info_span!.entered;
info!;
output:
12:44:12 INFO testing field join
field_a: changed
field_b: original||changed
span: span_1::span_1
Event
tracing::event! allows you to add fields to message without having to create a span, just remember to put the level e.g. tracing::Level::INFO as the first parameter, this also shows how to create a custom message in an event, and how to output a Debug implementation:
use Level;
06:47:00 INFO event triggered
event_example: add field and log it without a span
06:47:00 WARN overwrite message, and debug a vector
vector: [10, 15, 20]
Write to a file
If you wanted to write to log files instead of std, it's as simple adding the dependency to Cargo.toml:
[]
= "0.2.2"
And initializing it via the builder:
let file_appender = hourly;
builder.json.writer.on;
info!;
The writer accepts anything that implements the Write trait, if you want to hold onto a buffer wrapped in an Arc and Mutex there is buffer() method on the builder.
Compose with other layers
You can also use the formatting layer with other tracing layers as you get more comfortable with the tracing ecosystem, for example to change the filter:
use ;
Change the case of keys
Often you'll be consuming different crates that implement their own traces and you need all their keys to match a certain format, this example also demonstrates how to use different instances of traceon for a given scope with on_thread(), which returns a guard so the subscriber is only running on the current thread, and will be turned off when the guard is dropped.
use Case;
use Level;
Output:
Performance
This crate uses the idea originated from: LukeMathWalker/tracing-bunyan-formatter of storing fields from visited spans in a HashMap instead of a BTreeMap which is more suited for flattening fields, and results in very similar performance to the json formatter in tracing-subcriber:
logging to a sink
units = nanosecond or billionth of a second
logging to stdout
units = microsecond or millionth of a second
Nested spans three levels deep with concatenated fields:
