diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 2e8857a8fa..bceb40abb3 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -94,6 +94,9 @@ export default () => { n.name === 'dynamic-sampling').children} /> + + User Feedback Architecture +
  • diff --git a/src/docs/feedback-architecture.mdx b/src/docs/feedback-architecture.mdx new file mode 100644 index 0000000000..d4ed6d4ab2 --- /dev/null +++ b/src/docs/feedback-architecture.mdx @@ -0,0 +1,197 @@ +--- +title: User Feedback Architecture +--- + +**The goal of this doc is to give engineers an in-depth understanding of User Feedback's backend.** +It will: +1. describe the relevant ingestion pipelines, data models, and functions. +2. explain the difference between “feedback”, “user reports”, and “crash reports”, and why we built and need to support each. + +## Creation sources +When broken down, there are **5** ways to create feedback in our system 😵‍💫. +(But 4 of them, related to user reports, are quite similar!) A good reference is the +`FeedbackCreationSource(Enum)` in [create_feedback.py](https://github.com/getsentry/sentry/blob/2b642e149c79b251e1c2f4339fc73d656347d74e/src/sentry/feedback/usecases/create_feedback.py#L33-L33). +The 4 ways _clients_ can create feedback are: + +`NEW_FEEDBACK_ENVELOPE`: [The new format](https://develop.sentry.dev/sdk/envelopes/#full-examples) created by the Replay team when adding +the [User Feedback Widget](https://docs.sentry.io/product/user-feedback/#user-feedback-widget) +to the JavaScript SDK. It allows adding more information, for example tags, +release, url, etc. + +`USER_REPORT_ENVELOPE`: [The older format](https://develop.sentry.dev/sdk/envelopes/#user-feedback) with name/email/comments, that requires +`event_id` to link a Sentry error event. + +`USER_REPORT_DJANGO_ENDPOINT`: [The Web API](https://docs.sentry.io/api/projects/submit-user-feedback/) + +`CRASH_REPORT_EMBED_FORM`: The [crash report modal](https://docs.sentry.io/product/user-feedback/#crash-report-modal) + +## How feedback is stored +On the backend, each feedback submission in Sentry's UI is **an un-grouped issue occurrence**, +saved via the [issues platform](https://develop.sentry.dev/issue-platform/). +The entrypoint is [**`create_feedback_issue()`**](https://github.com/getsentry/sentry/blob/2b642e149c79b251e1c2f4339fc73d656347d74e/src/sentry/feedback/usecases/create_feedback.py#L184-L184), +which +1. filters feedback with empty or spam messages. Note **anonymous feedbacks are not filtered** (missing name and/or email). +2. sends to issues pipeline in a standardized format. To make sure it is never grouped, we use a random UUID for the fingerprint. + +--- + +## Feedback events + +The new and preferred way to send feedback from the SDK is in an [event envelope](https://develop.sentry.dev/sdk/envelopes/#full-examples). +The format is the same as error events, except the `type` header = `"feedback"`. While +user reports have an associated event, **new feedback _is_ an event**. This +offers 2 improvements: + +1. Users can submit generic feedback without an error occurring. We're allowing users to catch things that Sentry can’t! +2. Rather than waiting for the associated error to be ingested asynchronously, we immediately get access to context like the environment, platform, replay, user, tags... + +The user's submission is wrapped in a context object: + +```Python/Javascript +event[”contexts”][”feedback”] = { + "name": , + "contact_email": , + "message": , + "url": , + "source": +} + +// all fields are technically optional, but recommended +// the widget can be configured to require a non-empty email and/or name +``` + +- This doc refers to the payload format (`event` in the pseudo-code above) as a “**feedback event”**. +- The feedback [widget](https://docs.sentry.io/platforms/javascript/user-feedback/#user-feedback-widget), which is installed by default, sends these envelopes. +- API: for SDK v8.0.0+, we use the `sendFeedback` function. +

    + +### Ingest diagram + +![](https://mermaid.ink/svg/pako:eNptkc1qwzAQhF9F7MmGmN59KJSmx_ZQ91IsYxR5YwvbkqqfgEny7l05NW0hOg3a0erb2TNI0yGU0DthB_ax55rREdbWnyY69mTtpKQIyuiGFcUju3BAfcLJWORwYe84iYVrHlaxOo6tNNrHGV1WK92jD8URsTsIORZ4Qh18k6cXv76t8VfEiJ4JzyRO6BYWhB_TL9KhCNhubVrlfcTU425hbWf-UHD44TBSRudQS_QcbhTmP4WmNHwwDuvsbZN5c8dIuC9pmCoQwpwgvY4HUWccqiQenim3cTDRU055Azugl7NQHWV9TiFzCAPOVCxJTqofAgeur2QUMZhq0RLK4CLuINqOhtwrQTuat0vsFLG93pa37vD6DRJvo7g) +[diagram source](https://mermaid.live/edit#pako:eNptkc1qwzAQhF9F7MmGmN59KJSmx_ZQ91IsYxR5YwvbkqqfgEny7l05NW0hOg3a0erb2TNI0yGU0DthB_ax55rREdbWnyY69mTtpKQIyuiGFcUju3BAfcLJWORwYe84iYVrHlaxOo6tNNrHGV1WK92jD8URsTsIORZ4Qh18k6cXv76t8VfEiJ4JzyRO6BYWhB_TL9KhCNhubVrlfcTU425hbWf-UHD44TBSRudQS_QcbhTmP4WmNHwwDuvsbZN5c8dIuC9pmCoQwpwgvY4HUWccqiQenim3cTDRU055Azugl7NQHWV9TiFzCAPOVCxJTqofAgeur2QUMZhq0RLK4CLuINqOhtwrQTuat0vsFLG93pa37vD6DRJvo7g) + +In Relay v24.5.1, we migrated feedback to its own kafka topic + consumer, +`ingest-feedback-events`. This decouples risk and ownership from errors +(`ingest-events`). +
    + +### Attachments + +We only use attachments for the widget’s screenshot feature, which allows users +to submit **at most 1 screenshot per feedback**. Attachments are another [item type](https://develop.sentry.dev/sdk/envelopes/#attachment) +in an envelope. +- SDK v8.0.0+, Relay v24.5.1+: Sends the feedback and attachment items in the same envelope. +- SDK < v8, all Relay versions: Sends a separate envelope for each item. + +**The feedback pipeline does not process attachments**. Relay routes them to +a separate topic and storage, and the UI makes a separate request for them. + +--- + +## User reports + +The deprecated way of sending feedback is as a **user report**. This is a simple typed dict: + +```Python/Javascript +user_report = { + "event_id": , + "email": , + "name": , + "comments": , +} +``` +
    + +### Ingest diagram +![](https://mermaid.ink/svg/pako:eNp9VNtq3DAQ_ZVBEHAgy77vQ6EkDfShF7KhUKzFaOXZtYgtqbpsCUn-vaOxvWmT3frF4-HozJkzIz8J7VoUK7EPyndwfyOtTDFvx8812hQeS0qmgN6F1KBtvTM21VIsc8Sw2CG2W6UfpNiMQB1U7JoJPrhW9YS9Lkm44yT8MPh7ghMdvf6uuctWJ-NsrG_nCBYQsgVjIeAOQ8AwlYrqgE1RMVYrSaAndmZokmuO0iZdqBIek42JMeNJHlgsPsCzFJ9vAQ9kAfjgNMaIrRTPZ9jfZpnjbMlj2xcXgPaAvfMI3njsjWWA8r7-6XKAj973Rqviw2bWNZ8oau6wVzwhDhihSIc3uqqN3WNMC5WS0t1AjcTNJZOPAAa_t3BWNc4ZlG2BRwojIk7yZjHfv63vi5A3-3GG_NTJ9wvzX2VsYgTfq7RzYSjZkz4ziWs0LVAeMFS0hZMhTutMrJZmSmvIlrzi-JilSxGTC1hXX-fwcnMCSG18KiuypvuhBl4Pm7eqrtbltbym2T10jrrg02MDpafJLm4yLrNvST87e2oVvYtpHzDWFZk2xVKMerjcLOVXxmDIG95aFs2K_qUUV4LUD8q0dO2fpAWQInU4EHZFYW_2XZJC2hcCqpzc-tFqsUoh45UYhd4YRXd1EKud6iNlsTVU68v4I-H_ycsfwTWaqg) +[diagram source](https://mermaid.live/edit#pako:eNp9VNtq3DAQ_ZVBEHAgy77vQ6EkDfShF7KhUKzFaOXZtYgtqbpsCUn-vaOxvWmT3frF4-HozJkzIz8J7VoUK7EPyndwfyOtTDFvx8812hQeS0qmgN6F1KBtvTM21VIsc8Sw2CG2W6UfpNiMQB1U7JoJPrhW9YS9Lkm44yT8MPh7ghMdvf6uuctWJ-NsrG_nCBYQsgVjIeAOQ8AwlYrqgE1RMVYrSaAndmZokmuO0iZdqBIek42JMeNJHlgsPsCzFJ9vAQ9kAfjgNMaIrRTPZ9jfZpnjbMlj2xcXgPaAvfMI3njsjWWA8r7-6XKAj973Rqviw2bWNZ8oau6wVzwhDhihSIc3uqqN3WNMC5WS0t1AjcTNJZOPAAa_t3BWNc4ZlG2BRwojIk7yZjHfv63vi5A3-3GG_NTJ9wvzX2VsYgTfq7RzYSjZkz4ziWs0LVAeMFS0hZMhTutMrJZmSmvIlrzi-JilSxGTC1hXX-fwcnMCSG18KiuypvuhBl4Pm7eqrtbltbym2T10jrrg02MDpafJLm4yLrNvST87e2oVvYtpHzDWFZk2xVKMerjcLOVXxmDIG95aFs2K_qUUV4LUD8q0dO2fpAWQInU4EHZFYW_2XZJC2hcCqpzc-tFqsUoh45UYhd4YRXd1EKud6iNlsTVU68v4I-H_ycsfwTWaqg) +
    + +### Shimming to feedback + +Before it was extended to generic feedback, the [`UserReport` model](https://github.com/getsentry/sentry/blob/2b642e149c79b251e1c2f4339fc73d656347d74e/src/sentry/models/userreport.py#L9-L9) +was first used for crash reports. Therefore an associated event ID is required, and +we use it to set environment and group before saving the model to **Postgres**. +Only then can we shim the report to a **feedback event** and pass it to `create_feedback_issue()`. + +If the event hasn’t reached eventstore (Snuba) by the time of ingest, we still +save the report, but leave the environment + group empty and skip feedback creation. + +To ensure the skipped reports eventually get fixed and shimmed, we +added a post process job to the errors pipeline: [`link_event_to_user_report()`](https://github.com/getsentry/sentry/blob/2b642e149c79b251e1c2f4339fc73d656347d74e/src/sentry/tasks/post_process.py#L1387-L1387). +This is the 5th, automated way of creating feedback. + +Simplified diagram: +![](https://mermaid.ink/svg/pako:eNpdULtuwzAM_BWBUwIkzm4UXZq1QIF0MwNDlllLTfSoKAUokvx7ZasZWi06kMfj8a6g_EjQwhRl0OJ9j06Ux9rYPvn-g2gcpDqJ7fZZ3BCapkG4CXZ5kN3qMH-7l7NRJ-0z0_qIDtPSfAx8ZYqGWNCFXOLkI83zIfSffqirKu4QKEYfWQTPSYToFTGLmQVH8fRXbZfDKBPxIlXoUyTuVghvvxihOqnSi5X_B9XdsAFL0UozlgSucw0habLFZFvg2Uw6IaC7F6LMyR--nYI2xUwbqB72Rpbk7KNIoyk3vtZIl2TvP_9DeX4) +[diagram source](https://mermaid.live/edit#pako:eNpdULtuwzAM_BWBUwIkzm4UXZq1QIF0MwNDlllLTfSoKAUokvx7ZasZWi06kMfj8a6g_EjQwhRl0OJ9j06Ux9rYPvn-g2gcpDqJ7fZZ3BCapkG4CXZ5kN3qMH-7l7NRJ-0z0_qIDtPSfAx8ZYqGWNCFXOLkI83zIfSffqirKu4QKEYfWQTPSYToFTGLmQVH8fRXbZfDKBPxIlXoUyTuVghvvxihOqnSi5X_B9XdsAFL0UozlgSucw0habLFZFvg2Uw6IaC7F6LMyR--nYI2xUwbqB72Rpbk7KNIoyk3vtZIl2TvP_9DeX4) + +
    + +### Envelopes + +User reports are also sent to Relay in [envelope format](https://develop.sentry.dev/sdk/envelopes/#user-feedback). +**This item type is misleadingly called “user feedback” in some of our docs, but the +item header will read `"user_report"`.** + +The SDK function that sends these is `captureUserFeedback`. +
    + +### Django endpoint + +Before our envelope ingest service existed, older SDKs directly POST the report +to Sentry, with +`/api/0/projects/{organization_id_or_slug}/{project_id_or_slug}/user-feedback/`. + +See [https://docs.sentry.io/api/projects/submit-user-feedback/](https://docs.sentry.io/api/projects/submit-user-feedback/). +
    + +### Crash reports + +The **crash report modal** pops up when Sentry detects an error on the current +page, prompting users to describe what happened. On the backend, crash report +data is the same as user reports. + +You can install it as an [SDK integration](https://docs.sentry.io/platforms/javascript/user-feedback/#crash-report-modal). + +We implement it as a Django view: +* URL: `/api/embed/error-page/` +* Python Class: `error_page_embed.ErrorPageEmbedView` + +Crash reports are also shimmed to feedback. The pipeline is the same as the +`/user-feedback` endpoint. + +--- + +## Sentry UI + +![](https://mermaid.ink/svg/pako:eNp9Uj1vwjAQ_SvWTUECsWdgKaViqFQVmDhUmeRILBI7tc8DAv577YRAG6F68fn53b37OkNmcoIUCiubUqznqEU4zu87YMXGyoJcB8fTGMeFJbdNED5utkg2juwnNcayGyGMdg--034vt8kqXtOXSmXH0nhHPYV03hmokR-ypNmeIoSsnPPkvgKxMUrzFmHaQQi7pwwxmczEBeHtdY1wEV4FlwVRvpfZUWyWIpkebq_RPYRtc_8jEpK0k57ZEvuSBuSB3u23lV3GzMScWKrKRe2ZiJ0Si2dh_y_jHvZX3zqzn0jv8O3JqtieyzDTtsdxEE-oA3UYQ022lioPy3GOQghcUk0IaTArVZSMgPoaiNKzWZ10BilbT2PwTS6Z5kqGYdaQHmTlAkq5Csv03q1bu3XXH4El2dU) +[diagram source](https://mermaid.live/edit#pako:eNp9Uj1vwjAQ_SvWTUECsWdgKaViqFQVmDhUmeRILBI7tc8DAv577YRAG6F68fn53b37OkNmcoIUCiubUqznqEU4zu87YMXGyoJcB8fTGMeFJbdNED5utkg2juwnNcayGyGMdg--034vt8kqXtOXSmXH0nhHPYV03hmokR-ypNmeIoSsnPPkvgKxMUrzFmHaQQi7pwwxmczEBeHtdY1wEV4FlwVRvpfZUWyWIpkebq_RPYRtc_8jEpK0k57ZEvuSBuSB3u23lV3GzMScWKrKRe2ZiJ0Si2dh_y_jHvZX3zqzn0jv8O3JqtieyzDTtsdxEE-oA3UYQ022lioPy3GOQghcUk0IaTArVZSMgPoaiNKzWZ10BilbT2PwTS6Z5kqGYdaQHmTlAkq5Csv03q1bu3XXH4El2dU) + +You can view the user reports related to a specific issue at the "User Feedback" +tab of Issue Details. This excludes "new" feedback (anything sent from the widget). + +--- + +## Email alerts + +Email alerts are triggered in feedback’s post process pipeline. (Unrelated to +the post process diagram above.) We apply some feedback-specific +filters, **skipping emails if:** + +1. The feedback is [marked as spam](https://docs.sentry.io/product/user-feedback/#spam-detection-for-user-feedback) AND the `organizations.user-feedback-spam-filter-actions` feature flag is enabled. +2. The source is NOT a new feedback envelope / the widget, AND the “Crash Report Notifications” setting is disabled. + - in UI: Settings > Projects > (project slug) > User Feedback > “Enable Crash Report Notifications” + - project option in code: `sentry:feedback_user_report_notifications` + - default = true/enabled + +--- + +## Feature flags for self-hosted +To disable all UI features and/or ingestion, set these to false: +* `organizations:user-feedback-ui` +* `organizations:feedback-visible` +* `organizations:user-feedback-ingest` + +To disable auto spam filters, set these to false: +* `organizations:user-feedback-spam-filter-ingest` +* `organizations:user-feedback-spam-filter-actions`