use std::time::SystemTime;
use reqwest::blocking::Client;
use super::PProfBackend;
#[derive(Debug, Clone)]
#[must_use]
pub struct PyroscopeBackendConfig {
ingestion_url: String,
app_name: String,
}
impl PyroscopeBackendConfig {
pub fn new(application_name: impl Into<String>, ingestion_url: impl Into<String>) -> Self {
Self {
app_name: application_name.into(),
ingestion_url: ingestion_url.into(),
}
}
}
#[must_use]
pub struct PyroscopeBackend {
config: PyroscopeBackendConfig,
client: Client,
}
impl PyroscopeBackend {
pub fn new(config: PyroscopeBackendConfig) -> Self {
Self {
client: Client::builder()
.user_agent(&config.app_name)
.build()
.unwrap(),
config,
}
}
}
impl PProfBackend for PyroscopeBackend {
fn save_profile(
&self,
kind: &str,
start_time: SystemTime,
end_time: SystemTime,
profile_data: Vec<u8>,
) {
let url = format!(
"{}?format=pprof&name={}&from={}&until={}",
self.config.ingestion_url,
self.config.app_name,
start_time
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
end_time
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
);
match self
.client
.post(&url)
.header("Content-Type", "application/octet-stream")
.body(profile_data)
.send()
{
Ok(res) => {
let status = res.status();
if status.is_success() {
tracing::trace!("profile sent to pyroscope");
} else {
let res_text = res.text().unwrap_or_default();
tracing::warn!(
"failed to send profile `{kind}` to pyroscope: ({}) {}",
status,
res_text
);
}
}
Err(error) => {
tracing::warn!("failed to send profile `{kind}` to pyroscope: {}", error);
}
}
}
}