peshming/src/config.rs
2022-04-03 01:49:46 +02:00

151 lines
5.1 KiB
Rust

/********************************************************************************
* Prometheus exporter for monitoring network connectivity using icmp pings *
* *
* Copyright (C) 2019-2020 Jan Christian Grünhage *
* Copyright (C) 2020 Famedly GmbH *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
********************************************************************************/
use anyhow::{Context, Result};
use clap::Parser;
use log::info;
use metrics::{describe_histogram, register_histogram, Unit};
use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle};
use serde::Deserialize;
use serde_with::{serde_as, DurationMilliSeconds};
use std::{collections::HashMap, time::Duration};
use crate::cli::Cli;
pub(crate) struct App {
pub(crate) config: Config,
pub(crate) handle: PrometheusHandle,
}
#[derive(Deserialize, Clone)]
pub(crate) struct Config {
pub(crate) listener: std::net::SocketAddr,
pub(crate) ping: PingConfig,
#[serde(default)]
pub(crate) log: LogConfig,
}
#[serde_as]
#[derive(Deserialize, Clone)]
pub(crate) struct PingConfig {
#[serde_as(as = "DurationMilliSeconds<f64>")]
#[serde(default = "default_timeout")]
pub(crate) timeout: Duration,
#[serde(default = "default_buckets")]
pub(crate) bucket_sizes: Vec<f64>,
pub(crate) hosts: HashMap<std::net::IpAddr, u64>,
}
fn default_timeout() -> Duration {
Duration::from_secs(3)
}
fn default_buckets() -> Vec<f64> {
vec![
0.5, 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0,
350.0, 400.0, 450.0, 500.0, 550.0, 600.0, 650.0, 700.0, 750.0, 800.0, 900.0, 1000.0,
1250.0, 1500.0, 1750.0, 2000.0,
]
}
#[derive(Deserialize, Clone)]
pub(crate) struct LogConfig {
pub(crate) level: log::LevelFilter,
}
impl Default for LogConfig {
fn default() -> Self {
Self {
level: log::LevelFilter::Error,
}
}
}
fn setup_clap() -> Cli {
Cli::parse()
}
fn setup_fern(level: log::LevelFilter) {
match fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}] {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
record.level(),
message
))
})
.level(level)
.chain(std::io::stdout())
.apply()
{
Err(_) => {
eprintln!("error setting up logging!");
}
_ => info!("logging set up properly"),
}
}
fn read_config(path: &std::path::Path) -> Result<Config> {
let config_file_content = std::fs::read_to_string(path).context("Couldn't read config file")?;
toml::from_str(&config_file_content).context("Couldn't parse config file")
}
pub(crate) fn setup_app() -> Result<App> {
let cli = setup_clap();
let config = read_config(&cli.config).context("Couldn't read config file!")?;
setup_fern(determine_level(cli.verbose, config.log.level));
let handle = setup_prometheus(&config)?;
Ok(App { config, handle })
}
pub(crate) fn setup_prometheus(config: &Config) -> Result<PrometheusHandle> {
let handle = PrometheusBuilder::new()
.set_buckets_for_metric(
Matcher::Full("ping_rtt_milliseconds".into()),
&config.ping.bucket_sizes,
)?
.install_recorder()?;
for target in config.ping.hosts.keys() {
register_histogram!("ping_rtt_milliseconds", "target" => target.to_string());
}
describe_histogram!(
"ping_rtt_milliseconds",
Unit::Milliseconds,
"The ping round trip time in milliseconds"
);
Ok(handle)
}
fn determine_level(verbose_occurrences: usize, config_level: log::LevelFilter) -> log::LevelFilter {
let cli_level = match verbose_occurrences {
0 => log::LevelFilter::Error,
1 => log::LevelFilter::Warn,
2 => log::LevelFilter::Info,
3 => log::LevelFilter::Debug,
_ => log::LevelFilter::Trace,
};
Ord::max(cli_level, config_level)
}