Skip to main content

keyhog_profile/
collector.rs

1use serde::{Deserialize, Serialize};
2
3pub const COLLECTOR_CAPABILITY_VERSION: u16 = 1;
4
5const fn legacy_collector_capability_version() -> u16 {
6    1
7}
8
9/// Stable identity of a profiling data collector.
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
11#[serde(rename_all = "kebab-case")]
12#[non_exhaustive]
13pub enum CollectorId {
14    ProcessResources,
15    HardwareCounters,
16    SchedulerActivity,
17    ThreadUtilization,
18    CpuTopology,
19    AllocationTracking,
20    SystemIo,
21    PressureThermal,
22}
23
24impl CollectorId {
25    pub const fn as_str(self) -> &'static str {
26        match self {
27            Self::ProcessResources => "process-resources",
28            Self::HardwareCounters => "hardware-counters",
29            Self::SchedulerActivity => "scheduler-activity",
30            Self::ThreadUtilization => "thread-utilization",
31            Self::CpuTopology => "cpu-topology",
32            Self::AllocationTracking => "allocation-tracking",
33            Self::SystemIo => "system-io",
34            Self::PressureThermal => "pressure-thermal",
35        }
36    }
37}
38
39/// Whether a collector can produce measurements on this host.
40#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
41#[serde(rename_all = "kebab-case")]
42pub enum CollectorAvailability {
43    Available,
44    Disabled,
45    PermissionDenied,
46    Unavailable,
47    Unsupported,
48}
49
50impl CollectorAvailability {
51    pub const fn as_str(self) -> &'static str {
52        match self {
53            Self::Available => "available",
54            Self::Disabled => "disabled",
55            Self::PermissionDenied => "permission-denied",
56            Self::Unavailable => "unavailable",
57            Self::Unsupported => "unsupported",
58        }
59    }
60}
61
62/// Host-specific availability report for one collector.
63#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
64pub struct CollectorCapability {
65    #[serde(default = "legacy_collector_capability_version")]
66    pub version: u16,
67    pub collector: CollectorId,
68    pub availability: CollectorAvailability,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub detail: Option<String>,
71}
72
73impl CollectorCapability {
74    pub(crate) fn available(collector: CollectorId) -> Self {
75        Self {
76            version: COLLECTOR_CAPABILITY_VERSION,
77            collector,
78            availability: CollectorAvailability::Available,
79            detail: None,
80        }
81    }
82
83    pub(crate) fn unavailable(
84        collector: CollectorId,
85        availability: CollectorAvailability,
86        detail: impl Into<String>,
87    ) -> Self {
88        Self {
89            version: COLLECTOR_CAPABILITY_VERSION,
90            collector,
91            availability,
92            detail: Some(detail.into()),
93        }
94    }
95}
96
97/// Portable lifecycle for a collector that snapshots one metric family.
98pub trait SnapshotCollector: Send {
99    type Snapshot;
100
101    /// Report whether sampling is supported and usable on this host.
102    fn capability(&self) -> CollectorCapability;
103
104    /// Capture the current values without retaining source data or labels.
105    fn sample(&mut self) -> Self::Snapshot;
106}