use std::collections::HashMap;
use std::sync::{Arc, Condvar, Mutex};
use std::thread::JoinHandle;
use std::sync::mpsc::channel;
use std::sync::mpsc::{Receiver, Sender};
use crate::backends::pprof::Pprof;
use crate::backends::Backend;
use crate::error::Result;
use crate::session::Session;
use crate::timer::Timer;
#[derive(Clone, Debug)]
pub struct PyroscopeConfig {
pub url: String,
pub application_name: String,
pub tags: HashMap<String, String>,
pub sample_rate: i32,
}
impl PyroscopeConfig {
pub fn new<S: AsRef<str>>(url: S, application_name: S) -> Self {
Self {
url: url.as_ref().to_owned(),
application_name: application_name.as_ref().to_owned(),
tags: HashMap::new(),
sample_rate: 100i32,
}
}
pub fn sample_rate(self, sample_rate: i32) -> Self {
Self {
sample_rate,
..self
}
}
pub fn tags(self, tags: &[(&str, &str)]) -> Self {
let tags_hashmap: HashMap<String, String> = tags
.to_owned()
.iter()
.cloned()
.map(|(a, b)| (a.to_owned(), b.to_owned()))
.collect();
Self {
tags: tags_hashmap,
..self
}
}
}
pub struct PyroscopeAgentBuilder {
backend: Arc<Mutex<dyn Backend>>,
config: PyroscopeConfig,
}
impl PyroscopeAgentBuilder {
pub fn new<S: AsRef<str>>(url: S, application_name: S) -> Self {
Self {
backend: Arc::new(Mutex::new(Pprof::default())), config: PyroscopeConfig::new(url, application_name),
}
}
pub fn backend<T: 'static>(self, backend: T) -> Self
where T: Backend {
Self {
backend: Arc::new(Mutex::new(backend)),
..self
}
}
pub fn sample_rate(self, sample_rate: i32) -> Self {
Self {
config: self.config.sample_rate(sample_rate),
..self
}
}
pub fn tags(self, tags: &[(&str, &str)]) -> Self {
Self {
config: self.config.tags(tags),
..self
}
}
pub fn build(self) -> Result<PyroscopeAgent> {
let backend = Arc::clone(&self.backend);
backend.lock()?.initialize(self.config.sample_rate)?;
let timer = Timer::default().initialize();
Ok(PyroscopeAgent {
backend: self.backend,
config: self.config,
timer,
tx: None,
handle: None,
running: Arc::new((Mutex::new(false), Condvar::new())),
})
}
}
#[derive(Debug)]
pub struct PyroscopeAgent {
pub backend: Arc<Mutex<dyn Backend>>,
timer: Timer,
tx: Option<Sender<u64>>,
handle: Option<JoinHandle<Result<()>>>,
running: Arc<(Mutex<bool>, Condvar)>,
pub config: PyroscopeConfig,
}
impl Drop for PyroscopeAgent {
fn drop(&mut self) {
self.timer.drop_listeners().unwrap(); self.timer.handle.take().unwrap().join().unwrap().unwrap(); }
}
impl PyroscopeAgent {
pub fn builder<S: AsRef<str>>(url: S, application_name: S) -> PyroscopeAgentBuilder {
PyroscopeAgentBuilder::new(url, application_name)
}
pub fn start(&mut self) -> Result<()> {
let backend = Arc::clone(&self.backend);
backend.lock()?.start()?;
let pair = Arc::clone(&self.running);
let (lock, cvar) = &*pair;
let mut running = lock.lock()?;
*running = true;
drop(lock);
drop(cvar);
drop(running);
let (tx, rx): (Sender<u64>, Receiver<u64>) = channel();
self.timer.attach_listener(tx.clone())?;
self.tx = Some(tx.clone());
let config = self.config.clone();
self.handle = Some(std::thread::spawn(move || {
while let Ok(time) = rx.recv() {
let report = backend.lock()?.report()?;
Session::new(time, config.clone(), report)?.send()?;
if time == 0 {
let (lock, cvar) = &*pair;
let mut running = lock.lock()?;
*running = false;
cvar.notify_one();
return Ok(());
}
}
return Ok(());
}));
Ok(())
}
pub fn stop(&mut self) -> Result<()> {
self.tx.take().unwrap().send(0)?;
let pair = Arc::clone(&self.running);
let (lock, cvar) = &*pair;
cvar.wait_while(lock.lock()?, |running| *running)?;
let backend = Arc::clone(&self.backend);
backend.lock()?.stop()?;
Ok(())
}
pub fn add_tags(&mut self, tags: &[(&str, &str)]) -> Result<()> {
self.stop()?;
let tags_hashmap: HashMap<String, String> = tags
.to_owned()
.iter()
.cloned()
.map(|(a, b)| (a.to_owned(), b.to_owned()))
.collect();
self.config.tags.extend(tags_hashmap);
self.start()?;
Ok(())
}
pub fn remove_tags(&mut self, tags: &[&str]) -> Result<()> {
self.stop()?;
tags.iter().for_each(|key| {
self.config.tags.remove(key.to_owned());
});
self.start()?;
Ok(())
}
}