Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(profiling): Add support for Profiling #479

Merged
merged 12 commits into from Jul 1, 2022
10 changes: 9 additions & 1 deletion sentry-core/Cargo.toml
Expand Up @@ -26,14 +26,22 @@ client = ["rand"]
# and macros actually expand features (and extern crate) where they are used!
debug-logs = ["log_"]
test = ["client"]
profiling = ["pprof", "build_id", "uuid", "sys-info", "findshlibs"]

[dependencies]
log_ = { package = "log", version = "0.4.8", optional = true, features = ["std"] }
once_cell = "1"
rand = { version = "0.8.1", optional = true }
sentry-types = { version = "0.27.0", path = "../sentry-types" }
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.46"
serde_json = { version = "1.0.46" }
uuid = { version = "1.0.0", features = ["v4", "serde"], optional = true }
sys-info = { version = "0.9.1", optional = true }
build_id = { version = "0.2.1", optional = true }
findshlibs = { version = "=0.10.2", optional = true }

[target.'cfg(not(target_os = "windows"))'.dependencies]
pprof = { version = "0.10.0", optional = true }

[dev-dependencies]
# Because we re-export all the public API in `sentry`, we actually run all the
Expand Down
16 changes: 3 additions & 13 deletions sentry-core/src/client.rs
Expand Up @@ -215,7 +215,7 @@ impl Client {
scope.update_session_from_event(&event);
}

if !self.sample_should_send() {
if !self.sample_should_send(self.options.sample_rate) {
None
} else {
Some(event)
Expand Down Expand Up @@ -338,19 +338,9 @@ impl Client {
}
}

fn sample_should_send(&self) -> bool {
let rate = self.options.sample_rate;
if rate >= 1.0 {
true
} else {
random::<f32>() <= rate
}
}

/// Returns a random boolean with a probability defined
/// by the [`ClientOptions`]'s `traces_sample_rate`
pub fn sample_traces_should_send(&self) -> bool {
let rate = self.options.traces_sample_rate;
/// by rate
pub fn sample_should_send(&self, rate: f32) -> bool {
if rate >= 1.0 {
true
} else {
Expand Down
12 changes: 12 additions & 0 deletions sentry-core/src/clientoptions.rs
Expand Up @@ -74,6 +74,14 @@ pub struct ClientOptions {
pub sample_rate: f32,
/// The sample rate for tracing transactions. (0.0 - 1.0, defaults to 0.0)
pub traces_sample_rate: f32,
/// Enables profiling
pub enable_profiling: bool,
/// The sample rate for profiling a transactions. (0.0 - 1.0, defaults to 0.0)
///
/// This is dependent on `traces_sample_rate`. The probability of sending a profile
/// is given by `traces_sample_rate * profiles_sample_rate`.
/// If a given transaction is not sent, then the profile won't be sent neither.
pub profiles_sample_rate: f32,
/// Maximum number of breadcrumbs. (defaults to 100)
pub max_breadcrumbs: usize,
/// Attaches stacktraces to messages.
Expand Down Expand Up @@ -183,6 +191,8 @@ impl fmt::Debug for ClientOptions {
.field("environment", &self.environment)
.field("sample_rate", &self.sample_rate)
.field("traces_sample_rate", &self.traces_sample_rate)
.field("enable_profiling", &self.enable_profiling)
.field("profiles_sample_rate", &self.profiles_sample_rate)
.field("max_breadcrumbs", &self.max_breadcrumbs)
.field("attach_stacktrace", &self.attach_stacktrace)
.field("send_default_pii", &self.send_default_pii)
Expand Down Expand Up @@ -215,6 +225,8 @@ impl Default for ClientOptions {
environment: None,
sample_rate: 1.0,
traces_sample_rate: 0.0,
enable_profiling: false,
profiles_sample_rate: 0.0,
max_breadcrumbs: 100,
attach_stacktrace: false,
send_default_pii: false,
Expand Down
3 changes: 3 additions & 0 deletions sentry-core/src/lib.rs
Expand Up @@ -92,6 +92,9 @@ pub use crate::client::Client;
#[cfg(feature = "test")]
pub mod test;

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
mod profiling;

// public api from other crates
#[doc(inline)]
pub use sentry_types as types;
Expand Down
33 changes: 30 additions & 3 deletions sentry-core/src/performance.rs
@@ -1,6 +1,8 @@
use std::sync::Arc;
use std::sync::Mutex;

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
viglia marked this conversation as resolved.
Show resolved Hide resolved
use crate::profiling;
use crate::{protocol, Hub};

#[cfg(feature = "client")]
Expand Down Expand Up @@ -269,8 +271,10 @@ pub(crate) struct TransactionInner {
#[cfg(feature = "client")]
client: Option<Arc<Client>>,
sampled: bool,
context: protocol::TraceContext,
pub(crate) context: protocol::TraceContext,
pub(crate) transaction: Option<protocol::Transaction<'static>>,
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
pub(crate) profiler_guard: Option<profiling::ProfilerGuard>,
}

type TransactionArc = Arc<Mutex<TransactionInner>>;
Expand All @@ -297,8 +301,9 @@ impl Transaction {

let (sampled, mut transaction) = match client.as_ref() {
Some(client) => (
ctx.sampled
.unwrap_or_else(|| client.sample_traces_should_send()),
ctx.sampled.unwrap_or_else(|| {
client.sample_should_send(client.options().traces_sample_rate)
}),
Some(protocol::Transaction {
name: Some(ctx.name),
..Default::default()
Expand All @@ -313,13 +318,23 @@ impl Transaction {
transaction = None;
client = None;
}
// if the transaction was sampled then a profile, linked to the transaction,
// might as well be sampled
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
let profiler_guard = if sampled {
client.as_deref().and_then(profiling::start_profiling)
} else {
None
};

Self {
inner: Arc::new(Mutex::new(TransactionInner {
client,
sampled,
context,
transaction,
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
profiler_guard,
})),
}
}
Expand All @@ -339,6 +354,8 @@ impl Transaction {
sampled,
context,
transaction: None,
#[cfg(all(feature = "profiling", not(target_os = "windows")))]
profiler_guard: None,
})),
}
}
Expand Down Expand Up @@ -404,9 +421,19 @@ impl Transaction {
transaction.environment = opts.environment.clone();
transaction.sdk = Some(std::borrow::Cow::Owned(client.sdk_info.clone()));

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
let profile = inner.profiler_guard.take().and_then(|profiler_guard| {
profiling::finish_profiling(&transaction, profiler_guard, inner.context.trace_id)
});

let mut envelope = protocol::Envelope::new();
envelope.add_item(transaction);

#[cfg(all(feature = "profiling", not(target_os = "windows")))]
if let Some(profile) = profile {
envelope.add_item(profile);
}

client.send_envelope(envelope)
}
}
Expand Down