Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### New Features

- Added [`EnvelopeFilter`](https://docs.rs/sentry-types/latest/sentry_types/protocol/v7/trait.EnvelopeFilter.html) and [`EnvelopeFilterCallbacks`](https://docs.rs/sentry-types/latest/sentry_types/protocol/v7/struct.EnvelopeFilterCallbacks.html), which let callers observe envelope items removed by [`Envelope::filter`](https://docs.rs/sentry-types/latest/sentry_types/protocol/v7/struct.Envelope.html#method.filter), including attachments removed after their event or transaction is filtered out. Existing `Envelope::filter` closure callers continue to work, although some closures may require an explicit item type annotation ([#1182](https://github.com/getsentry/sentry-rust/pull/1182)).
- Added [`TransportFactory::create_transport_with_options`](https://docs.rs/sentry-core/latest/sentry_core/trait.TransportFactory.html#method.create_transport_with_options), which constructs transports from [`TransportOptions`](https://docs.rs/sentry-core/latest/sentry_core/struct.TransportOptions.html) instead of full [`ClientOptions`](https://docs.rs/sentry-core/latest/sentry_core/struct.ClientOptions.html) ([#1142](https://github.com/getsentry/sentry-rust/pull/1142)).
- Added transport-specific options types and `with_options` constructors for built-in HTTP transports, including `ReqwestHttpTransportOptions`, `CurlHttpTransportOptions`, `UreqHttpTransportOptions`, and `EmbeddedSVCHttpTransportOptions` ([#1142](https://github.com/getsentry/sentry-rust/pull/1142)).
- Added transport worker options types and deprecated the legacy constructors for built-in background transport workers, including `StdTransportThreadOptions` and `TokioTransportThreadOptions` ([#1142](https://github.com/getsentry/sentry-rust/pull/1142)).
Expand Down
102 changes: 97 additions & 5 deletions sentry-types/src/protocol/envelope.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::mem;
use std::{borrow::Cow, io::Write, path::Path, time::SystemTime};

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -367,6 +368,46 @@ impl Items {
}
}

/// A trait for types which can filter items in envelopes.
///
/// This trait is used by [`Envelope::filter`].
pub trait EnvelopeFilter: private::Sealed {
/// The function used to filter the envelopes.
///
/// A return value of `true` indicates that the item should be kept in the envelope, `false`
/// will filter the value out.
///
/// A value of `true` does not guarantee the item is kept; in particular, [`Envelope::filter`]
/// removes attachments if the corresponding event or transaction item is removed from the
/// envelope, as it no longer makes sense to send them in this case.
fn filter(&mut self, item: &EnvelopeItem) -> bool;

/// A callback which is called with all items removed by filtering, including items for which
/// [`Self::filter`] had returned `true`, such as no-longer-applicable attachments.
fn on_filtered(&mut self, item: EnvelopeItem) {
let _ = item;
}
}

/// A container for callbacks that can be passed to [`Envelope::filter`].
pub struct EnvelopeFilterCallbacks<F, C> {
filter: F,
on_filtered: C,
}

impl<F, C> EnvelopeFilterCallbacks<F, C> {
/// Create a new [`EnvelopeFilterCallbacks`].
///
/// `filter` will be called to determine whether the envelope items should be kept.
/// `on_filtered` will be called on all envelope items which are then dropped.
pub fn new(filter: F, on_filtered: C) -> Self {
Self {
filter,
on_filtered,
}
}
}

/// A Sentry Envelope.
///
/// An Envelope is the data format that Sentry uses for Ingestion. It can contain
Expand Down Expand Up @@ -473,30 +514,43 @@ impl Envelope {
/// contains an [`EnvelopeItem::Event`] or [`EnvelopeItem::Transaction`].
///
/// [`None`] is returned if no items remain in the Envelope after filtering.
pub fn filter<P>(self, mut predicate: P) -> Option<Self>
pub fn filter<F>(self, mut filter: F) -> Option<Self>
where
P: FnMut(&EnvelopeItem) -> bool,
F: EnvelopeFilter,
Comment thread
lcian marked this conversation as resolved.
{
let Items::EnvelopeItems(items) = self.items else {
return if predicate(&EnvelopeItem::Raw) {
return if filter.filter(&EnvelopeItem::Raw) {
Some(self)
} else {
filter.on_filtered(EnvelopeItem::Raw);
None
};
};

let mut filtered = Envelope::new();
for item in items {
if predicate(&item) {
if filter.filter(&item) {
filtered.add_item(item);
} else {
filter.on_filtered(item);
}
}

// filter again, removing attachments which do not make any sense without
// an event/transaction
Comment thread
szokeasaurusrex marked this conversation as resolved.
if filtered.uuid().is_none() {
if let Items::EnvelopeItems(ref mut items) = filtered.items {
items.retain(|item| !matches!(item, EnvelopeItem::Attachment(..)))
let old_items = mem::take(items);
*items = old_items
.into_iter()
.filter_map(|item| match item {
EnvelopeItem::Attachment(..) => {
filter.on_filtered(item);
None
}
_ => Some(item),
})
.collect();
}
}

Expand Down Expand Up @@ -783,6 +837,44 @@ where
}
}

impl<F, C> EnvelopeFilter for EnvelopeFilterCallbacks<F, C>
where
F: FnMut(&EnvelopeItem) -> bool,
C: FnMut(EnvelopeItem),
{
fn filter(&mut self, item: &EnvelopeItem) -> bool {
(self.filter)(item)
}

fn on_filtered(&mut self, item: EnvelopeItem) {
(self.on_filtered)(item);
}
}

impl<F> EnvelopeFilter for F
where
F: FnMut(&EnvelopeItem) -> bool,
{
fn filter(&mut self, item: &EnvelopeItem) -> bool {
self(item)
}
}

mod private {
use super::*;

pub trait Sealed {}

impl<F, C> Sealed for EnvelopeFilterCallbacks<F, C>
where
F: FnMut(&EnvelopeItem) -> bool,
C: FnMut(EnvelopeItem),
{
}

impl<F> Sealed for F where F: FnMut(&EnvelopeItem) -> bool {}
}

#[cfg(test)]
mod test {
use std::str::FromStr;
Expand Down
2 changes: 1 addition & 1 deletion sentry/src/transports/ratelimit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl RateLimiter {
///
/// Returns [`None`] if all the envelope items were filtered out.
pub fn filter_envelope(&self, envelope: Envelope) -> Option<Envelope> {
envelope.filter(|item| {
envelope.filter(|item: &_| {
self.is_enabled(match item {
EnvelopeItem::Event(_) => RateLimitingCategory::Error,
EnvelopeItem::SessionUpdate(_) | EnvelopeItem::SessionAggregates(_) => {
Expand Down
Loading