From 7a812eaf7deec615a9e479b6410c1b5767bbb05d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 17:19:35 +0200 Subject: [PATCH 01/11] Implement before_send_feedback --- include/sentry.h | 22 ++++++++++++++++++++++ src/sentry_core.c | 14 ++++++++++++++ src/sentry_options.c | 8 ++++++++ src/sentry_options.h | 2 ++ 4 files changed, 46 insertions(+) diff --git a/include/sentry.h b/include/sentry.h index daac01a58..9c0180c72 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -3766,6 +3766,28 @@ SENTRY_API void sentry_capture_feedback_with_hint( SENTRY_API sentry_uuid_t sentry_scope_capture_feedback( sentry_scope_t *scope, sentry_value_t user_feedback, sentry_hint_t *hint); +/** + * Type of the `before_send_feedback` callback. + * + * The callback takes ownership of the `feedback` event and should usually + * return that same event. In case the feedback should be discarded, the + * callback needs to call `sentry_value_decref` on the provided event and + * return a `sentry_value_new_null()` instead. + * + * Feedback events do not go through the `before_send` callback. + */ +typedef sentry_value_t (*sentry_before_send_feedback_function_t)( + sentry_value_t feedback, void *hint, void *user_data); + +/** + * Sets the `before_send_feedback` callback. + * + * See the `sentry_before_send_feedback_function_t` typedef above for more + * information. + */ +SENTRY_API void sentry_options_set_before_send_feedback(sentry_options_t *opts, + sentry_before_send_feedback_function_t func, void *user_data); + /** * The status of a Span or Transaction. * diff --git a/src/sentry_core.c b/src/sentry_core.c index fe75a89a9..1ad108834 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -854,6 +854,20 @@ prepare_user_feedback(const sentry_options_t *options, sentry__scope_apply_to_event(scope, options, event, SENTRY_SCOPE_NONE); } + if (options->before_send_feedback_func) { + SENTRY_DEBUG("invoking `before_send_feedback` hook"); + event = options->before_send_feedback_func( + event, hint, options->before_send_feedback_data); + if (sentry_value_is_null(event)) { + SENTRY_DEBUG( + "feedback was discarded by the `before_send_feedback` hook"); + sentry__client_report_discard(SENTRY_DISCARD_REASON_BEFORE_SEND, + SENTRY_DATA_CATEGORY_FEEDBACK, 1); + sentry__attachments_free(all_attachments); + return NULL; + } + } + sentry__ensure_event_id(event, event_id); sentry_envelope_t *envelope = sentry__envelope_new(); diff --git a/src/sentry_options.c b/src/sentry_options.c index ee18a5e6c..e7c780750 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -255,6 +255,14 @@ sentry_options_set_before_breadcrumb(sentry_options_t *opts, opts->before_breadcrumb_data = user_data; } +void +sentry_options_set_before_send_feedback(sentry_options_t *opts, + sentry_before_send_feedback_function_t func, void *user_data) +{ + opts->before_send_feedback_func = func; + opts->before_send_feedback_data = user_data; +} + void sentry_options_set_dsn_n( sentry_options_t *opts, const char *raw_dsn, size_t raw_dsn_len) diff --git a/src/sentry_options.h b/src/sentry_options.h index 6e7a79f3f..75d52220a 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -72,6 +72,8 @@ struct sentry_options_s { void *before_send_log_data; sentry_before_breadcrumb_function_t before_breadcrumb_func; void *before_breadcrumb_data; + sentry_before_send_feedback_function_t before_send_feedback_func; + void *before_send_feedback_data; /* Experimentally exposed */ double traces_sample_rate; From dbdbabd0ec946dfaaea3b343e853c6656e408917 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 18:01:34 +0200 Subject: [PATCH 02/11] Refactor and fix hint attachments issue in prepare_feedback --- src/sentry_core.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/sentry_core.c b/src/sentry_core.c index 1ad108834..0303bcf92 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -838,19 +838,13 @@ prepare_user_feedback(const sentry_options_t *options, sentry_value_set_by_key( event, "level", sentry__value_new_level(SENTRY_LEVEL_INFO)); - sentry_attachment_t *all_attachments = NULL; - if (hint) { - sentry__attachments_extend(&all_attachments, hint->attachments); - } - if (local_scope) { + SENTRY_DEBUG("merging local scope into feedback event"); sentry__scope_apply_to_event( local_scope, options, event, SENTRY_SCOPE_NONE); - // must be taken before the scope is freed below - sentry__attachments_extend(&all_attachments, local_scope->attachments); - sentry__scope_free_one_shot(local_scope); } SENTRY_WITH_SCOPE (scope) { + SENTRY_DEBUG("merging global scope into feedback event"); sentry__scope_apply_to_event(scope, options, event, SENTRY_SCOPE_NONE); } @@ -863,7 +857,7 @@ prepare_user_feedback(const sentry_options_t *options, "feedback was discarded by the `before_send_feedback` hook"); sentry__client_report_discard(SENTRY_DISCARD_REASON_BEFORE_SEND, SENTRY_DATA_CATEGORY_FEEDBACK, 1); - sentry__attachments_free(all_attachments); + sentry__scope_free_one_shot(local_scope); return NULL; } } @@ -875,24 +869,31 @@ prepare_user_feedback(const sentry_options_t *options, goto fail; } + sentry_attachment_t *combined_attachments = NULL; + if (hint) { + sentry__attachments_extend(&combined_attachments, hint->attachments); + } + if (local_scope) { + sentry__attachments_extend( + &combined_attachments, local_scope->attachments); + } + SENTRY_WITH_SCOPE (scope) { - if (all_attachments) { - // all attachments merged from the hint and the scopes - sentry__attachments_extend(&all_attachments, scope->attachments); - sentry__envelope_add_attachments( - envelope, all_attachments, options); - } else { - // only the global scope has attachments - sentry__envelope_add_attachments( - envelope, scope->attachments, options); + const sentry_attachment_t *attachments = scope->attachments; + if (combined_attachments) { + sentry__attachments_extend( + &combined_attachments, scope->attachments); + attachments = combined_attachments; } + sentry__envelope_add_attachments(envelope, attachments, options); if (options->run) { - sentry__cache_attachment_refs(envelope, - all_attachments ? all_attachments : scope->attachments, options, + sentry__cache_attachment_refs(envelope, attachments, options, options->run->cache_path, options->run->run_path); } } - sentry__attachments_free(all_attachments); + + sentry__attachments_free(combined_attachments); + sentry__scope_free_one_shot(local_scope); return envelope; @@ -900,7 +901,7 @@ prepare_user_feedback(const sentry_options_t *options, SENTRY_WARN("dropping user feedback"); sentry_envelope_free(envelope); sentry_value_decref(event); - sentry__attachments_free(all_attachments); + sentry__scope_free_one_shot(local_scope); return NULL; } From 21267b069ccbc6bdf2b067e407e363210a55e97c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 18:49:10 +0200 Subject: [PATCH 03/11] Always provide a valid hint to the hook --- src/sentry_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sentry_core.c b/src/sentry_core.c index 0303bcf92..c76f3992e 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1847,6 +1847,10 @@ capture_feedback(sentry_value_t user_feedback, sentry_hint_t *hint, bool was_sent = false; SENTRY_WITH_OPTIONS (options) { was_captured = true; + // Give the hook something to attach to when the caller passed no hint. + if (!hint && options->before_send_feedback_func) { + hint = sentry_hint_new(); + } sentry_envelope_t *envelope = prepare_user_feedback( options, user_feedback, hint, local_scope, &event_id); if (envelope) { From 44ca997924c705211a66fd38094c43b846e8e705 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 20:04:42 +0200 Subject: [PATCH 04/11] Pass concrete hint type --- include/sentry.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sentry.h b/include/sentry.h index 9c0180c72..c222b54d6 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -3777,7 +3777,7 @@ SENTRY_API sentry_uuid_t sentry_scope_capture_feedback( * Feedback events do not go through the `before_send` callback. */ typedef sentry_value_t (*sentry_before_send_feedback_function_t)( - sentry_value_t feedback, void *hint, void *user_data); + sentry_value_t feedback, sentry_hint_t *hint, void *user_data); /** * Sets the `before_send_feedback` callback. From 36b0b752f6cd227274a7c481acfbbfb1aaac5684 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 20:52:15 +0200 Subject: [PATCH 05/11] Add tests --- tests/unit/test_feedback.c | 205 ++++++++++++++++++++++++++++++++++++- tests/unit/tests.inc | 5 + 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_feedback.c b/tests/unit/test_feedback.c index ca8212850..5fb312a69 100644 --- a/tests/unit/test_feedback.c +++ b/tests/unit/test_feedback.c @@ -21,7 +21,8 @@ send_envelope_test_feedback(sentry_envelope_t *envelope, void *_data) } static void -setup_feedback_test(sentry_feedback_testdata_t *testdata) +setup_feedback_test_with_before_send(sentry_feedback_testdata_t *testdata, + sentry_before_send_feedback_function_t func, void *user_data) { testdata->called = 0; sentry__stringbuilder_init(&testdata->serialized_envelope); @@ -31,6 +32,7 @@ setup_feedback_test(sentry_feedback_testdata_t *testdata) sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); sentry_options_set_release(options, "my-app@1.2.3"); sentry_options_set_environment(options, "staging"); + sentry_options_set_before_send_feedback(options, func, user_data); sentry_transport_t *transport = sentry_transport_new(send_envelope_test_feedback); sentry_transport_set_state(transport, testdata); @@ -39,6 +41,12 @@ setup_feedback_test(sentry_feedback_testdata_t *testdata) sentry_init(options); } +static void +setup_feedback_test(sentry_feedback_testdata_t *testdata) +{ + setup_feedback_test_with_before_send(testdata, 0, NULL); +} + SENTRY_TEST(feedback_without_hint) { sentry_feedback_testdata_t testdata; @@ -433,3 +441,198 @@ SENTRY_TEST(feedback_with_scope_and_hint) TEST_CHECK_INT_EQUAL(testdata.called, 1); } + +static sentry_value_t +before_send_feedback_inspect( + sentry_value_t feedback, sentry_hint_t *hint, void *user_data) +{ + (void)hint; + uint64_t *called = user_data; + *called += 1; + + // the hook runs after scopes have been applied + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(feedback, "release")), + "my-app@1.2.3"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key( + sentry_value_get_by_key(feedback, "tags"), "global_tag")), + "from_global"); + + return feedback; +} + +SENTRY_TEST(feedback_before_send_receives_scope_data) +{ + sentry_feedback_testdata_t testdata; + uint64_t before_send_called = 0; + setup_feedback_test_with_before_send( + &testdata, before_send_feedback_inspect, &before_send_called); + + sentry_set_tag("global_tag", "from_global"); + + sentry_uuid_t event_id + = sentry_uuid_from_string("4c035723-8638-4c3a-923f-2ab9d08b4018"); + sentry_value_t feedback = sentry_value_new_feedback( + "test message", "test@example.com", "Test User", &event_id); + + sentry_capture_feedback(feedback); + + TEST_CHECK_INT_EQUAL(before_send_called, 1); + + char *serialized + = sentry_stringbuilder_take_string(&testdata.serialized_envelope); + TEST_CHECK(strstr(serialized, "{\"type\":\"feedback\"") != NULL); + sentry_free(serialized); + + sentry_close(); + + TEST_CHECK_INT_EQUAL(testdata.called, 1); +} + +static sentry_value_t +before_send_feedback_modify( + sentry_value_t feedback, sentry_hint_t *hint, void *user_data) +{ + (void)hint; + (void)user_data; + sentry_value_set_by_key( + feedback, "logger", sentry_value_new_string("modified-by-hook")); + return feedback; +} + +SENTRY_TEST(feedback_before_send_can_modify) +{ + sentry_feedback_testdata_t testdata; + setup_feedback_test_with_before_send( + &testdata, before_send_feedback_modify, NULL); + + sentry_uuid_t event_id + = sentry_uuid_from_string("4c035723-8638-4c3a-923f-2ab9d08b4018"); + sentry_value_t feedback = sentry_value_new_feedback( + "test message", "test@example.com", "Test User", &event_id); + + sentry_capture_feedback(feedback); + + char *serialized + = sentry_stringbuilder_take_string(&testdata.serialized_envelope); + const char *item = strstr(serialized, "{\"type\":\"feedback\""); + TEST_ASSERT(item != NULL); + TEST_CHECK(strstr(item, "\"logger\":\"modified-by-hook\"") != NULL); + sentry_free(serialized); + + sentry_close(); + + TEST_CHECK_INT_EQUAL(testdata.called, 1); +} + +static sentry_value_t +before_send_feedback_discard( + sentry_value_t feedback, sentry_hint_t *hint, void *user_data) +{ + (void)hint; + uint64_t *called = user_data; + *called += 1; + sentry_value_decref(feedback); + return sentry_value_new_null(); +} + +SENTRY_TEST(feedback_before_send_discards_with_scope_and_hint) +{ + sentry_feedback_testdata_t testdata; + uint64_t before_send_called = 0; + setup_feedback_test_with_before_send( + &testdata, before_send_feedback_discard, &before_send_called); + + sentry_uuid_t event_id + = sentry_uuid_from_string("4c035723-8638-4c3a-923f-2ab9d08b4018"); + sentry_value_t feedback = sentry_value_new_feedback( + "test message", "test@example.com", "Test User", &event_id); + + // The scope and the hint are consumed by the discarding capture; + // ASan fails the test if they leak. + sentry_scope_t *local_scope = sentry_local_scope_new(); + sentry_scope_attach_bytes(local_scope, "dropped", 7, "dropped.txt"); + + sentry_hint_t *hint = sentry_hint_new(); + sentry_hint_attach_bytes(hint, "dropped", 7, "hint.txt"); + + sentry_uuid_t feedback_id + = sentry_scope_capture_feedback(local_scope, feedback, hint); + + TEST_CHECK_INT_EQUAL(before_send_called, 1); + TEST_CHECK(sentry_uuid_is_nil(&feedback_id)); + TEST_CHECK_INT_EQUAL(testdata.called, 0); + + char *serialized + = sentry_stringbuilder_take_string(&testdata.serialized_envelope); + TEST_CHECK(strstr(serialized, "\"type\":\"feedback\"") == NULL); + sentry_free(serialized); + + sentry_close(); +} + +static sentry_value_t +before_send_feedback_attach( + sentry_value_t feedback, sentry_hint_t *hint, void *user_data) +{ + (void)user_data; + sentry_hint_attach_bytes(hint, "from hook", 9, "hook.txt"); + return feedback; +} + +SENTRY_TEST(feedback_before_send_can_attach_to_hint) +{ + sentry_feedback_testdata_t testdata; + setup_feedback_test_with_before_send( + &testdata, before_send_feedback_attach, NULL); + + sentry_uuid_t event_id + = sentry_uuid_from_string("4c035723-8638-4c3a-923f-2ab9d08b4018"); + sentry_value_t feedback = sentry_value_new_feedback( + "test message", "test@example.com", "Test User", &event_id); + + sentry_hint_t *hint = sentry_hint_new(); + sentry_hint_attach_bytes(hint, "before hook", 11, "before.txt"); + + // The callback adds an additional attachment to the hint. + sentry_capture_feedback_with_hint(feedback, hint); + + char *serialized + = sentry_stringbuilder_take_string(&testdata.serialized_envelope); + TEST_CHECK(strstr(serialized, "\"filename\":\"before.txt\"") != NULL); + TEST_CHECK(strstr(serialized, "before hook") != NULL); + TEST_CHECK(strstr(serialized, "\"filename\":\"hook.txt\"") != NULL); + TEST_CHECK(strstr(serialized, "from hook") != NULL); + sentry_free(serialized); + + sentry_close(); + + TEST_CHECK_INT_EQUAL(testdata.called, 1); +} + +SENTRY_TEST(feedback_before_send_can_attach_without_hint) +{ + sentry_feedback_testdata_t testdata; + setup_feedback_test_with_before_send( + &testdata, before_send_feedback_attach, NULL); + + sentry_uuid_t event_id + = sentry_uuid_from_string("4c035723-8638-4c3a-923f-2ab9d08b4018"); + sentry_value_t feedback = sentry_value_new_feedback( + "test message", "test@example.com", "Test User", &event_id); + + // No hint is passed; capture should create one so the callback can attach + // additional files to it. + sentry_capture_feedback(feedback); + + char *serialized + = sentry_stringbuilder_take_string(&testdata.serialized_envelope); + TEST_CHECK(strstr(serialized, "\"filename\":\"hook.txt\"") != NULL); + TEST_CHECK(strstr(serialized, "from hook") != NULL); + sentry_free(serialized); + + sentry_close(); + + TEST_CHECK_INT_EQUAL(testdata.called, 1); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 6789e1d92..7c0d6e2fe 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -157,6 +157,11 @@ XX(envelope_materialize) XX(envelope_remove_item) XX(event_with_id) XX(exception_without_type_or_value_still_valid) +XX(feedback_before_send_can_attach_to_hint) +XX(feedback_before_send_can_attach_without_hint) +XX(feedback_before_send_can_modify) +XX(feedback_before_send_discards_with_scope_and_hint) +XX(feedback_before_send_receives_scope_data) XX(feedback_carries_scope_data) XX(feedback_with_bytes_attachment) XX(feedback_with_file_attachment) From 6e913030b7b5586a66c493f3e2801b686938135c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 21:17:06 +0200 Subject: [PATCH 06/11] combined_attachments => all_attachments --- src/sentry_core.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sentry_core.c b/src/sentry_core.c index c76f3992e..8acf4c900 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -869,21 +869,19 @@ prepare_user_feedback(const sentry_options_t *options, goto fail; } - sentry_attachment_t *combined_attachments = NULL; + sentry_attachment_t *all_attachments = NULL; if (hint) { - sentry__attachments_extend(&combined_attachments, hint->attachments); + sentry__attachments_extend(&all_attachments, hint->attachments); } if (local_scope) { - sentry__attachments_extend( - &combined_attachments, local_scope->attachments); + sentry__attachments_extend(&all_attachments, local_scope->attachments); } SENTRY_WITH_SCOPE (scope) { const sentry_attachment_t *attachments = scope->attachments; - if (combined_attachments) { - sentry__attachments_extend( - &combined_attachments, scope->attachments); - attachments = combined_attachments; + if (all_attachments) { + sentry__attachments_extend(&all_attachments, scope->attachments); + attachments = all_attachments; } sentry__envelope_add_attachments(envelope, attachments, options); if (options->run) { @@ -892,7 +890,7 @@ prepare_user_feedback(const sentry_options_t *options, } } - sentry__attachments_free(combined_attachments); + sentry__attachments_free(all_attachments); sentry__scope_free_one_shot(local_scope); return envelope; From 6b96770d02ba2c9bd1babd6371ad301dfb60ff13 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 21:29:04 +0200 Subject: [PATCH 07/11] Update sentry.h --- include/sentry.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sentry.h b/include/sentry.h index c222b54d6..04c2daff0 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -3774,6 +3774,8 @@ SENTRY_API sentry_uuid_t sentry_scope_capture_feedback( * callback needs to call `sentry_value_decref` on the provided event and * return a `sentry_value_new_null()` instead. * + * The hint can be used to add attachments to the in-flight feedback event. + * * Feedback events do not go through the `before_send` callback. */ typedef sentry_value_t (*sentry_before_send_feedback_function_t)( From 6af6aeabc79bad3bafd2bf738cc12414e6bfad7b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 21:42:10 +0200 Subject: [PATCH 08/11] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92aa15c34..92ea1c5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +**Features**: + +- Add `sentry_options_set_before_send_feedback` to filter or enrich user feedback. Feedback does not go through `before_send`. ([#1923](https://github.com/getsentry/sentry-native/pull/1923)) + ## 0.16.0 **Features**: From 917290cf33bd8717eccafee748b83f5772254ff2 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 21:55:28 +0200 Subject: [PATCH 09/11] Add comment to clarify --- src/sentry_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sentry_core.c b/src/sentry_core.c index 8acf4c900..c1cb0b2ed 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1847,6 +1847,8 @@ capture_feedback(sentry_value_t user_feedback, sentry_hint_t *hint, was_captured = true; // Give the hook something to attach to when the caller passed no hint. if (!hint && options->before_send_feedback_func) { + // A failed allocation is tolerated: operating on a NULL hint + // no-ops. hint = sentry_hint_new(); } sentry_envelope_t *envelope = prepare_user_feedback( From 8905fe2e97fb682ddbffc5543f7ad74701b1cd24 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 22:01:08 +0200 Subject: [PATCH 10/11] Clarify hint doc --- include/sentry.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sentry.h b/include/sentry.h index 04c2daff0..351725136 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -3774,7 +3774,7 @@ SENTRY_API sentry_uuid_t sentry_scope_capture_feedback( * callback needs to call `sentry_value_decref` on the provided event and * return a `sentry_value_new_null()` instead. * - * The hint can be used to add attachments to the in-flight feedback event. + * The hint is always provided and can be used to add attachments to the event. * * Feedback events do not go through the `before_send` callback. */ From 2be903c696e131229991abaaf9c137de59aaaf1f Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 27 Jul 2026 22:14:02 +0200 Subject: [PATCH 11/11] Add client reports test --- examples/example.c | 15 ++++++++++++ tests/test_integration_client_reports.py | 30 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/examples/example.c b/examples/example.c index 02db336c6..2efcfa23f 100644 --- a/examples/example.c +++ b/examples/example.c @@ -314,6 +314,16 @@ discarding_before_send_metric_callback(sentry_value_t metric, void *user_data) return sentry_value_new_null(); } +static sentry_value_t +discarding_before_send_feedback_callback( + sentry_value_t feedback, sentry_hint_t *hint, void *user_data) +{ + (void)hint; + (void)user_data; + sentry_value_decref(feedback); + return sentry_value_new_null(); +} + static sentry_value_t before_breadcrumb_callback(sentry_value_t breadcrumb, void *user_data) { @@ -899,6 +909,11 @@ main(int argc, char **argv) options, discarding_before_send_metric_callback, NULL); } + if (has_arg(argc, argv, "discarding-before-send-feedback")) { + sentry_options_set_before_send_feedback( + options, discarding_before_send_feedback_callback, NULL); + } + if (has_arg(argc, argv, "before-breadcrumb")) { sentry_options_set_before_breadcrumb( options, before_breadcrumb_callback, NULL); diff --git a/tests/test_integration_client_reports.py b/tests/test_integration_client_reports.py index f0d17232e..5c741a877 100644 --- a/tests/test_integration_client_reports.py +++ b/tests/test_integration_client_reports.py @@ -230,6 +230,36 @@ def test_client_report_before_send_metric(cmake, httpserver): ) +def test_client_report_before_send_feedback(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) + + httpserver.expect_request("/api/123456/envelope/").respond_with_data("OK") + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + # Feedback is discarded by before_send_feedback. The session at + # shutdown carries the client report. + run( + tmp_path, + "sentry_example", + [ + "log", + "start-session", + "capture-user-feedback", + "discarding-before-send-feedback", + ], + env=env, + ) + + assert len(httpserver.log) == 1 + envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) + + assert_session(envelope) + assert_client_report( + envelope, + [{"reason": "before_send", "category": "feedback", "quantity": 1}], + ) + + def test_client_report_before_send_transaction(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"})