use std::collections::BTreeMap;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResourceMeasurementScope {
Global,
ApplicationProcess,
ProcessTree,
}
impl ResourceMeasurementScope {
pub const fn as_str(self) -> &'static str {
match self {
Self::Global => "global",
Self::ApplicationProcess => "application_process",
Self::ProcessTree => "process_tree",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResourceOperatingSystem {
Linux,
Macos,
Windows,
Unsupported,
}
impl ResourceOperatingSystem {
pub const fn as_str(self) -> &'static str {
match self {
Self::Linux => "linux",
Self::Macos => "macos",
Self::Windows => "windows",
Self::Unsupported => "unsupported",
}
}
}
pub trait ResourceUnit: Copy {
fn as_str(self) -> &'static str;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DurationUnit {
Microseconds,
#[default]
Milliseconds,
Seconds,
Minutes,
}
impl DurationUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::Microseconds => "microseconds",
Self::Milliseconds => "milliseconds",
Self::Seconds => "seconds",
Self::Minutes => "minutes",
}
}
}
impl ResourceUnit for DurationUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum CapacityUnit {
Bytes,
Kilobytes,
Megabytes,
Gigabytes,
Terabytes,
#[default]
Kibibytes,
Mebibytes,
Gibibytes,
Tebibytes,
}
impl CapacityUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::Bytes => "bytes",
Self::Kilobytes => "kilobytes",
Self::Megabytes => "megabytes",
Self::Gigabytes => "gigabytes",
Self::Terabytes => "terabytes",
Self::Kibibytes => "kibibytes",
Self::Mebibytes => "mebibytes",
Self::Gibibytes => "gibibytes",
Self::Tebibytes => "tebibytes",
}
}
}
impl ResourceUnit for CapacityUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DataUnit {
#[default]
Bytes,
Kilobytes,
Megabytes,
Gigabytes,
Terabytes,
Kibibytes,
Mebibytes,
Gibibytes,
Tebibytes,
}
impl DataUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::Bytes => "bytes",
Self::Kilobytes => "kilobytes",
Self::Megabytes => "megabytes",
Self::Gigabytes => "gigabytes",
Self::Terabytes => "terabytes",
Self::Kibibytes => "kibibytes",
Self::Mebibytes => "mebibytes",
Self::Gibibytes => "gibibytes",
Self::Tebibytes => "tebibytes",
}
}
}
impl ResourceUnit for DataUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
#[allow(clippy::enum_variant_names)] pub enum BandwidthUnit {
#[default]
BytesPerSecond,
KibibytesPerSecond,
MebibytesPerSecond,
GibibytesPerSecond,
BitsPerSecond,
MegabitsPerSecond,
GigabitsPerSecond,
}
impl BandwidthUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::BytesPerSecond => "bytes_per_second",
Self::KibibytesPerSecond => "kibibytes_per_second",
Self::MebibytesPerSecond => "mebibytes_per_second",
Self::GibibytesPerSecond => "gibibytes_per_second",
Self::BitsPerSecond => "bits_per_second",
Self::MegabitsPerSecond => "megabits_per_second",
Self::GigabitsPerSecond => "gigabits_per_second",
}
}
}
impl ResourceUnit for BandwidthUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum CpuUnit {
#[default]
LogicalProcessors,
Millicores,
}
impl CpuUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::LogicalProcessors => "logical_processors",
Self::Millicores => "millicores",
}
}
}
impl ResourceUnit for CpuUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum UtilizationUnit {
#[default]
Percentage,
Fraction,
}
impl UtilizationUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::Percentage => "percentage",
Self::Fraction => "fraction",
}
}
}
impl ResourceUnit for UtilizationUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CountUnit {
Processes,
Threads,
FileDescriptors,
Handles,
Events,
Operations,
Packets,
Errors,
}
impl CountUnit {
pub const fn as_str(self) -> &'static str {
match self {
Self::Processes => "processes",
Self::Threads => "threads",
Self::FileDescriptors => "file_descriptors",
Self::Handles => "handles",
Self::Events => "events",
Self::Operations => "operations",
Self::Packets => "packets",
Self::Errors => "errors",
}
}
}
impl ResourceUnit for CountUnit {
fn as_str(self) -> &'static str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResourceMetricValue {
Integer(u64),
Decimal(f64),
}
impl From<u64> for ResourceMetricValue {
fn from(value: u64) -> Self {
Self::Integer(value)
}
}
impl From<f64> for ResourceMetricValue {
fn from(value: f64) -> Self {
Self::Decimal(value)
}
}
impl ResourceMetricValue {
fn compare_to_u64(&self, other: u64) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering;
match self {
Self::Integer(value) => Some(value.cmp(&other)),
Self::Decimal(value) if value.is_nan() => None,
Self::Decimal(value) if *value < 0.0 => Some(Ordering::Less),
Self::Decimal(value) if *value >= u64::MAX as f64 => Some(Ordering::Greater),
Self::Decimal(value) => {
let whole = value.trunc() as u64;
match whole.cmp(&other) {
Ordering::Equal if value.fract() > 0.0 => Some(Ordering::Greater),
ordering => Some(ordering),
}
}
}
}
}
impl PartialEq for ResourceMetricValue {
fn eq(&self, other: &Self) -> bool {
use std::cmp::Ordering;
match (self, other) {
(Self::Integer(left), Self::Integer(right)) => left == right,
(Self::Decimal(left), Self::Decimal(right)) => left == right,
(Self::Decimal(_), Self::Integer(right)) => {
self.compare_to_u64(*right) == Some(Ordering::Equal)
}
(Self::Integer(left), Self::Decimal(_)) => {
other.compare_to_u64(*left) == Some(Ordering::Equal)
}
}
}
}
impl PartialEq<u64> for ResourceMetricValue {
fn eq(&self, other: &u64) -> bool {
self.compare_to_u64(*other) == Some(std::cmp::Ordering::Equal)
}
}
impl PartialOrd<u64> for ResourceMetricValue {
fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
self.compare_to_u64(*other)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceMeasurement<T, U: ResourceUnit> {
pub value: T,
pub unit: U,
}
impl<T, U: ResourceUnit> ResourceMeasurement<T, U> {
pub fn new(value: impl Into<T>, unit: U) -> Self {
Self {
value: value.into(),
unit,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResourceLimitResource {
Cpu,
Memory,
Processes,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResourceLimitEventKind {
Throttled,
High,
Maximum,
OutOfMemory,
Terminated,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceLimitEventCount {
pub resource: ResourceLimitResource,
pub event: ResourceLimitEventKind,
pub count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AcceleratorVendor {
Nvidia,
Amd,
Intel,
Apple,
Other,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AcceleratorDeviceMetrics {
pub vendor: AcceleratorVendor,
pub device_identifier: String,
pub device_index: Option<u32>,
pub memory_used: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub compute_utilization: Option<ResourceMeasurement<f64, UtilizationUnit>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AcceleratorProcessMetrics {
pub vendor: AcceleratorVendor,
pub device_identifier: String,
pub device_index: Option<u32>,
pub process_id: u32,
pub memory_used: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub compute_utilization: Option<ResourceMeasurement<f64, UtilizationUnit>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FilesystemCapacityMetrics {
pub path: String,
pub total_capacity: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub available_capacity: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub free_capacity: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProcessSamplingMetadata {
pub visible_processes: u64,
pub sampled_processes: u64,
pub field_sampled_processes: BTreeMap<String, u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CpuMetrics {
pub user_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub system_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub total_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub consumption_rate: Option<ResourceMeasurement<f64, CpuUnit>>,
pub throttled_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub effective_limit: Option<ResourceMeasurement<f64, CpuUnit>>,
pub some_pressure_stall_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub full_pressure_stall_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub limit_events: Vec<ResourceLimitEventCount>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MemoryMetrics {
pub system_used: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub system_total: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub system_available: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub resident: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub private: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub physical_footprint: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub virtual_memory: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub peak_resident: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub limit: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub environment_accounted: Option<ResourceMeasurement<ResourceMetricValue, CapacityUnit>>,
pub some_pressure_stall_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub full_pressure_stall_time: Option<ResourceMeasurement<ResourceMetricValue, DurationUnit>>,
pub out_of_memory_event_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub limit_events: Vec<ResourceLimitEventCount>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProcessMetrics {
pub active_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub descendant_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub thread_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub lifetime_creation_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub open_file_descriptor_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub windows_handle_count: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub limit_events: Vec<ResourceLimitEventCount>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DiskMetrics {
pub read_data: Option<ResourceMeasurement<ResourceMetricValue, DataUnit>>,
pub write_data: Option<ResourceMeasurement<ResourceMetricValue, DataUnit>>,
pub read_throughput: Option<ResourceMeasurement<f64, BandwidthUnit>>,
pub write_throughput: Option<ResourceMeasurement<f64, BandwidthUnit>>,
pub read_operations: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub write_operations: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub filesystems: Vec<FilesystemCapacityMetrics>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NetworkTrafficMetrics {
pub received_data: Option<ResourceMeasurement<ResourceMetricValue, DataUnit>>,
pub transmitted_data: Option<ResourceMeasurement<ResourceMetricValue, DataUnit>>,
pub receive_throughput: Option<ResourceMeasurement<f64, BandwidthUnit>>,
pub transmit_throughput: Option<ResourceMeasurement<f64, BandwidthUnit>>,
pub received_packets: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub transmitted_packets: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub receive_errors: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
pub transmit_errors: Option<ResourceMeasurement<ResourceMetricValue, CountUnit>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NetworkInterfaceMetrics {
pub name: String,
pub traffic: NetworkTrafficMetrics,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NetworkMetrics {
pub measurement_scope: ResourceMeasurementScope,
pub system: NetworkTrafficMetrics,
pub interfaces: Vec<NetworkInterfaceMetrics>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GpuMetrics {
pub device_metrics: Option<Vec<AcceleratorDeviceMetrics>>,
pub process_metrics: Option<Vec<AcceleratorProcessMetrics>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceMetricsSnapshot {
pub timestamp: DateTime<Utc>,
pub operating_system: ResourceOperatingSystem,
pub measurement_scope: ResourceMeasurementScope,
pub process_sampling: Option<ProcessSamplingMetadata>,
pub cpu: Option<CpuMetrics>,
pub memory: Option<MemoryMetrics>,
pub process: Option<ProcessMetrics>,
pub disk: Option<DiskMetrics>,
pub gpu: Option<GpuMetrics>,
pub network: Option<NetworkMetrics>,
}