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}