➕ 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