From 9cc3781230dfdf5783741c70ca7999c1fc964170 Mon Sep 17 00:00:00 2001 From: Sijisu Date: Mon, 29 May 2023 23:08:21 +0200 Subject: [PATCH] Init --- .gitignore | 1 + Cargo.lock | 62 ++++++++++++++++++++++ Cargo.toml | 18 +++++++ src/collectors/mod.rs | 1 + src/collectors/time.rs | 12 +++++ src/collectors/uname.rs | 7 +++ src/collectors/wifi.rs | 0 src/main.rs | 32 ++++++++++++ src/metric.rs | 110 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 243 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/collectors/mod.rs create mode 100644 src/collectors/time.rs create mode 100644 src/collectors/uname.rs create mode 100644 src/collectors/wifi.rs create mode 100644 src/main.rs create mode 100644 src/metric.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4aded78 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,62 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chunked_transfer" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "prometheus_gbl_exporter" +version = "0.1.0" +dependencies = [ + "anyhow", + "tiny_http", +] + +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4739506 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "prometheus_gbl_exporter" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tiny_http = "0.12" +anyhow = "1.0.71" + +# minimalize size +[profile.release] +strip = true +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" diff --git a/src/collectors/mod.rs b/src/collectors/mod.rs new file mode 100644 index 0000000..c25ca52 --- /dev/null +++ b/src/collectors/mod.rs @@ -0,0 +1 @@ +pub mod time; \ No newline at end of file diff --git a/src/collectors/time.rs b/src/collectors/time.rs new file mode 100644 index 0000000..fb349a1 --- /dev/null +++ b/src/collectors/time.rs @@ -0,0 +1,12 @@ +use crate::metric::{Metrics, Metric, MType}; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub fn scrape() -> Metrics { + let mut met = Metrics::new(); + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + met.metrics.push(Metric::new_val("node_time_seconds", MType::Gauge, since_the_epoch.as_secs() as f64)); + return met +} diff --git a/src/collectors/uname.rs b/src/collectors/uname.rs new file mode 100644 index 0000000..3b17a81 --- /dev/null +++ b/src/collectors/uname.rs @@ -0,0 +1,7 @@ +use crate::metric::{Metrics, Metric, MType}; + +pub fn scrape() -> Metrics { + let mut met = Metrics::new(); + met.metrics.push(Metric::new_val("node_time_seconds", MType::Gauge, since_the_epoch.as_secs() as f64)); + return met +} diff --git a/src/collectors/wifi.rs b/src/collectors/wifi.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d83ef7a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,32 @@ +mod metric; +mod collectors; +use tiny_http::{Server, Response, Method}; +use anyhow::{Context, Result}; + +use collectors::time; + + +fn main() -> Result<()> { + let server = Server::http("0.0.0.0:9100").unwrap(); + + for request in server.incoming_requests() { + if request.method() != &Method::Get || request.url() != "/metrics" { + let response = Response::from_string("Not Found").with_status_code(404); + request.respond(response)?; + continue; + } + println!("request! method: {:?}, url: {:?}, headers: {:?}", + request.method(), + request.url(), + request.headers() + ); + + let mut output = String::new(); + + output.push_str(&time::scrape().prometheus()); + + let response = Response::from_string(output); + request.respond(response)?; + } + Ok(()) +} diff --git a/src/metric.rs b/src/metric.rs new file mode 100644 index 0000000..fa727f7 --- /dev/null +++ b/src/metric.rs @@ -0,0 +1,110 @@ + +pub enum MType { + Gauge, + Counter, +} + +impl std::fmt::Display for MType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + MType::Gauge => write!(f, "gauge"), + MType::Counter => write!(f, "counter") + } + } +} + +struct MetricLabel { + label: String, + value: String, +} + +impl MetricLabel { + fn prometheus(&self) -> String { + return format!("{}=\"{}\"", self.label, self.value) + } +} + +struct MetricData { + labels: Vec, + value: f64 +} + +impl MetricData { + fn new(value: f64) -> MetricData { + return MetricData { + labels: Vec::new(), + value: value, + }; + } + fn add_label(&mut self, label: String, value: String) { + self.labels.push(MetricLabel{label: label, value: value}) + } + fn rounded_val(&self) -> String { + if self.value.fract() == 0.0 { + return (self.value.trunc() as i64).to_string(); + } + return self.value.to_string(); + } +} + +pub struct Metric { + name: String, + mtype: MType, + data: Vec +} + +impl Metric { + pub fn new(name: &str, mtype: MType) -> Metric { + return Metric { + name: String::from(name), + mtype: mtype, + data: Vec::new() + }; + } + pub fn new_val(name: &str, mtype: MType, value: f64) -> Metric { + return Metric { + name: String::from(name), + mtype: mtype, + data: vec![MetricData::new(value)] + }; + } +} + +pub struct Metrics { + pub metrics: Vec, + pub processtime: f64 +} + +impl Metrics { + pub fn prometheus(&self) -> String { + let mut type_section = String::new(); + let mut metric_section = String::new(); + + for m in &self.metrics { + type_section.push_str(&format!("# TYPE {} {}\n", m.name, m.mtype)); + for d in &m.data { + let mut labels = String::new(); + for (i, l) in d.labels.iter().enumerate() { + labels.push_str(&l.prometheus()); + if i != d.labels.len()-1 { + labels.push(',') + } + } + if labels.is_empty() { + metric_section.push_str(&format!("{} {}\n", m.name, d.rounded_val())); + } else { + metric_section.push_str(&format!("{}{{{}}} {}\n", m.name, labels, d.rounded_val())); + } + } + } + + return type_section+&metric_section; + } + + pub fn new() -> Metrics { + return Metrics { + metrics: Vec::new(), + processtime: 0.0 + }; + } +}