grafbabe 0.1.0

Simple openmetrics stats collector
use crate::database;
use crate::{ParseError, parse_prometheus};
use mio::Waker;
use rusqlite::Connection;
use std::time::Duration;
use std::{fmt, thread};
use ureq::Agent;

pub fn background_loop(duration: Duration, waker: Waker) {
    std::thread::spawn(move || {
        loop {
            thread::sleep(duration);
            if let Err(e) = waker.wake() {
                log::error!("Error waking up main thread: {e}");
            }
        }
    });
}

pub fn pull_metrics<'url>(
    connection: &mut Connection,
    http_client: &Agent,
    url: &'url str,
) -> Result<(), PullMetricsError<'url>> {
    log::debug!(prometheus_addr:% = url; "Pulling prometheus metrics");

    let body: String = http_client
        .get(url)
        .call()
        .map_err(|cause| PullMetricsError::Request { url, cause })?
        .body_mut()
        .read_to_string()
        .map_err(|cause| PullMetricsError::Request { url, cause })?;
    let snapshot =
        parse_prometheus(&body).map_err(|cause| PullMetricsError::MalformedBody { url, cause })?;
    database::store_snapshot(connection, &snapshot).map_err(PullMetricsError::Database)?;
    database::prune_old_metrics(connection).map_err(PullMetricsError::Database)?;

    Ok(())
}

pub enum PullMetricsError<'url> {
    Request { url: &'url str, cause: ureq::Error },
    MalformedBody { url: &'url str, cause: ParseError },
    Database(rusqlite::Error),
}

impl<'url> fmt::Display for PullMetricsError<'url> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            PullMetricsError::Request { url, cause } => {
                write!(f, "Error making request to {url}: {cause}")
            }
            PullMetricsError::MalformedBody { url, cause } => {
                write!(f, "Error parsing {url} body response: {cause}")
            }
            PullMetricsError::Database(error) => {
                write!(f, "Error writing snapshot to database: {error}")
            }
        }
    }
}