below_dump/
print.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use model::Field;
16use model::FieldId;
17use model::Nameable;
18use model::Queriable;
19use model::Recursive;
20use render::RenderConfig;
21use render::RenderOpenMetricsConfigBuilder;
22
23use super::*;
24
25impl CommonField {
26    /// Default RenderConfig for CommonField
27    pub fn get_render_config(&self) -> RenderConfig {
28        let rc = render::RenderConfigBuilder::new();
29        match self {
30            Self::Timestamp => rc.title("Timestamp").width(10),
31            Self::Datetime => rc.title("Datetime").width(19),
32        }
33        .get()
34    }
35}
36
37impl<F> DumpField<F>
38where
39    F: FieldId,
40    <<F as FieldId>::Queriable as Queriable>::FieldId: ToString,
41{
42    fn get_field_id_str(&self) -> String {
43        match self {
44            Self::Common(common) => common.to_string(),
45            Self::FieldId(field_id) => field_id.to_string(),
46        }
47    }
48}
49
50impl<F> DumpField<F>
51where
52    F: FieldId,
53    F::Queriable: HasRenderConfigForDump,
54{
55    pub fn get_render_config(&self) -> RenderConfig {
56        match self {
57            Self::Common(common) => common.get_render_config(),
58            Self::FieldId(field_id) => F::Queriable::get_render_config_for_dump(field_id),
59        }
60    }
61
62    pub fn get_openmetrics_render_config(
63        &self,
64        model: &F::Queriable,
65    ) -> Option<RenderOpenMetricsConfigBuilder> {
66        match self {
67            // Common fields (eg timestamp) are already encoded into metric
68            Self::Common(_) => None,
69            Self::FieldId(field_id) => model.get_openmetrics_config_for_dump(field_id),
70        }
71    }
72
73    pub fn get_field(&self, ctx: &CommonFieldContext, model: &F::Queriable) -> Option<Field> {
74        match self {
75            Self::Common(common) => common.get_field(ctx),
76            Self::FieldId(field_id) => model.query(field_id),
77        }
78    }
79
80    pub fn dump_field(
81        &self,
82        ctx: &CommonFieldContext,
83        model: &F::Queriable,
84        raw: bool,
85        fixed_width: bool,
86    ) -> String {
87        let mut config = self.get_render_config();
88        if raw {
89            config.format = None;
90            config.suffix = None;
91        }
92        config.render(self.get_field(ctx, model), fixed_width)
93    }
94
95    pub fn dump_field_openmetrics(
96        &self,
97        key: &str,
98        ctx: &CommonFieldContext,
99        model: &F::Queriable,
100    ) -> Option<String> {
101        match self.get_field(ctx, model) {
102            Some(f) => self.get_openmetrics_render_config(model).map(|b| {
103                b.label("hostname", &ctx.hostname)
104                    .build()
105                    .render(key, f, ctx.timestamp)
106            }),
107            None => None,
108        }
109    }
110}
111
112impl<F> DumpField<F>
113where
114    F: FieldId,
115    F::Queriable: HasRenderConfigForDump + Recursive,
116{
117    pub fn dump_field_indented(
118        &self,
119        ctx: &CommonFieldContext,
120        model: &F::Queriable,
121        raw: bool,
122        fixed_width: bool,
123    ) -> String {
124        let mut config = self.get_render_config();
125        if raw {
126            config.format = None;
127            config.suffix = None;
128        }
129        config.render_indented(self.get_field(ctx, model), fixed_width, model.get_depth())
130    }
131}
132
133pub fn dump_kv<T: HasRenderConfigForDump>(
134    fields: &[DumpField<T::FieldId>],
135    ctx: &CommonFieldContext,
136    model: &T,
137    raw: bool,
138) -> String {
139    let mut res = String::new();
140    for field in fields {
141        let config = field.get_render_config();
142        res.push_str(&format!(
143            "{}: {}\n",
144            config.render_title(false),
145            field.dump_field(ctx, model, raw, false),
146        ));
147    }
148    res.push('\n');
149    res
150}
151
152pub fn dump_json<T: HasRenderConfigForDump>(
153    fields: &[DumpField<T::FieldId>],
154    ctx: &CommonFieldContext,
155    model: &T,
156    raw: bool,
157) -> Value {
158    let mut res = json!({});
159    for field in fields {
160        let config = field.get_render_config();
161        res[config.render_title(false)] = json!(field.dump_field(ctx, model, raw, false));
162    }
163    res
164}
165
166fn dump_title_line<F>(fields: &[DumpField<F>], sep: &'static str, fixed_width: bool) -> String
167where
168    F: FieldId,
169    F::Queriable: HasRenderConfigForDump,
170{
171    let mut line = String::new();
172    for field in fields {
173        line.push_str(&field.get_render_config().render_title(fixed_width));
174        line.push_str(sep);
175    }
176    line.push('\n');
177    line
178}
179
180pub fn dump_raw<T: HasRenderConfigForDump>(
181    fields: &[DumpField<T::FieldId>],
182    ctx: &CommonFieldContext,
183    model: &T,
184    round: usize,
185    repeat_title: Option<usize>,
186    disable_title: bool,
187    raw: bool,
188) -> String {
189    let mut res = String::new();
190    let repeat = repeat_title.unwrap_or(0);
191    if !disable_title && (round == 0 || (repeat != 0 && round % repeat == 0)) {
192        res.push_str(&dump_title_line(fields, " ", true));
193    }
194    for field in fields {
195        res.push_str(&field.dump_field(ctx, model, raw, true));
196        res.push(' ');
197    }
198    res.push('\n');
199    res
200}
201
202pub fn dump_raw_indented<T: HasRenderConfigForDump + Recursive>(
203    fields: &[DumpField<T::FieldId>],
204    ctx: &CommonFieldContext,
205    model: &T,
206    round: usize,
207    repeat_title: Option<usize>,
208    disable_title: bool,
209    raw: bool,
210) -> String {
211    let mut res = String::new();
212    let repeat = repeat_title.unwrap_or(0);
213    if !disable_title && (round == 0 || (repeat != 0 && round % repeat == 0)) {
214        res.push_str(&dump_title_line(fields, " ", true));
215    }
216    for field in fields {
217        res.push_str(&field.dump_field_indented(ctx, model, raw, true));
218        res.push(' ');
219    }
220    res.push('\n');
221    res
222}
223
224pub fn dump_csv<T: HasRenderConfigForDump>(
225    fields: &[DumpField<T::FieldId>],
226    ctx: &CommonFieldContext,
227    model: &T,
228    round: usize,
229    disable_title: bool,
230    raw: bool,
231) -> String {
232    let mut res = String::new();
233    if !disable_title && round == 0 {
234        res.push_str(&dump_title_line(fields, ",", false));
235    }
236    for field in fields {
237        res.push_str(&field.dump_field(ctx, model, raw, false));
238        res.push(',');
239    }
240    res.push('\n');
241    res
242}
243
244pub fn dump_tsv<T: HasRenderConfigForDump>(
245    fields: &[DumpField<T::FieldId>],
246    ctx: &CommonFieldContext,
247    model: &T,
248    round: usize,
249    disable_title: bool,
250    raw: bool,
251) -> String {
252    let mut res = String::new();
253    if !disable_title && round == 0 {
254        res.push_str(&dump_title_line(fields, "\t", false));
255    }
256    for field in fields {
257        res.push_str(&field.dump_field(ctx, model, raw, false));
258        res.push('\t');
259    }
260    res.push('\n');
261    res
262}
263
264pub fn dump_openmetrics<T>(
265    fields: &[DumpField<T::FieldId>],
266    ctx: &CommonFieldContext,
267    model: &T,
268) -> String
269where
270    T: HasRenderConfigForDump,
271    T: Nameable,
272    T::FieldId: ToString,
273{
274    fields
275        .iter()
276        .filter_map(|field| {
277            // OpenMetrics forbids `.` in metric name
278            let key = format!(
279                "{}_{}",
280                T::name(),
281                field.get_field_id_str().replace('.', "_")
282            );
283            field.dump_field_openmetrics(&key, ctx, model)
284        })
285        .flat_map(|s| s.chars().collect::<Vec<_>>().into_iter())
286        .collect::<String>()
287}