Skip to main content

tracing_config/
config.model.rs

1//! This module contains the configuration file data model.
2//!
3//! All [`Option`][enum@std::option::Option] fields are optional and have default values.
4//!
5//! # Example detailed configuration file.
6//!
7//! ```toml
8//! title = "My App tracing Config"
9//! # declare a layer named "stdout"
10//! [layer.stdout]
11//! # type can be "fmt", "json", "sifting"
12//! type = "fmt"
13//! writer = "stdout" # in this configuration file there should be a writer called "stdout"
14//! # the following are "fmt" layer specific properties
15//! formatter = "pretty" # can be "full", "compact", "pretty", "json"
16//! span_events = "none" # do not record span events
17//! ansi = true # use ansi color escape codes
18//! # the other properties are set to their default values
19//!
20//! # declare a writer named "stdout"
21//! [writer.stdout]
22//! type = "standard_output" # we only support "file" and "standard_output"
23//! # standard_output writer does not require additional configuration
24//!
25//! # declare a layer named "util"
26//! [layer.util]
27//! type = "fmt"
28//! writer = "util" # will write to "util" writer which is a file
29//! # fmt layer specific config
30//! formatter = "full"
31//! span_events = "none"
32//! ansi = false
33//!
34//! # declare a writer named "util"
35//! [writer.util]
36//! type = "file" # will write to a file
37//! directory_path = "${env:fs_app_logs}/rust/" # ${env:fs_app_logs} there should be an environment variable named "fs_app_logs"
38//! file_name = "util"
39//! rotation = "daily" # every day we will get a new file in "directory_path"
40//! non_blocking = true # does not block caller (when you tracing::info!(...) does not wait to write to the file)
41//!
42//! # declare a layer named "webapi"
43//! [layer.webapi]
44//! type = "sifting" # this is a sifting layer, it will dynamically create new layers and writers at runtime as new "sift_on" values are introduced
45//! writer = "webapi" # the output will be the "webapi" writer which MUST have parameter values for each declared "sift_on" keys.
46//! layer = "webapi-sifted" # what layer should be sifted ?
47//! sift_on = ["id"] # the keys this sifting layers sifts.
48//!
49//! # declare a layer named "webapi-sifted"
50//! # given that is is the target of a sifting layer it's writer must be ${sl:sifted}
51//! # "sl" is a special placeholder replacement scheme indicating the initial letters of sifting layer
52//! [layer.webapi-sifted]
53//! type = "fmt"
54//! writer = "${sl:sifted}"
55//! formatter = "full"
56//! span_events = "exit" # records exit span events
57//! ansi = false # it records to a file, so no colors
58//!
59//! # declare a writer named "webapi"
60//! # given that is is the target of a sifting layer it must incorporate all the keys defined in "sift_on" in the sifting layer
61//! # in this case it's the id property, you will have a different file name for each different id
62//! [writer.webapi]
63//! type = "file"
64//! directory_path = "${env:fs_app_logs}/rust/webapi"
65//! file_name = "${sl:id}"
66//! file_ext = "log"
67//! max_log_files = 50 # keep at most 50 log files
68//! rotation = "daily" # 50 log files daily means 50 days of history
69//! non_blocking = true # async
70//! lossy = false # no loss of information/logs
71//!
72//! # declare a layer named "jayson"
73//! [layer.jayson]
74//! type = "json" # uses the custom tracing-config json layer
75//! writer = "jayson" # use the "jayson" writer
76//! pretty = true
77//!
78//! # declare a writer named "jayson"
79//! [writer.jayson]
80//! type = "file"
81//! directory_path = "${env:fs_app_logs}/rust/"
82//! file_name = "jayson"
83//! file_ext = "json"
84//! rotation = "daily"
85//! non_blocking = true
86//!
87//! # declare a filter named "my_filter"
88//! [filter.my_filter]
89//! level = "trace" # at lever trace
90//! directives = [
91//!     "my_module::proto=info" # however my_module::proto only emits info events and above
92//! ]
93//!
94//! # this is required, it's the root filter
95//! [filter.root]
96//! level = "info" # if not specified otherwise the event should be at info level
97//! directives = [
98//!     # in this example we do not want events from the hyper crate at levels below error
99//!     "hyper=error",
100//!     "hyper::client::connect::dns=error",
101//!     "hyper::proto::h1::conn=error",
102//!     "hyper::proto::h1::conn=error",
103//!     "hyper::proto::h1::io=error",
104//!     "hyper::proto::h1::role=error",
105//!     "hyper::proto::h1::encode=error",
106//!     "hyper::client::pool=error",
107//! ]
108//! ```
109#![doc = include_str!("../doc/prism_js.html")]
110
111use serde::{Deserialize, Serialize};
112
113use std::collections::HashMap;
114
115/// An [`fmt Layer`][trait@tracing_subscriber::layer::Layer] [`formatter (check docs)`][struct@tracing_subscriber::fmt::format::Format]
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(rename_all = "snake_case")]
118pub enum FmtLayerFormatter {
119    Full,
120    Compact,
121    Pretty,
122    Json,
123}
124
125/// A [`tracing`][mod@tracing] [`Level (check docs)`][struct@tracing::Level]
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
127#[serde(rename_all = "snake_case")]
128pub enum Level {
129    Trace,
130    Debug,
131    Info,
132    Warn,
133    Error,
134}
135
136/// A [`tracing appender`][mod@tracing_appender] [`file rotation (check docs)`][struct@tracing_appender::rolling::Rotation] scheme
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
138#[serde(rename_all = "snake_case")]
139pub enum FileRotation {
140    Minutely,
141    Hourly,
142    Daily,
143    Never,
144}
145
146/// Span event types, check [`FmtSpan`][struct@tracing_subscriber::fmt::format::FmtSpan] Implementations.
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
148#[serde(rename_all = "snake_case")]
149pub enum SpanEvents {
150    /// one event when span is created
151    New,
152    /// one event per enter of a span
153    Enter,
154    /// one event per exit of a span
155    Exit,
156    /// one event when the span is dropped
157    Close,
158    /// spans are ignored (this is the default)
159    None,
160    /// one event per enter/exit of a span
161    Active,
162    /// events at all points (new, enter, exit, drop)
163    Full,
164}
165
166/// A file [`Writer`][trait@tracing_subscriber::fmt::MakeWriter]
167/// # Example
168/// ```toml
169/// # declare a writer named "my_file"
170/// [writer.my_file]
171/// type = "file"
172/// directory_path = "${env:fs_app_logs}/rust/" # ${env:fs_app_logs} there should be an environment variable named "fs_app_logs"
173/// file_name = "util"
174/// # The following are optional
175/// file_ext = "log" # filename will end in .log
176/// max_log_files = 50 # keep at most 50 log files
177/// rotation = "daily" # possible values are : "minutely", "hourly", "daily", "never"
178/// # The following are optional and only related to non blocking
179/// non_blocking = true # async
180/// buffered_lines_limit = 2048 # lines buffer
181/// lossy = false # no loss of information/logs
182/// thread_name = "my_file_async_thread"
183/// ```
184#[doc = include_str!("../doc/prism_js.html")]
185#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186pub struct FileWriter {
187    /// Must point to an existing directory on the file system
188    pub directory_path: String,
189    /// The name of the tracing file without file extension
190    pub file_name: String,
191    /// The file extension without the . (dot)
192    pub file_ext: Option<String>,
193    /// The maximum number of log/tracing files inside of `directory_path` for this writer.
194    /// Only valid if `FileRotation` is not `Never`
195    pub max_log_files: Option<usize>,
196    /// File rotation options
197    pub rotation: Option<FileRotation>,
198    /// Non blocking options
199    #[serde(flatten)]
200    pub non_blocking: NonBlockingOptions,
201}
202
203/// [`tracing appender`][mod@tracing_appender] [`NonBlocking`][struct@tracing_appender::non_blocking::NonBlockingBuilder] options
204#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
205pub struct NonBlockingOptions {
206    /// Whether non blocking is enabled or not
207    #[serde(rename = "non_blocking")]
208    pub enabled: bool,
209    /// Number of lines to buffer before dropping logs (only valid if `lossy` is enabled)
210    pub buffered_lines_limit: Option<usize>,
211    /// If set to true, logs will be dropped when the buffered limit (`buffered_lines_limit`) is reached
212    pub lossy: Option<bool>,
213    /// Override the worker thread’s name.
214    pub thread_name: Option<String>,
215}
216
217/// Supported [`Writer`][trait@tracing_subscriber::fmt::MakeWriter] types
218/// # Example
219/// ```toml
220/// # declare a writer named "my_writer_stdout"
221/// [writer.my_writer_stdout]
222/// type = "standard_output"
223/// # declare a writer named "my_file"
224/// [writer.my_file]
225/// type = "file"
226/// # see FileWriter for FileWriter properties
227/// ```
228#[doc = include_str!("../doc/prism_js.html")]
229#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
230#[serde(rename_all = "snake_case")]
231#[serde(tag = "type")]
232pub enum Writer {
233    File(FileWriter),
234    StandardOutput,
235    StandardError,
236}
237
238/// [`fmt Layer (see docs)`][struct@tracing_subscriber::fmt::Layer] configuration.
239/// # Example
240/// ```toml
241/// # declare a layer named "app"
242/// [layer.app]
243/// type = "fmt"
244/// writer = "my_file" # must be a declared writer
245/// formatter =  "pretty" # can be : "full", "compact", "pretty", "json"
246/// span_events = "none" # can be : "new", "enter", "exit", "close", "none", "active", "full"
247/// ansi = false # color terminal
248/// # the following properties are optional
249/// filter = "only_app" # must be a named filter
250/// time = true
251/// level = true
252/// target = true
253/// file = true
254/// line_number = true
255/// thread_ids = true
256/// thread_names = true
257/// span_list = true # for "json" formatter only
258/// current_span = true # for "json" formatter only
259/// flatten_event = true # for "json" formatter only
260/// ```
261#[doc = include_str!("../doc/prism_js.html")]
262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
263pub struct FmtLayer {
264    /// Applies a per Layer filter, makes this a [`Filtered Layer`][struct@tracing_subscriber::filter::Filtered]
265    pub filter: Option<String>,
266    /// Where does the output go
267    pub writer: String,
268    /// Formatting kind/type
269    pub formatter: FmtLayerFormatter,
270    /// Which span events should be recorded
271    pub span_events: SpanEvents,
272    /// Ansi color escape codes (for color terminal) recommend to disable if writer is file
273    pub ansi: bool,
274    /// Record time
275    pub time: Option<bool>,
276    /// Record level
277    pub level: Option<bool>,
278    /// Record span/event target
279    pub target: Option<bool>,
280    /// Record source file
281    pub file: Option<bool>,
282    /// Record source file line number
283    #[serde(rename = "line")]
284    pub line_number: Option<bool>,
285    /// Record thread id
286    pub thread_ids: Option<bool>,
287    /// Record thread name
288    pub thread_names: Option<bool>,
289
290    /// Record span list (json formatter only)
291    #[serde(rename = "json_span_list")]
292    pub span_list: Option<bool>,
293    /// Record current span (json formatter only)
294    #[serde(rename = "json_current_span")]
295    pub current_span: Option<bool>,
296    /// Flatten json output (json formatter only)
297    #[serde(rename = "json_flatten_event")]
298    pub flatten_event: Option<bool>,
299}
300
301/// tracing-config custom [`JsonLayer`][struct@crate::tracing::JsonLayer]
302/// # Example
303/// ```toml
304/// # declare a layer named "app"
305/// [layer.app]
306/// type = "json"
307/// writer = "my_file" # must be a declared writer
308/// # the following properties are optional
309/// filter = "only_app" # must be a named filter
310/// trailing_comma = true
311/// pretty_json = true
312/// span_id = true
313/// span_uuid = true
314/// span_timestamp = true
315/// span_level = true
316/// span_name = true
317/// span_target = true
318/// span_module_path = true
319/// span_file = true
320/// span_line = true
321/// span_fields = true
322/// event_timestamp = true
323/// event_level = true
324/// event_name = true
325/// event_target = true
326/// event_module_path = true
327/// event_file = true
328/// event_line = true
329/// event_fields = true
330/// event_span_id = true
331/// event_span_uuid = true
332/// event_spans = true
333/// ```
334#[doc = include_str!("../doc/prism_js.html")]
335#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
336pub struct JsonLayer {
337    /// Applies a per Layer filter, makes this a [`Filtered Layer`][struct@tracing_subscriber::filter::Filtered]
338    pub filter: Option<String>,
339    /// Where does the output go
340    pub writer: String,
341    /// Add a comma after the json event (so you can parse the entire file as a JSON array)
342    pub trailing_comma: Option<bool>,
343    /// Pretty print events
344    #[serde(rename = "pretty")]
345    pub pretty_json: Option<bool>,
346    /// Record span id
347    pub span_id: Option<bool>,
348    /// Record span uuid (a json layer concept only)
349    pub span_uuid: Option<bool>,
350    /// Record span timestamp
351    pub span_timestamp: Option<bool>,
352    /// Record span level
353    pub span_level: Option<bool>,
354    /// Record span name
355    pub span_name: Option<bool>,
356    /// Record span target
357    pub span_target: Option<bool>,
358    /// Record span module path
359    pub span_module_path: Option<bool>,
360    /// Record span source file
361    pub span_file: Option<bool>,
362    /// Record span source line
363    pub span_line: Option<bool>,
364    /// Record span fields (recorded fields)
365    pub span_fields: Option<bool>,
366    /// Record event timestamp
367    pub event_timestamp: Option<bool>,
368    /// Record event level
369    pub event_level: Option<bool>,
370    /// Record event name
371    pub event_name: Option<bool>,
372    /// Record event target
373    pub event_target: Option<bool>,
374    /// Record event module path
375    pub event_module_path: Option<bool>,
376    /// Record event source file
377    pub event_file: Option<bool>,
378    /// Record event source line
379    pub event_line: Option<bool>,
380    /// Record event fields (recorded fields)
381    pub event_fields: Option<bool>,
382    /// Record event span id
383    pub event_span_id: Option<bool>,
384    /// Record event span uuid (a json layer concept only)
385    pub event_span_uuid: Option<bool>,
386    /// Record event spans
387    pub event_spans: Option<bool>,
388}
389
390/// tracing-config custom [`SiftingLayer (read how it works here)`][struct@crate::tracing::SiftingLayer].
391///
392/// Given it's purpose (i.e.: to dynamically create different layers based on some runtime criteria) the sifting layer will require a special writer as well as a special layer to sift on.
393///
394/// > **IMPORTANT** : The sifting layer will create (dynamically at runtime) a new `layer` for each set of different `sift_on` values.
395/// >
396/// > For example; to handle a user, the application enters a "user_handler" info span saving username as span data `info_span!("user_handler", username)`.
397/// >
398/// > While it does not make sense to create drastically different layers for each different user, having the same layer (or a copy or clone of it) write to a different file does (and it's the primary goal of the sifting layer).
399/// > In the example above the `sift_on` value should be `sift_on = ["username"]`.
400///
401/// # Limitation
402/// The limitation of the configuration file form of the sifting layer is the fact that it will essentially clone the sifted layer with a different writer.
403/// This means that the writer **MUST** be different for each set of values declared in the `sift_on` field.
404///
405/// # Selector
406/// This represents a [`SiftingLayerSelector`][struct@crate::tracing::SiftingLayerSelector].
407///
408/// To select for metadata :
409/// - `${meta:level}` => selects level
410/// - `${meta:name}` => selects name
411/// - `${meta:target}` => selects target
412/// - `${meta:module_path}` => selects module_path
413/// - `${meta:file}` => selects file
414/// - `${meta:line}` => selects line
415///
416/// Otherwise simply type in the name of the span data key as seen in the source code.
417/// ```toml
418/// sift_on = ["username", "${meta:module_path}"]
419/// ```
420///
421/// # Writer
422/// The configuration file form of the sifting layer only supports the [`FileWriter`][struct@FileWriter].
423///
424/// A sifting layer writer has additional/special placeholders in the form of `${sl:key}` and `${sl:meta:}` available as values in the configuration file.
425/// These additional placeholders can be used in the following properties of the file writer :
426/// - `directory_path`
427/// - `file_name`
428/// - `file_ext`
429///
430/// Here is a list of the available meta values :
431/// - `${sl:meta:level}` => returns the level of the event
432/// - `${sl:meta:name}` => returns the name of the event
433/// - `${sl:meta:target}` => returns the target of the event
434/// - `${sl:meta:module_path}` => returns the module path of the event
435/// - `${sl:meta:file}` => returns the source file of the event
436/// - `${sl:meta:line}` => returns the source line of the event
437///
438/// For any other `sift_on` non meta value the syntax is `${sl:key}` the word `meta` itself is allowed as a non meta key.
439/// For example `info_span!("metadata_handler", meta)` use `sift_on = ["meta"]` and `${sl:meta}` to retrieve the value of meta.
440///
441/// > **IMPORTANT** : All values declared in the `sift_on` field must be used in either `directory_path`, `file_name` or `file_ext`.
442/// > If meta values are required they must also be declared in the `sift_on` list.
443/// >
444/// > Example :
445/// > ```toml
446/// > # sifting layer
447/// > sift_on = ["username", "${meta:level}"]
448/// > # later on file writer
449/// > directory_path = "${env:fs_app_logs}/rust/webapi/${sl:username}"
450/// > file_ext = "${sl:meta:level}.log"
451/// > # Notice we used both username and level
452/// > ```
453///
454/// # Layer
455/// The configuration file form of the sifting layer can only sift trough the [`FmtLayer`][struct@FmtLayer] or the [`JsonLayer`][struct@JsonLayer].
456/// The programmatic form of the [`SiftingLayer`][struct@crate::tracing::SiftingLayer] can sift trough any type of [`Layer`][trait@tracing_subscriber::Layer].
457///
458/// The layer must **NOT** contain a `filter` itself. This is because the sifting layer creates fake layers and does not register them with tracing thus the filters (if any) are not registered either.
459/// A filter on a sifted layer results in a panic at runtime.
460///
461/// The writer of a sifted layer must be `${sl:sifted}` otherwise it will be processed as a regular layer.
462///
463/// Example :
464/// ```toml
465/// [layer.webapi-sifted]
466/// # a sifted layer MUST NOT include a filter
467/// type = "fmt"
468/// writer = "${sl:sifted}" # the writer MUST be the sifted writer
469/// ```
470///
471/// # Example
472/// ```toml
473/// # declare a layer named "webapi"
474/// [layer.webapi]
475/// type = "sifting" # this is a sifting layer, it will dynamically create new layers and writers at runtime as new "sift_on" values are introduced
476/// writer = "webapi_writer" # the output will be the "webapi" writer which MUST have parameter values for each declared "sift_on" keys.
477/// layer = "webapi-sifted" # what layer should be sifted ? must NOT have a filter
478/// sift_on = ["id"] # the keys this sifting layers sifts.
479///
480/// # we must declare a writer that includes all "sift_on" keys named "webapi_writer"
481/// [writer.webapi_writer]
482/// type = "file"
483/// directory_path = "${env:fs_app_logs}/rust/webapi"
484/// file_name = "${sl:id}" # use the "sl" scheme to refer back to "sift_on" keys
485/// file_ext = "log"
486/// max_log_files = 50 # keep at most 50 log files
487/// rotation = "daily" # 50 log files daily means 50 days of history
488/// non_blocking = true # async
489/// lossy = false # no loss of information/logs
490///
491/// # we must also declare the sifted layer, you can name it however you want, i chose to add "-sifted" to the original name
492/// [layer.webapi-sifted]
493/// # a sifted layer MUST NOT include a filter
494/// type = "fmt"
495/// writer = "${sl:sifted}" # the writer MUST be the sifted writer
496/// formatter = "full"
497/// span_events = "exit" # records exit span events
498/// ansi = false # it records to a file, so no colors
499/// ```
500#[doc = include_str!("../doc/prism_js.html")]
501#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
502pub struct SiftingLayer {
503    /// Applies a per Layer filter, makes this a [`Filtered Layer`][struct@tracing_subscriber::filter::Filtered]
504    pub filter: Option<String>,
505    /// A supported sifting writer; documented above.
506    pub writer: String,
507    /// A supported sifted layer; documented above. The layer must **NOT** contain a `filter` itself.
508    pub layer: String,
509    /// This represents a [`SiftingLayerSelector`][struct@crate::tracing::SiftingLayerSelector].
510    pub sift_on: Vec<String>,
511}
512
513/// Supported [`Layer`][trait@tracing_subscriber::Layer] types
514/// # Example
515/// ```toml
516/// # declare a layer named "app"
517/// [layer.app]
518/// type = "fmt" # type can be "fmt", "json", "sifting"
519/// # all other properties depend on the type of layer you are configuring
520/// ```
521#[doc = include_str!("../doc/prism_js.html")]
522#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
523#[serde(rename_all = "snake_case")]
524#[serde(tag = "type")]
525pub enum Layer {
526    Fmt(FmtLayer),
527    Json(JsonLayer),
528    Sifting(SiftingLayer),
529}
530
531/// A [`Filter`][trait@tracing_subscriber::layer::Filter]
532///
533/// A filter named `root` is required in your configuration file
534///
535/// ```toml
536/// [filter.root]
537/// level = "debug"
538/// directives = []
539/// ```
540///
541/// # Example
542/// ```toml
543/// # declare a filter named "my_filter"
544/// [filter.my_filter]
545/// level = "trace" # at lever trace, possible values are : "trace", "debug", "info", "warn", "error"
546/// directives = [ # see tracing subscriber env filter for more details
547///     "my_module::proto=info" # however my_module::proto only emits info events and above
548/// ]
549/// ```
550#[doc = include_str!("../doc/prism_js.html")]
551#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
552pub struct Filter {
553    /// Base filter level
554    pub level: Level,
555    /// See docs at [`EnvFilter`][struct@tracing_subscriber::filter::EnvFilter] `Directives` section
556    pub directives: Option<Vec<String>>,
557}
558
559/// This represents the whole configuration file.
560/// - `title` : does not do anything, is is for your reference
561/// - `writers` : list all your writers (where tracing will write events), only 1 per layer is allowed, reuse is not allowed
562/// - `layers` : list all your layers, these will all be added to a [`Registry`][struct@tracing_subscriber::registry::Registry]
563/// - `filters` : list all your filters by name, reuse is allowed
564///
565/// # Example :
566/// ```toml
567/// title = "My App tracing Config"
568///
569/// [writer.my_output]
570/// # writer properties
571///
572/// [writer.second]
573/// # writer properties
574///
575/// [layer.one]
576/// # layer properties
577///
578/// [layer.two]
579/// # layer properties
580///
581/// [filter.f_one]
582/// # filter properties
583///
584/// [filter.f_two]
585/// # filter properties
586///
587/// # this is required, it's the "root" filter
588/// [filter.root]
589/// # filter properties
590/// ```
591#[doc = include_str!("../doc/prism_js.html")]
592#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
593pub struct TracingConfig {
594    pub title: String,
595    #[serde(rename = "writer")]
596    pub writers: HashMap<String, Writer>,
597    #[serde(rename = "layer")]
598    pub layers: HashMap<String, Layer>,
599    #[serde(rename = "filter")]
600    pub filters: HashMap<String, Filter>,
601}