➕ update: measuring CPU usage every minute
parent
1e5aa528c9
commit
f335644b03
|
@ -1653,6 +1653,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -18,6 +18,7 @@ structopt = "0.3.20"
|
|||
tokio = { version = "0.2.22", features = ["full"] }
|
||||
tracing = "0.1.21"
|
||||
tracing-subscriber = "0.2.15"
|
||||
uom = "0.30.0"
|
||||
|
||||
ptth_core = { path = "../ptth_core" }
|
||||
ptth_server = { path = "../ptth_server" }
|
||||
|
|
|
@ -97,24 +97,46 @@ async fn main () -> Result <(), anyhow::Error> {
|
|||
config_file.name.unwrap_or_else (|| "PTTH File Server".to_string ())
|
||||
);
|
||||
|
||||
let metrics_gauges = Arc::new (ArcSwap::default ());
|
||||
let metrics_interval = Arc::new (ArcSwap::default ());
|
||||
|
||||
let gauge_writer = Arc::clone (&metrics_gauges);
|
||||
let interval_writer = Arc::clone (&metrics_interval);
|
||||
|
||||
tokio::spawn (async move {
|
||||
let mut interval = tokio::time::interval (std::time::Duration::from_secs (2));
|
||||
use std::time::Duration;
|
||||
|
||||
use uom::si::ratio::percent;
|
||||
|
||||
let mut interval = tokio::time::interval (Duration::from_secs (60));
|
||||
|
||||
let mut counter = 0_u64;
|
||||
let mut next_10_time = counter;
|
||||
let mut metrics_at_last_10: Arc <Option <metrics::Interval>> = Arc::new (None);
|
||||
|
||||
loop {
|
||||
interval.tick ().await;
|
||||
|
||||
let new_gauges = match file_server::metrics::Gauges::new ().await {
|
||||
let new_interval_metrics = match file_server::metrics::Interval::new ().await {
|
||||
Err (e) => {
|
||||
error! ("Failed to update gauge metrics: {:?}", e);
|
||||
error! ("Failed to update interval metrics: {:?}", e);
|
||||
continue;
|
||||
},
|
||||
Ok (x) => x,
|
||||
};
|
||||
let new_gauges = Arc::new (Some (new_gauges));
|
||||
gauge_writer.store (new_gauges);
|
||||
let new_interval_metrics = Arc::new (Some (new_interval_metrics));
|
||||
|
||||
if counter >= next_10_time {
|
||||
if let (Some (old), Some (new)) = (&*metrics_at_last_10, &*new_interval_metrics) {
|
||||
let diff = new.cpu_usage.clone () - old.cpu_usage.clone ();
|
||||
trace! ("CPU usage: {}%", diff.get::<percent> ());
|
||||
}
|
||||
|
||||
next_10_time += 1;
|
||||
metrics_at_last_10 = new_interval_metrics.clone ();
|
||||
}
|
||||
|
||||
interval_writer.store (new_interval_metrics);
|
||||
counter += 1;
|
||||
//trace! ("interval metrics 1");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -124,7 +146,7 @@ async fn main () -> Result <(), anyhow::Error> {
|
|||
},
|
||||
handlebars,
|
||||
metrics_startup,
|
||||
metrics_gauges,
|
||||
metrics_interval,
|
||||
hidden_path: Some (path),
|
||||
});
|
||||
|
||||
|
|
|
@ -52,12 +52,12 @@ pub async fn serve_root (
|
|||
#[derive (Serialize)]
|
||||
struct RootHtml <'a> {
|
||||
metrics_startup: &'a metrics::Startup,
|
||||
metrics_gauges: &'a Option <metrics::Gauges>,
|
||||
metrics_interval: &'a Option <metrics::Interval>,
|
||||
}
|
||||
|
||||
let params = RootHtml {
|
||||
metrics_startup: &state.metrics_startup,
|
||||
metrics_gauges: &**state.metrics_gauges.load (),
|
||||
metrics_interval: &**state.metrics_interval.load (),
|
||||
};
|
||||
|
||||
let s = state.handlebars.render ("file_server_root", ¶ms)?;
|
||||
|
|
|
@ -2,15 +2,10 @@ use chrono::{DateTime, Utc};
|
|||
use tracing::debug;
|
||||
use ulid::Ulid;
|
||||
|
||||
fn serialize_ulid <S: serde::Serializer> (t: &Ulid, s: S)
|
||||
-> Result <S::Ok, S::Error>
|
||||
{
|
||||
let t = t.to_string ();
|
||||
s.serialize_str (&t)
|
||||
}
|
||||
|
||||
// Instance metrics are captured when the ptth_server process starts.
|
||||
// They don't change after that.
|
||||
// Metrics are named for when they're updated:
|
||||
// - Startup (Once, when the server state is initialized)
|
||||
// - Interval (About once per minute)
|
||||
// - Events (When a request is processed)
|
||||
|
||||
#[derive (Debug, serde::Serialize)]
|
||||
pub struct Startup {
|
||||
|
@ -33,23 +28,45 @@ pub struct Startup {
|
|||
pub startup_utc: DateTime <Utc>,
|
||||
}
|
||||
|
||||
// Gauges are things we instananeously measure on a fixed interval.
|
||||
// They are not read back and accumulated like counters.
|
||||
|
||||
#[derive (Debug, serde::Serialize)]
|
||||
pub struct Gauges {
|
||||
pub struct Interval {
|
||||
pub utc: DateTime <Utc>,
|
||||
pub rss_mib: u64,
|
||||
|
||||
// What's the difference?
|
||||
pub cpu_time_user: f64,
|
||||
pub cpu_time_system: f64,
|
||||
|
||||
#[serde (skip)]
|
||||
pub cpu_usage: heim::process::CpuUsage,
|
||||
}
|
||||
|
||||
impl Gauges {
|
||||
pub struct Events {
|
||||
|
||||
}
|
||||
|
||||
impl Startup {
|
||||
#[must_use]
|
||||
pub fn new (server_name: String) -> Self
|
||||
{
|
||||
let x = Self {
|
||||
machine_id: get_machine_id (),
|
||||
git_version: None,
|
||||
server_name,
|
||||
instance_id: ulid::Ulid::new (),
|
||||
startup_utc: Utc::now (),
|
||||
};
|
||||
|
||||
debug! ("metrics at startup: {:?}", x);
|
||||
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_ulid <S: serde::Serializer> (t: &Ulid, s: S)
|
||||
-> Result <S::Ok, S::Error>
|
||||
{
|
||||
let t = t.to_string ();
|
||||
s.serialize_str (&t)
|
||||
}
|
||||
|
||||
impl Interval {
|
||||
pub async fn new () -> Result <Self, super::FileServerError> {
|
||||
use tokio::join;
|
||||
use heim::process;
|
||||
|
@ -61,33 +78,22 @@ impl Gauges {
|
|||
|
||||
let our_process = process::current ().await?;
|
||||
|
||||
let cpu_time = our_process.cpu_time ();
|
||||
let cpu_usage = our_process.cpu_usage ();
|
||||
|
||||
let (cpu_time, cpu_usage) = join! (
|
||||
cpu_time,
|
||||
let (cpu_usage, mem) = join! {
|
||||
cpu_usage,
|
||||
);
|
||||
|
||||
let cpu_time = cpu_time?;
|
||||
|
||||
let cpu_time_user = cpu_time.user ().get::<second> ();
|
||||
let cpu_time_system = cpu_time.system ().get::<second> ();
|
||||
our_process.memory ()
|
||||
};
|
||||
let cpu_usage = cpu_usage?;
|
||||
|
||||
let mem = our_process.memory ().await?;
|
||||
let mem = mem?;
|
||||
let rss_mib = mem.rss ().get::<mebibyte> ();
|
||||
|
||||
let x = Gauges {
|
||||
let x = Self {
|
||||
utc: Utc::now (),
|
||||
rss_mib,
|
||||
cpu_time_user,
|
||||
cpu_time_system,
|
||||
cpu_usage,
|
||||
};
|
||||
|
||||
debug! ("metric gauges: {:?}", x);
|
||||
|
||||
Ok (x)
|
||||
}
|
||||
}
|
||||
|
@ -110,24 +116,6 @@ fn get_machine_id () -> Option <String> {
|
|||
Some (s)
|
||||
}
|
||||
|
||||
impl Startup {
|
||||
#[must_use]
|
||||
pub fn new (server_name: String) -> Self
|
||||
{
|
||||
let x = Self {
|
||||
machine_id: get_machine_id (),
|
||||
git_version: None,
|
||||
server_name,
|
||||
instance_id: ulid::Ulid::new (),
|
||||
startup_utc: Utc::now (),
|
||||
};
|
||||
|
||||
debug! ("metrics at startup: {:?}", x);
|
||||
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg (test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -59,7 +59,7 @@ pub struct State {
|
|||
pub config: Config,
|
||||
pub handlebars: handlebars::Handlebars <'static>,
|
||||
pub metrics_startup: metrics::Startup,
|
||||
pub metrics_gauges: Arc <ArcSwap <Option <metrics::Gauges>>>,
|
||||
pub metrics_interval: Arc <ArcSwap <Option <metrics::Interval>>>,
|
||||
pub hidden_path: Option <PathBuf>,
|
||||
}
|
||||
|
||||
|
|
|
@ -201,7 +201,7 @@ pub async fn run_server (
|
|||
let handlebars = file_server::load_templates (&asset_root)?;
|
||||
|
||||
let metrics_startup = file_server::metrics::Startup::new (config_file.name);
|
||||
let metrics_gauges = Arc::new (ArcSwap::default ());
|
||||
let metrics_interval = Arc::new (ArcSwap::default ());
|
||||
|
||||
let state = Arc::new (State {
|
||||
file_server: file_server::State {
|
||||
|
@ -210,7 +210,7 @@ pub async fn run_server (
|
|||
},
|
||||
handlebars,
|
||||
metrics_startup,
|
||||
metrics_gauges,
|
||||
metrics_interval,
|
||||
hidden_path,
|
||||
},
|
||||
config: Config {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<h2>Gauges</h2>
|
||||
|
||||
<p>RSS MiB: {{metrics_gauges.rss_mib}}</p>
|
||||
<p>RSS MiB: {{metrics_interval.rss_mib}}</p>
|
||||
|
||||
<div class="entry_list">
|
||||
|
||||
|
|
Loading…
Reference in New Issue