| Crates.io | tracing-config |
| lib.rs | tracing-config |
| version | 0.2.2 |
| created_at | 2024-08-24 20:35:17.281215+00 |
| updated_at | 2025-04-24 07:01:13.300753+00 |
| description | Provides a configuration file approach to initializing and configuring tracing and tracing-subscriber |
| homepage | |
| repository | https://github.com/mateiandrei94/tracing-config |
| max_upload_size | |
| id | 1350548 |
| size | 268,769 |
NOTE: For README.md viewers, the links in the documentation do not work for you.
The documentation is best read online at
docs.rs or locally.
To generate the documentation locally :
cargo doc --openThe primary purpose of this crate is to allow rust programs to configure the tracing
crate using the Registry Subscriber implementation from the tracing-subscriber
crate given a toml configuration file and a simple init!() macro call in main() or
in any #[test] function.
This crate is not meant to be used by library authors. If your project contains a lib.rs file,
remove tracing-config from your Cargo.toml project file.
If you use this crate to build and set up your global tracing Subscriber,
the implementation will be a tracing-subscriber Registry and all Layers added
to said Registry will be dynamic dispatch Box<dyn Layer>.
Moreover tracing-config's own SpanRecordLayer will be added to the Registry
right after the root EnvFilter which will essentially keep an in memory serde_json Value
representation of all (non filtered out) Span Values practically negating any and
all performance gained by tracings visitor pattern which does not keep an in-memory
representation of a span data after it's been created/entered.
The SpanRecordLayer visits the values and leverages tracing-subscribers
span Extensions, to persist span data for the remaining of the span's lifetime.
If you suspect that your application suffers performance penalties due to how tracing is configured:
level_filters)main() doing so removes the need for
dynamic dispatch layers.tracing-config from your Cargo.toml project file and
find a different way to configure tracing.Note : Given that there are a myriad of programming languages that only use dynamic dispatch or
heavily rely on it for logging/tracing purposes.
I think that having the same in rust is no big deal especially because once your configuration
is mature enough you can easily construct your subscriber without dynamic dispatch
or the SpanRecordLayer.
Cargo.tomltracing-config = { version = "0.2" }
tracing = { version = "0.1", features = [
"max_level_trace", # trace for debug
"release_max_level_info" # info for release
]}
main.rsuse tracing::*;
fn main() {
tracing_config::init!(); // panics; read the docs on why and when.
let _main_info_span = info_span!("main").entered();
info!("Hello World");
}
tracing_configSet the environment variable so that it points directly to the tracing.toml file.
If setting up an environment variable is too much work, you can also place tracing.toml in the
current directory (usually near Cargo.toml), cargo run as well as your IDE will both work.
tracing.tomltitle = "Pretty colored ts-fmt to stdout"
[layer.ts-fmt]
type = "fmt"
writer = "stdout"
formatter = "pretty"
span_events = "none"
ansi = true
[writer.stdout]
type = "standard_output"
[filter.root]
level = "trace"
tracing.toml in the current directory (near Cargo.toml) and cargo run.tracing_config environment variable and have it point directly to a .toml
configuration file.tracing_config_test environment variable if you want a separate configuration
file for tests.Both environment variables can also point to a directory containing :
tracing-${name}.toml; where ${name} is replaced by the package.name in
your Cargo.toml.tracing.toml.#[test]), files with the -test suffix are checked first
(i.e.: tracing-${name}-test.toml and tracing-test.toml).The init!() macro or the initialize function can be supplied with:
path : A Path pointing directly to a .toml configuration file or to a directory.env : The key of an environment variable, pointing directly to a .toml configuration file
or to a directory.name defaults to package.name in your Cargo.toml.qualifier defaults to an empty string (read ProjectDirs).organization defaults to an empty string (read ProjectDirs).In order to understand the directories mentioned in the following search path, please
read the documentation of the directories crate. ProjectDirs is constructed by default
with empty strings except for the application parameter which is the same name that you supply
to the init!() macro (that defaults to package.name if not set).
qualifier and/or organization are optional but can either or both be supplied to init!()
which will forward them to the ProjectDirs constructor.
The search path is an ordered list of either environment variables or files or directories.
The first element in the list has the highest priority. The search function will loop trough all
elements from highest priority to least priority returning the first existing .toml configuration
file.
path (supplied in source to the init!() macro or the initialize function).env (supplied in source to the init!() macro or the initialize function).env_parent if env is set but does not exist.tracing_config_test environment variable.tracing_config_test_parent if tracing_config_test is set but does not exist.tracing_config environment variable.tracing_config_parent if tracing_config is set but does not exist.project_dirs_preference_dirproject_dirs_config_dirproject_dirs_config_local_dirbase_dirs_preference_dirbase_dirs_config_dirbase_dirs_config_local_diruser_dirs_home_dirbase_dirs_home_dircurrent_exe_dircurrent_dirWithin each directory in the search path the first file that matches the following is accepted :
tracing-${name}.toml; where ${name} is replaced by the package.name in
your Cargo.toml or by the name override passed to init!() or initialize.tracing.toml#[test]), files with the -test suffix are checked first
(e.g.: tracing-${name}-test.toml)The search path is processed as follows :
env, tracing_config and tracing_config_test) specifying
(or pointing to) a direct .toml configuration file or directory are accepted if the
file/directory exists, otherwise demoted to the directory in which the (non existing) file
or directory resides (i.e.: it's parent).${env:key}, are resolved
by replacing the token with the value of the environment variable specified by key if it exists;
this is done recursively up to a certain depth (>=25)..toml file, said file is accepted
regardless of it's name, though calling it tracing.toml is recommended.By default, during initialization, tracing-config will only emit errors and warnings in ansi
color to the program's standard output, this can be changed by setting a different verbosity level
when calling init!() (e.g: init! { verbosity : "trace", };) or
initialize(Some(verbosity)) otherwise, an environment variable tracing_config_verbosity can
be set; accepted values are : trace, debug, info, warn, error, none.
Setting this to debug or trace will cause the function responsible to evaluate the search path
to "print" information about where it's looking and which file is accepted.
Call init!() with verbosity : "none" this overrides the tracing_config_verbosity environment
variable, otherwise make sure it's value is set to "none", this is not recommended though, as the
default verbosity is set to warn which only outputs warnings that should be resolved and hard
errors which eventually panic if initialized by macro.
To fully understand the nomenclature of the configuration file, a thorough read of the documentation
on both tracing and tracing-subscriber crates is required; however, here is a brief summary:
writer is used by a layer and is responsible to write the data incoming form a layer to a
destination, which can be anything, e.g.(standard_output, file, network, database, etc...).
A writer as a component in the system is not strictly necessary since a layer could do the
writing itself.layer is something that receives structured events and spans (i.e.: all the information that
event! and span! macro calls contain) and is responsible to either ignore such events
and spans or format them and either directly write somewhere or send the formatted events and
spans to a writer.filter is a special kind of layer with the sole purpose of filtering out events and spans.
A filter is exclusive, in that it allows everything by default unless there is an exclusion rule.The "flow" that events and spans usually go trough is : filter->layer->writer.
You can find a detailed example and how the configuration file works
in the docs for the config::model module.
For a full understanding of the configuration file structure, start by reading the docs for the root
level configuration structure i.e.: a TracingConfig structure.
Note: The configuration file can include environment variables in the form of ${env:key}
tokens in any toml string in the file, they are resolved with depth=25.In this basic example, we will configure a compact fmt layer with colors for the terminal/console.
Next we will require an environment variable named tracing_config_logs (the name is arbitrary)
which will point to a directory where we will save both .log files and .json files in subdirectories
(.log for humans, and .json for machines).
titletitle = "Basic configuration file"
layer that will output in color to the terminal/console.color-terminal (again, name is arbitrary).type of the layer is fmt from tracing-subscriberwriter but we can name it now "terminal" (name is arbitrary).fmt layer to use the compact formatter.span_eventsansi which means pretty colors.[layer.color-terminal]
type = "fmt"
writer = "terminal"
formatter = "compact"
span_events = "none"
ansi = true
terminal since the layer color-terminal is using it.type to standard_output no other configuration
is necessary for this type of writer.[writer.terminal]
type = "standard_output"
color-terminal except :log-filefullactive span_events since these will emit an event on span enter and exit.ansi[layer.log-file]
type = "fmt"
writer = "log-file"
formatter = "full"
span_events = "active"
ansi = false
writers.tracing-configs custom json layer, set type to json.pretty flag is off by default,
we set it explicitly here to show that pretty output is possible.machine-readable.[layer.json-file]
type = "json"
writer = "machine-readable"
pretty = false
log-file and another machine-readable.log-filetype to filehuman subdirectory inside whatever directory
the environment variable tracing_config_logs points to.
We set directory_path with a reference to ${env:tracing_config_logs}.file_name and file_ext[writer.log-file]
type = "file"
directory_path = "${env:tracing_config_logs}/human"
file_name = "my_app"
file_ext = "log"
max_log_files = 7 # keep at most 7 log files
rotation = "daily" # 7 log files daily means a week of history
non_blocking = true # async writes
lossy = true # it's okay if we loose some, we still have the json
[writer.machine-readable]
type = "file"
directory_path = "${env:tracing_config_logs}/machine"
file_name = "my_app"
file_ext = "json"
rotation = "never" # This will be GB in size
non_blocking = true
lossy = false
[filter.root]
level = "trace"
directives = [
"hyper=error",
"hyper::client::connect::dns=error",
"hyper::proto::h1::conn=error",
"hyper::proto::h1::conn=error",
"hyper::proto::h1::io=error",
"hyper::proto::h1::role=error",
"hyper::proto::h1::encode=error",
"hyper::client::pool=error",
]
Combining it together:
tracing-basic.tomltitle = "Basic configuration file"
[layer.color-terminal]
type = "fmt"
writer = "terminal"
formatter = "compact"
span_events = "none"
ansi = true
[writer.terminal]
type = "standard_output"
[layer.log-file]
type = "fmt"
writer = "log-file"
formatter = "full"
span_events = "active"
ansi = false
[layer.json-file]
type = "json"
writer = "machine-readable"
pretty = false
[writer.log-file]
type = "file"
directory_path = "${env:tracing_config_logs}/human"
file_name = "my_app"
file_ext = "log"
max_log_files = 7 # keep at most 7 log files
rotation = "daily" # 7 log files daily means a week of history
non_blocking = true # async writes
lossy = true # it's okay if we loose some, we still have the json
[writer.machine-readable]
type = "file"
directory_path = "${env:tracing_config_logs}/machine"
file_name = "my_app"
file_ext = "json"
rotation = "never" # This will be GB in size
non_blocking = true
lossy = false
[filter.root]
level = "trace"
directives = [
"hyper=error",
"hyper::client::connect::dns=error",
"hyper::proto::h1::conn=error",
"hyper::proto::h1::conn=error",
"hyper::proto::h1::io=error",
"hyper::proto::h1::role=error",
"hyper::proto::h1::encode=error",
"hyper::client::pool=error",
]
#[test]
fn test_basic_config() {
use tracing::*;
std::env::set_var("tracing_config_logs", "path/to/some/dir");
tracing_config::init! {
path : std::path::Path::new("path/to/tracing-basic.toml")
};
// do more tests here
let _span = info_span!("my_span").entered();
info!("Test done!");
}