Filtering

Learn more about how to configure your SDK to filter events reported to Sentry.

When you add Sentry to your app, you get a lot of valuable information about errors and performance. And lots of information is good -- as long as it's the right information, at a reasonable volume.

The Sentry SDKs have several configuration options to help you filter out events.

We also offer Inbound Filters to filter events in sentry.io. We recommend filtering at the client level though, because it removes the overhead of sending events you don't actually want. Learn more about the fields available in an event.

Configure your SDK to filter error events by using the before_send callback method and configuring, enabling, or disabling integrations.

All Sentry SDKs support the before_send callback method. Because it's called immediately before the event is sent to the server, this is your last chance to decide not to send data or to edit it. before_send receives the event object as a parameter, which you can use to either modify the event’s data or drop it completely by returning null, based on custom logic and the data available on the event.

Copied
#include <sentry.h>

sentry_value_t strip_sensitive_data(sentry_value_t event, void *hint) {
  /* modify event here or return NULL to discard the event */
  return event;
}

int main(void) {
  sentry_options_t *options = sentry_options_new();
  sentry_options_set_before_send(options, strip_sensitive_data, NULL);
  sentry_init(options);

  /* ... */
}

The callback is executed in the same thread as the call to sentry_capture_event. Work performed by the function may thus block the executing thread. For this reason, consider avoiding heavy work in before_send.

The before_send callback implementation in sentry-native makes it hard to distinguish between normal events and crashes. For this reason, we introduced another callback, on_crash, which - at this point - only exists in sentry_native:

Copied
#include <sentry.h>

static sentry_value_t
crash_cleanup(
    const sentry_ucontext_t *uctx, // provides the user-space context of the crash
    sentry_value_t event,          // used the same way as in `before_send`
    void *closure                  // user-data that you can provide at configuration time
)
{
    // Do contextual clean-up before the crash is sent to sentry's backend infrastructure

    /* ... */

    // tell the backend to retain the event (+ dump)
    // or to discard it, you could free the event and return a `null`:
    //       sentry_value_decref(event);
    //       return sentry_value_new_null();
    return event;
}

int main(void) {
  sentry_options_t *options = sentry_options_new();
  sentry_options_set_on_crash(options, crash_cleanup, NULL);
  sentry_init(options);

  /* ... */
}

The on_crash callback replaces before_send as a callback for crash events only. They can be defined simultaneously, where the SDK prevents before_send from being invoked for crash events. This allows for better differentiation between crashes and other events and gradual migration from existing before_send implementations:

  • If you have a before_send implementation and do not define an on_crash callback before_send will receive both normal and crash events as before
  • If you only want to pre-process normal events with before_send, then you can define an "empty" on_crash callback that returns the passed-in event and does nothing else.
  • If you are not interested in pre-processing normal events but only want to act on crashes, then only define an on_crash callback with the option to filter (available for all backends) or enrich (only for inproc) the crash event.

Note also that breadcrumbs can be filtered, as discussed in our Breadcrumbs documentation.

The before_send callback is passed both the event and a second argument, hint, that holds one or more hints.

Typically, a hint holds the original exception so that additional data can be extracted or grouping is affected. In this example, the fingerprint is forced to a common value if an exception of a certain type has been caught:

Copied
#include <string.h>
#include <sentry.h>

sentry_value_t before_send(sentry_value_t event, void *hint) {
  /* sentry_value_t functions handle NULL automatically */
  sentry_value_t exceptions = sentry_value_get_by_key(event, "exception");
  sentry_value_t values = sentry_value_get_by_key(exceptions, "values");
  sentry_value_t exception = sentry_value_get_by_index(values, 0);
  sentry_value_t type = sentry_value_get_by_key(exception, "type");
  const char *type_str = sentry_value_as_string(type);

  /* use the data passed during initialization */
  const char *custom_error = (const char *)hint;

  if (type_str && strcmp(type_str, custom_error) == 0) {
    sentry_value_t fingerprint = sentry_value_new_list();
    sentry_value_append(fingerprint, sentry_value_new_string("custom-error"));
    sentry_value_set_by_key(event, "fingerprint", fingerprint);
  }

  return event;
}

int main(void) {
  sentry_options_t *options = sentry_options_new();
  sentry_options_set_before_send(options, before_send, (void *)"CustomError");
  sentry_init(options);

  /* ... */
}

When the SDK creates an event or breadcrumb for transmission, that transmission is typically created from some sort of source object. For instance, an error event is typically created from a log record or exception instance. For better customization, SDKs send these objects to certain callbacks (before_send, before_breadcrumb or the event processor system in the SDK).

To prevent certain transactions from being reported to Sentry, use the traces_sampler or before_send_transaction configuration option, which allows you to provide a function to evaluate the current transaction and drop it if it's not one you want.

In its simplest form, used just for filtering the transaction, it looks like this:

Copied
static double
traces_sampler_callback(const sentry_transaction_context_t *transaction_ctx,
    sentry_value_t custom_sampling_ctx, const int *parent_sampled)
{
    // check if there was a parent sampling decision
    if (parent_sampled != NULL) {
        return *parent_sampled; // Inherit parent's sampling decision
    }
    // a sampling decision can be made on the transaction context name and operation
    if (strcmp(sentry_transaction_context_get_name(transaction_ctx),
        "little.teapot") == 0) {
        if (strcmp(sentry_transaction_context_get_operation(transaction_ctx),
            "Short and stout here is my handle and here is my spout") == 0) {
            // use the custom_sampling_ctx as a key-value dictionary
            if (sentry_value_as_int32(
                sentry_value_get_by_key(custom_sampling_ctx, "b")) == 42) {
                return 1;
            }
        }
    }
    return 0;
}

sentry_options_t *options = sentry_options_new();
sentry_options_set_traces_sampler(options, traces_sampler_callback);
sentry_init(options);

It also allows you to sample different transactions at different rates.

If the transaction currently being processed has a parent transaction (from an upstream service calling this service), the parent (upstream) sampling decision will always be included in the parent_sampled parameter, so that your traces_sampler can choose whether and when to inherit that decision. In most cases, inheritance is the right choice, to avoid breaking distributed traces. A broken trace will not include all your services. See Inheriting the parent sampling decision to learn more.

Learn more about configuring the sample rate.

Copied

static sentry_value_t
before_transaction_callback(sentry_value_t tx, void *user_data)
{
    (void)user_data;
      // throw out any transaction while a tag is active
    if (!sentry_value_is_null(sentry_value_get_by_key(tx, "tags"))) {
        sentry_value_decref(tx);
        return sentry_value_new_null();
    }
    // replace the transaction name with a custom one otherwise
    sentry_value_set_by_key(
        tx, "transaction", sentry_value_new_string("little.coffeepot"));
    return tx;
}

sentry_options_t *options = sentry_options_new();
sentry_options_set_before_transaction(options, before_transaction_callback, NULL);
sentry_init(options);
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").