use std::collections::HashMap;
pub type ExporterResult<T> = Result<T, ExporterError>;
#[derive(Debug)]
pub enum ExporterError {
SerializationError(String),
FormatError(String),
UnsupportedFormat(String),
Io(std::io::Error),
Other(String),
}
impl std::fmt::Display for ExporterError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SerializationError(msg) => write!(f, "Failed to serialize metrics: {msg}"),
Self::FormatError(msg) => write!(f, "Invalid metric format: {msg}"),
Self::UnsupportedFormat(msg) => write!(f, "Unsupported export format: {msg}"),
Self::Io(err) => write!(f, "IO error: {err}"),
Self::Other(msg) => write!(f, "Other error: {msg}"),
}
}
}
impl std::error::Error for ExporterError {}
impl From<std::io::Error> for ExporterError {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExportFormat {
Prometheus,
Json,
OpenMetrics,
StatsD,
InfluxDB,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct MetricMetadata {
pub name: String,
pub help: Option<String>,
pub unit: Option<String>,
pub metric_type: MetricType,
pub labels: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MetricType {
Counter,
Gauge,
Histogram,
Summary,
Untyped,
}
#[derive(Debug, Clone)]
pub struct MetricValue {
pub value: f64,
pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
pub labels: HashMap<String, String>,
}
#[derive(Debug, Clone, Default)]
pub struct MetricCollection {
pub metrics: HashMap<String, Vec<MetricValue>>,
pub metadata: HashMap<String, MetricMetadata>,
}
pub trait MetricsExporter: Send + Sync {
fn export(&self, format: ExportFormat) -> ExporterResult<String>;
fn export_prometheus(&self) -> ExporterResult<String> {
self.export(ExportFormat::Prometheus)
}
fn export_json(&self) -> ExporterResult<String> {
self.export(ExportFormat::Json)
}
fn get_metrics(&self) -> MetricCollection;
fn supported_formats(&self) -> Vec<ExportFormat> {
vec![ExportFormat::Prometheus, ExportFormat::Json]
}
fn validate_metrics(&self) -> ExporterResult<()> {
Ok(())
}
}
pub trait GpuMetricsExporter<T>: MetricsExporter {
fn export_gpu_metrics(&self, gpu: &T, index: usize) -> MetricCollection;
fn export_gpu_utilization(&self, gpu: &T, index: usize) -> MetricCollection;
fn export_gpu_memory(&self, gpu: &T, index: usize) -> MetricCollection;
fn export_gpu_temperature(&self, gpu: &T, index: usize) -> MetricCollection;
fn export_gpu_power(&self, gpu: &T, index: usize) -> MetricCollection;
fn export_gpu_processes(&self, gpu: &T, index: usize) -> MetricCollection;
}
pub trait CpuMetricsExporter<T>: MetricsExporter {
fn export_cpu_metrics(&self, cpu: &T) -> MetricCollection;
fn export_cpu_utilization(&self, cpu: &T) -> MetricCollection;
fn export_cpu_frequency(&self, cpu: &T) -> MetricCollection;
fn export_cpu_temperature(&self, cpu: &T) -> MetricCollection;
fn export_cpu_cores(&self, cpu: &T) -> MetricCollection;
}
pub trait MemoryMetricsExporter<T>: MetricsExporter {
fn export_memory_metrics(&self, memory: &T) -> MetricCollection;
fn export_memory_usage(&self, memory: &T) -> MetricCollection;
fn export_swap_metrics(&self, memory: &T) -> MetricCollection;
fn export_cache_metrics(&self, memory: &T) -> MetricCollection;
}
pub trait StorageMetricsExporter<T>: MetricsExporter {
fn export_storage_metrics(&self, storage: &T, index: usize) -> MetricCollection;
fn export_storage_usage(&self, storage: &T, index: usize) -> MetricCollection;
fn export_storage_io(&self, storage: &T, index: usize) -> MetricCollection;
}
pub trait CompositeExporter: MetricsExporter {
type GpuInfo;
type CpuInfo;
type MemoryInfo;
type StorageInfo;
fn export_all(&self) -> ExporterResult<String>;
fn export_gpus(&self, gpus: &[Self::GpuInfo]) -> ExporterResult<String>;
fn export_cpu(&self, cpu: &Self::CpuInfo) -> ExporterResult<String>;
fn export_memory(&self, memory: &Self::MemoryInfo) -> ExporterResult<String>;
fn export_storage(&self, storage: &[Self::StorageInfo]) -> ExporterResult<String>;
fn set_global_labels(&mut self, labels: HashMap<String, String>);
fn set_metric_prefix(&mut self, prefix: String);
}
pub trait ExporterBuilder {
type Exporter: MetricsExporter;
fn build(self) -> ExporterResult<Self::Exporter>;
fn with_format(self, format: ExportFormat) -> Self;
fn with_labels(self, labels: HashMap<String, String>) -> Self;
fn with_prefix(self, prefix: String) -> Self;
}
pub type BoxedCompositeExporter<G, C, M, S> =
Box<dyn CompositeExporter<GpuInfo = G, CpuInfo = C, MemoryInfo = M, StorageInfo = S>>;
pub trait ExporterFactory {
type GpuInfo;
type CpuInfo;
type MemoryInfo;
type StorageInfo;
fn create(&self, format: ExportFormat) -> ExporterResult<Box<dyn MetricsExporter>>;
fn create_gpu_exporter(
&self,
format: ExportFormat,
) -> ExporterResult<Box<dyn GpuMetricsExporter<Self::GpuInfo>>>;
fn create_cpu_exporter(
&self,
format: ExportFormat,
) -> ExporterResult<Box<dyn CpuMetricsExporter<Self::CpuInfo>>>;
#[allow(clippy::type_complexity)]
fn create_composite(
&self,
format: ExportFormat,
) -> ExporterResult<
BoxedCompositeExporter<Self::GpuInfo, Self::CpuInfo, Self::MemoryInfo, Self::StorageInfo>,
>;
}