mprobe_diagnostics/
lib.rs

1//! A library for parsing and reading MongoDB diagnostic data,
2//! generated by the Full Time Diagnostic Data Capture (FTDC) mechanism.
3//!
4//! # Read the diagnostic data
5//!
6//! One can read the diagnostic data by constructing an instance of [DiagnosticData],
7//! and iterate over it to get the diagnostic metrics. The iterator yields one
8//! [chunk] of the diagnostic data at a time. Each chunk contains a list of [metrics]
9//! and [metadata] associated with it. The metrics are always returned sorted in
10//! ascending order.
11//!
12//! [chunk]: crate::metrics::MetricsChunk
13//! [metadata]: crate::metadata::Metadata
14//! [metrics]: crate::metrics::Metric
15//!
16//! The following example shows how to read through the diagnostic data and print
17//! the metric names on the standard output.
18//!
19//! ```no_run
20//! use std::path::Path;
21//! use std::result::Result;
22//!
23//! use mprobe_diagnostics::DiagnosticData;
24//! use mprobe_diagnostics::error::MetricParseError;
25//!
26//! fn main() -> Result<(), MetricParseError> {
27//!     // Note: this example needs a valid path
28//!     // that contains diagnostic data for it to run
29//!     let path = Path::new("/path/to/diagnostic/data");
30//!     let diagnostic_data = DiagnosticData::new(&path)?;
31//!
32//!     for chunk in diagnostic_data {
33//!         for metric in chunk?.metrics {
34//!             println!("{}", metric.name);
35//!         }
36//!     }
37//!
38//!     Ok(())
39//! }
40//! ```
41//!
42//! # Filter the diagnostic data
43//!
44//! Since the [DiagnosticData] implements [IntoIterator], one could use
45//! the [Iterator::filter] combinator to filter the diagnostic data. However that will
46//! be applied to all the diagnostic data contained in the provided path, which
47//! can contain a lot of data. When one needs only the diagnostic data of a node,
48//! process, or the data in a specific time window, one can use
49//! the [DiagnosticData::filter] function and provide an instance of [MetricsFilter].
50//!
51//! In the example below it is show how one could use it.
52//!
53//! ```no_run
54//! use std::path::Path;
55//! use chrono::{DateTime, Duration, TimeDelta, Utc};
56//! use mprobe_diagnostics::{DiagnosticData, MetricsFilter};
57//!
58//! let path = Path::new("/path/to/diagnostic/data");
59//!
60//! let node = String::from("node-1");
61//! let start = Utc::now() - Duration::hours(1);
62//! let end = Utc::now();
63//!
64//! let filter = MetricsFilter::new(Some(node), Some(start), Some(end));
65//! let diagnostic_data = DiagnosticData::filter(&path, filter).expect("valid path");
66//! ```
67//!
68
69#![warn(missing_docs)]
70
71mod bson;
72mod bytes;
73mod compression;
74mod filter;
75mod iter;
76mod read;
77
78pub mod error;
79pub mod metadata;
80pub mod metrics;
81
82use std::fs;
83use std::fs::ReadDir;
84use std::io;
85use std::path::Path;
86
87use chrono::DateTime;
88use chrono::Utc;
89
90use crate::error::MetricParseError;
91use crate::metrics::MetricsChunk;
92use crate::read::MetricsIterator;
93
94/// `DiagnosticData` defines an API for parsing and reading MongoDB diagnostic data.
95#[derive(Debug)]
96pub struct DiagnosticData {
97    entries: ReadDir,
98    filter: MetricsFilter,
99}
100
101impl DiagnosticData {
102    /// Creates a new `DiagnosticData` that will parse and read
103    /// the diagnostic data at the specified `path`.
104    ///
105    /// The `path` must be valid and point to a directory containing
106    /// the diagnostic data unarchived.
107    pub fn new(path: &Path) -> Result<Self, io::Error> {
108        let entries = fs::read_dir(path)?;
109        let filter = MetricsFilter::default();
110
111        Ok(Self { entries, filter })
112    }
113
114    /// Creates a new `DiagnosticData` that will parse and read
115    /// the diagnostic data at the specified `path` and filter it
116    /// according to the `filter` specification.
117    ///
118    /// The `path` must be valid and point to a directory containing
119    /// the diagnostic data unarchived.
120    pub fn filter(path: &Path, filter: MetricsFilter) -> Result<Self, io::Error> {
121        let entries = fs::read_dir(path)?;
122
123        Ok(Self { entries, filter })
124    }
125}
126
127impl IntoIterator for DiagnosticData {
128    type Item = Result<MetricsChunk, MetricParseError>;
129
130    type IntoIter = MetricsIterator;
131
132    fn into_iter(self) -> Self::IntoIter {
133        MetricsIterator::new(self.entries, self.filter)
134    }
135}
136
137/// `MetricsFilter` specifies a filter for the diagnostic data.
138#[derive(Debug, Default)]
139pub struct MetricsFilter {
140    pub(crate) hostname: Option<String>,
141    pub(crate) start: Option<DateTime<Utc>>,
142    pub(crate) end: Option<DateTime<Utc>>,
143}
144
145impl MetricsFilter {
146    /// Creates a new `MetricsFilter` used for filtering diagnostic data.
147    ///
148    /// The `MetricsFilter` enables filtering the data based on the following
149    /// parameters:
150    ///
151    /// * `hostname` - if set, selects the data belonging for
152    ///     the specified hostname;
153    /// * `start` - if set, selects the data starting at the specified timestamp;
154    /// * `end` - if set, selects the data up until the specified timestamp.
155    pub fn new(
156        hostname: Option<String>,
157        start: Option<DateTime<Utc>>,
158        end: Option<DateTime<Utc>>,
159    ) -> Self {
160        Self {
161            hostname,
162            start,
163            end,
164        }
165    }
166}