sampler/
config.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6use fidl_fuchsia_diagnostics as fdiagnostics;
7use fuchsia_inspect::{Node, NumericProperty, UintProperty};
8use sampler_component_config::Config as ComponentConfig;
9use sampler_config::runtime::ProjectConfig;
10use sampler_config::{MetricType, ProjectId};
11use std::collections::HashMap;
12
13/// Container for all configurations needed to instantiate the Sampler infrastructure.
14/// Includes:
15///      - Project configurations.
16///      - Whether to configure the ArchiveReader for tests (e.g. longer timeouts)
17///      - Minimum sample rate.
18#[derive(Debug)]
19pub struct SamplerConfig {
20    pub project_configs: Vec<ProjectConfig>,
21    pub stats: SamplerStats,
22}
23
24#[derive(Debug)]
25pub struct ProjectStats {
26    _project_node: Node,
27    pub metrics_configured: UintProperty,
28    pub cobalt_logs_sent: UintProperty,
29}
30
31#[derive(Default, Debug)]
32pub struct SamplerStats {
33    pub projects: HashMap<ProjectId, ProjectStats>,
34}
35
36impl SamplerConfig {
37    pub fn new(config: ComponentConfig, stats: &Node) -> Result<Self, Error> {
38        let ComponentConfig { minimum_sample_rate_sec, project_configs } = config;
39        let mut sampler_stats = SamplerStats::default();
40        let project_configs = project_configs
41            .into_iter()
42            .map(|config| {
43                let config: ProjectConfig = serde_json::from_str(&config)?;
44                if config.poll_rate_sec < minimum_sample_rate_sec {
45                    return Err(anyhow::anyhow!(
46                        "Project {} had illegal poll rate. Actual: {}s, Min: {}s",
47                        config.project_id,
48                        config.poll_rate_sec,
49                        minimum_sample_rate_sec
50                    ));
51                }
52                sampler_stats
53                    .projects
54                    .entry(config.project_id)
55                    .and_modify(|project| {
56                        project.metrics_configured.add(config.metrics.len() as u64);
57                    })
58                    .or_insert_with(|| {
59                        let project_node =
60                            stats.create_child(format!("project_{}", config.project_id));
61                        let metrics_configured = project_node
62                            .create_uint("metrics_configured", config.metrics.len() as u64);
63                        let cobalt_logs_sent = project_node.create_uint("cobalt_logs_sent", 0);
64                        ProjectStats {
65                            _project_node: project_node,
66                            metrics_configured,
67                            cobalt_logs_sent,
68                        }
69                    });
70                Ok(config)
71            })
72            .collect::<Result<Vec<_>, Error>>()?;
73
74        Ok(Self { project_configs, stats: sampler_stats })
75    }
76
77    pub fn sample_data(&self) -> Vec<fdiagnostics::SampleDatum> {
78        let mut data = vec![];
79        for project in &self.project_configs {
80            for metric in &project.metrics {
81                let strategy = Some(match metric.metric_type {
82                    MetricType::Integer | MetricType::String => {
83                        fdiagnostics::SampleStrategy::Always
84                    }
85                    MetricType::IntHistogram | MetricType::Occurrence => {
86                        fdiagnostics::SampleStrategy::OnDiff
87                    }
88                });
89
90                for selector in &metric.selectors {
91                    data.push(fdiagnostics::SampleDatum {
92                        selector: Some(fdiagnostics::SelectorArgument::StructuredSelector(
93                            selector.clone(),
94                        )),
95                        interval_secs: Some(project.poll_rate_sec),
96                        strategy,
97                        ..Default::default()
98                    });
99                }
100            }
101        }
102
103        data
104    }
105}