diff --git a/examples/error.rs b/examples/error.rs new file mode 100644 index 00000000..15a00015 --- /dev/null +++ b/examples/error.rs @@ -0,0 +1,42 @@ +// Copyright 2021 Developers of Pyroscope. + +// Licensed under the Apache License, Version 2.0 . This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate pyroscope; + +use pyroscope::{PyroscopeAgent, Result}; + +fn fibonacci(n: u64) -> u64 { + match n { + 0 | 1 => 1, + n => fibonacci(n - 1) + fibonacci(n - 2), + } +} + +fn main() -> Result<()> { +// Force rustc to display the log messages in the console. + std::env::set_var("RUST_LOG", "trace"); + + // Initialize the logger. + pretty_env_logger::init_timed(); + + // This example should fail and return an error. + println!("This example should fail and return an error."); + println!("Run this with: RUST_BACKTRACE=1 cargo run --example error"); + + let mut agent = PyroscopeAgent::builder("http://invalid_url", "example.error") + .build()?; + // Start Agent + agent.start()?; + + let _result = fibonacci(47); + + // Stop Agent + agent.stop()?; + + drop(agent); + + Ok(()) +} diff --git a/src/backends/pprof.rs b/src/backends/pprof.rs index a277586f..c4097335 100644 --- a/src/backends/pprof.rs +++ b/src/backends/pprof.rs @@ -8,6 +8,7 @@ use pprof::{ProfilerGuard, ProfilerGuardBuilder, Report}; use crate::backends::Backend; use crate::backends::State; +use crate::PyroscopeError; use crate::Result; #[derive(Default)] @@ -31,9 +32,7 @@ impl Backend for Pprof<'_> { fn initialize(&mut self, sample_rate: i32) -> Result<()> { // Check if Backend is Uninitialized if self.state != State::Uninitialized { - return Err(crate::error::PyroscopeError { - msg: String::from("Pprof Backend is already Initialized"), - }); + return Err(PyroscopeError::new("Pprof Backend is already Initialized")); } // Construct a ProfilerGuardBuilder @@ -50,9 +49,7 @@ impl Backend for Pprof<'_> { fn start(&mut self) -> Result<()> { // Check if Backend is Ready if self.state != State::Ready { - return Err(crate::error::PyroscopeError { - msg: String::from("Pprof Backend is not Ready"), - }); + return Err(PyroscopeError::new("Pprof Backend is not Ready")); } self.guard = Some(self.inner_builder.as_ref().unwrap().clone().build()?); @@ -66,9 +63,7 @@ impl Backend for Pprof<'_> { fn stop(&mut self) -> Result<()> { // Check if Backend is Running if self.state != State::Running { - return Err(crate::error::PyroscopeError { - msg: String::from("Pprof Backend is not Running"), - }); + return Err(PyroscopeError::new("Pprof Backend is not Running")); } // drop the guard @@ -83,9 +78,7 @@ impl Backend for Pprof<'_> { fn report(&mut self) -> Result> { // Check if Backend is Running if self.state != State::Running { - return Err(crate::error::PyroscopeError { - msg: String::from("Pprof Backend is not Running"), - }); + return Err(PyroscopeError::new("Pprof Backend is not Running")); } let mut buffer = Vec::new(); @@ -102,7 +95,9 @@ impl Backend for Pprof<'_> { // Copyright: https://github.com/YangKeao fn fold(report: &Report, with_thread_name: bool, mut writer: W) -> Result<()> -where W: std::io::Write { +where + W: std::io::Write, +{ for (key, value) in report.data.iter() { if with_thread_name { if !key.thread_name.is_empty() { diff --git a/src/error.rs b/src/error.rs index 5ee48322..f7fb66f8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,9 +11,10 @@ use thiserror::Error; pub type Result = std::result::Result; /// Error type of Pyroscope -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug)] pub struct PyroscopeError { pub msg: String, + source: Option>, } impl fmt::Display for PyroscopeError { @@ -22,34 +23,65 @@ impl fmt::Display for PyroscopeError { } } +impl Default for PyroscopeError { + fn default() -> Self { + PyroscopeError { + msg: "".to_string(), + source: None, + } + } +} + +impl PyroscopeError { + /// Create a new instance of PyroscopeError + pub fn new(msg: &str) -> Self { + PyroscopeError { + msg: msg.to_string(), + source: None, + } + } + + /// Create a new instance of PyroscopeError with source + pub fn new_with_source(msg: &str, source: Box) -> Self { + PyroscopeError { + msg: msg.to_string(), + source: Some(source), + } + } +} + impl From for PyroscopeError { - fn from(_err: reqwest::Error) -> Self { + fn from(err: reqwest::Error) -> Self { PyroscopeError { msg: String::from("reqwest Error"), + source: Some(Box::new(err)), } } } impl From for PyroscopeError { - fn from(_err: pprof::Error) -> Self { + fn from(err: pprof::Error) -> Self { PyroscopeError { msg: String::from("pprof Error"), + source: Some(Box::new(err)), } } } impl From for PyroscopeError { - fn from(_err: std::time::SystemTimeError) -> Self { + fn from(err: std::time::SystemTimeError) -> Self { PyroscopeError { msg: String::from("SystemTime Error"), + source: Some(Box::new(err)), } } } impl From for PyroscopeError { - fn from(_err: std::io::Error) -> Self { + fn from(err: std::io::Error) -> Self { PyroscopeError { msg: String::from("IO Error"), + source: Some(Box::new(err)), } } } @@ -57,15 +89,17 @@ impl From for PyroscopeError { impl From> for PyroscopeError { fn from(_err: std::sync::PoisonError) -> Self { PyroscopeError { - msg: String::from("Poison/Mutex Error"), + msg: String::from("Poison Error"), + source: None, } } } -impl From> for PyroscopeError { - fn from(_err: std::sync::mpsc::SendError) -> Self { +impl From> for PyroscopeError { + fn from(err: std::sync::mpsc::SendError) -> Self { PyroscopeError { - msg: String::from("mpsc Send Error"), + msg: String::from("SendError Error"), + source: Some(Box::new(err)), } } } diff --git a/src/session.rs b/src/session.rs index 21b7280f..9ddcd05b 100644 --- a/src/session.rs +++ b/src/session.rs @@ -54,8 +54,8 @@ impl SessionManager { } SessionSignal::Kill => { // Kill the session manager - return Ok(()); log::trace!("SessionManager - Kill signal received"); + return Ok(()); } } } @@ -89,7 +89,7 @@ pub struct Session { impl Session { /// Create a new Session - /// # Example + /// # Example /// ```ignore /// let config = PyroscopeConfig::new("https://localhost:8080", "my-app"); /// let report = vec![1, 2, 3]; @@ -131,39 +131,38 @@ impl Session { pub fn send(self) -> Result<()> { log::info!("Session - Sending Session"); - let _handle: JoinHandle> = thread::spawn(move || { - if self.report.is_empty() { - return Ok(()); - } - - let client = reqwest::blocking::Client::new(); - // TODO: handle the error of this request - - // Clone URL - let url = self.config.url.clone(); - - // Merge application name with Tags - let application_name = merge_tags_with_app_name( - self.config.application_name.clone(), - self.config.tags.clone(), - )?; - - client - .post(format!("{}/ingest", url)) - .header("Content-Type", "binary/octet-stream") - .query(&[ - ("name", application_name.as_str()), - ("from", &format!("{}", self.from)), - ("until", &format!("{}", self.until)), - ("format", "folded"), - ("sampleRate", &format!("{}", self.config.sample_rate)), - ("spyName", "pprof-rs"), - ]) - .body(self.report) - .send()?; - + // Check if the report is empty + if self.report.is_empty() { return Ok(()); - }); + } + + // Create a new client + let client = reqwest::blocking::Client::new(); + + // Clone URL + let url = self.config.url.clone(); + + // Merge application name with Tags + let application_name = merge_tags_with_app_name( + self.config.application_name.clone(), + self.config.tags.clone(), + )?; + + // Create and send the request + client + .post(format!("{}/ingest", url)) + .header("Content-Type", "binary/octet-stream") + .query(&[ + ("name", application_name.as_str()), + ("from", &format!("{}", self.from)), + ("until", &format!("{}", self.until)), + ("format", "folded"), + ("sampleRate", &format!("{}", self.config.sample_rate)), + ("spyName", "pyroscope-rs"), + ]) + .body(self.report) + .timeout(std::time::Duration::from_secs(10)) + .send()?; Ok(()) }