From 8f8a9f6f044ac386352241d06b0fbdaff86c0dbc Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Mon, 13 Feb 2023 19:50:57 +0000 Subject: [PATCH] sentry: Add frontend event monitoring. Zulip already has integrations for server-side Sentry integration; however, it has historically used the Zulip-specific `blueslip` library for monitoring browser-side errors. However, the latter sends errors to email, as well optionally to an internal `#errors` stream. While this is sufficient for low volumes of users, and useful in that it does not rely on outside services, at higher volumes it is very difficult to do any analysis or filtering of the errors. Client-side errors are exceptionally noisy, with many false positives due to browser extensions or similar, so determining real real errors from a stream of un-grouped emails or messages in a stream is quite difficult. Add a client-side Javascript sentry integration. To provide useful backtraces, this requires extending the pre-deploy hooks to upload the source-maps to Sentry. Additional keys are added to the non-public API of `page_params` to control the DSN, realm identifier, and sample rates. --- package.json | 3 + .../files/hooks/post-deploy.d/sentry.hook | 6 +- .../files/hooks/pre-deploy.d/sentry.hook | 38 +++++++-- tools/test-js-with-node | 1 + version.py | 2 +- web/src/bundles/common.ts | 1 + web/src/page_params.ts | 9 ++ web/src/sentry.ts | 59 +++++++++++++ yarn.lock | 83 ++++++++++++++++++- zerver/lib/home.py | 10 +++ zerver/tests/test_home.py | 47 ++++++++++- zproject/default_settings.py | 3 + zproject/prod_settings_template.py | 6 +- 13 files changed, 257 insertions(+), 11 deletions(-) create mode 100644 web/src/sentry.ts diff --git a/package.json b/package.json index 2e7af2c140..b8789790eb 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,9 @@ "@formatjs/intl": "^2.0.0", "@giphy/js-components": "^5.0.5", "@giphy/js-fetch-api": "^4.0.1", + "@sentry/browser": "^7.41.0", + "@sentry/integrations": "7.41.0", + "@sentry/tracing": "^7.41.0", "@uppy/core": "^3.0.2", "@uppy/progress-bar": "^3.0.1", "@uppy/xhr-upload": "^3.0.2", diff --git a/puppet/zulip/files/hooks/post-deploy.d/sentry.hook b/puppet/zulip/files/hooks/post-deploy.d/sentry.hook index dd3a756992..69115fd985 100755 --- a/puppet/zulip/files/hooks/post-deploy.d/sentry.hook +++ b/puppet/zulip/files/hooks/post-deploy.d/sentry.hook @@ -20,8 +20,10 @@ if ! SENTRY_ORG=$(crudini --get /etc/zulip/zulip.conf sentry organization); then exit 0 fi -if ! SENTRY_PROJECT=$(crudini --get /etc/zulip/zulip.conf sentry project); then - echo "sentry: No project set! Set sentry.project in /etc/zulip/zulip.conf" +SENTRY_PROJECT=$(crudini --get /etc/zulip/zulip.conf sentry project) +SENTRY_FRONTEND_PROJECT=$(crudini --get /etc/zulip/zulip.conf sentry frontend_project) +if [ -z "$SENTRY_PROJECT" ] && [ -z "$SENTRY_FRONTEND_PROJECT" ]; then + echo "sentry: No project set! Set sentry.project and/or sentry.frontend_project in /etc/zulip/zulip.conf" exit 0 fi diff --git a/puppet/zulip/files/hooks/pre-deploy.d/sentry.hook b/puppet/zulip/files/hooks/pre-deploy.d/sentry.hook index e8af25a869..17d774b463 100755 --- a/puppet/zulip/files/hooks/pre-deploy.d/sentry.hook +++ b/puppet/zulip/files/hooks/pre-deploy.d/sentry.hook @@ -5,8 +5,8 @@ set -e set -u -if ! grep -q 'SENTRY_DSN' /etc/zulip/settings.py; then - echo "sentry: No DSN configured! Set SENTRY_DSN in /etc/zulip/settings.py" +if ! grep -Eq 'SENTRY_DSN|SENTRY_FRONTEND_DSN' /etc/zulip/settings.py; then + echo "sentry: No DSN configured! Set SENTRY_DSN or SENTRY_FRONTEND_DSN in /etc/zulip/settings.py" exit 0 fi @@ -20,8 +20,19 @@ if ! SENTRY_ORG=$(crudini --get /etc/zulip/zulip.conf sentry organization); then exit 0 fi -if ! SENTRY_PROJECT=$(crudini --get /etc/zulip/zulip.conf sentry project); then - echo "sentry: No project set! Set sentry.project in /etc/zulip/zulip.conf" +SENTRY_PROJECT=$(crudini --get /etc/zulip/zulip.conf sentry project) +SENTRY_FRONTEND_PROJECT=$(crudini --get /etc/zulip/zulip.conf sentry frontend_project) +if [ -z "$SENTRY_PROJECT" ] && [ -z "$SENTRY_FRONTEND_PROJECT" ]; then + echo "sentry: No project set! Set sentry.project and/or sentry.frontend_project in /etc/zulip/zulip.conf" + exit 0 +fi + +if [ -n "$SENTRY_PROJECT" ] && ! grep -q 'SENTRY_DSN' /etc/zulip/settings.py; then + echo "sentry: sentry.project is set but SENTRY_DSN is not set in /etc/zulip/settings.py" + exit 0 +fi +if [ -n "$SENTRY_FRONTEND_PROJECT" ] && ! grep -q 'SENTRY_FRONTEND_DSN' /etc/zulip/settings.py; then + echo "sentry: sentry.frontend_project is set but SENTRY_FRONTEND_DSN is not set in /etc/zulip/settings.py" exit 0 fi @@ -51,9 +62,26 @@ echo "$SENTRY_RELEASE" >./sentry-release echo "sentry: Creating release $SENTRY_RELEASE" export SENTRY_AUTH_TOKEN -sentry-cli releases --org="$SENTRY_ORG" --project="$SENTRY_PROJECT" new "$SENTRY_RELEASE" + +# sentry-cli only supports passing one project when making a new +# release, and we want to possibly create more than once at once. Use +# curl to make the API request. +json=$(jq -nc '{version: $ARGS.named.version, + projects: $ARGS.positional | map(select( . != ""))}' \ + --arg version "$SENTRY_RELEASE" \ + --args "$SENTRY_PROJECT" "$SENTRY_FRONTEND_PROJECT") +curl "https://sentry.io/api/0/organizations/$SENTRY_ORG/releases/" \ + -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \ + -H 'Content-Type: application/json' \ + -d "$json" \ + --silent -o /dev/null if [ -n "$MERGE_BASE" ]; then echo "sentry: Setting commit range based on merge-base to upstream of $MERGE_BASE" sudo -u zulip --preserve-env=SENTRY_AUTH_TOKEN sentry-cli releases --org="$SENTRY_ORG" set-commits "$SENTRY_RELEASE" --commit="zulip/zulip@$MERGE_BASE" fi + +if [ -n "$SENTRY_FRONTEND_PROJECT" ]; then + echo "sentry: Uploading sourcemaps" + sentry-cli releases --org="$SENTRY_ORG" --project="$SENTRY_FRONTEND_PROJECT" files "$SENTRY_RELEASE" upload-sourcemaps static/webpack-bundles/ +fi diff --git a/tools/test-js-with-node b/tools/test-js-with-node index bb5ccec977..c460337474 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -147,6 +147,7 @@ EXEMPT_FILES = make_set( "web/src/scroll_bar.js", "web/src/search_pill_widget.js", "web/src/sent_messages.js", + "web/src/sentry.ts", "web/src/server_events.js", "web/src/settings.js", "web/src/settings_account.js", diff --git a/version.py b/version.py index 07449c4d9e..6420979846 100644 --- a/version.py +++ b/version.py @@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 167 # historical commits sharing the same major version, in which case a # minor version bump suffices. -PROVISION_VERSION = (224, 0) +PROVISION_VERSION = (224, 1) diff --git a/web/src/bundles/common.ts b/web/src/bundles/common.ts index 88947b7276..368277cde0 100644 --- a/web/src/bundles/common.ts +++ b/web/src/bundles/common.ts @@ -1,3 +1,4 @@ +import "../sentry"; import "../webpack_public_path"; import "../../debug-require"; import "../alert_popup"; diff --git a/web/src/page_params.ts b/web/src/page_params.ts index 44f11ab9ae..d53a1f7ede 100644 --- a/web/src/page_params.ts +++ b/web/src/page_params.ts @@ -28,14 +28,23 @@ export const page_params: { realm_move_messages_between_streams_policy: number; realm_name_changes_disabled: boolean; realm_push_notifications_enabled: boolean; + realm_sentry_key: string | undefined; + realm_uri: string; realm_user_group_edit_policy: number; realm_waiting_period_threshold: number; request_language: string; server_avatar_changes_disabled: boolean; server_name_changes_disabled: boolean; + server_sentry_dsn: string | undefined; + server_sentry_environment: string | undefined; + server_sentry_sample_rate: number | undefined; + server_sentry_trace_rate: number | undefined; server_web_public_streams_enabled: boolean; translation_data: Record; + user_id: number | undefined; + webpack_public_path: string; zulip_plan_is_not_limited: boolean; + zulip_version: string; muted_users: {id: number; timestamp: number}[]; } = $("#page-params").remove().data("params"); const t2 = performance.now(); diff --git a/web/src/sentry.ts b/web/src/sentry.ts new file mode 100644 index 0000000000..028de2efeb --- /dev/null +++ b/web/src/sentry.ts @@ -0,0 +1,59 @@ +import * as Sentry from "@sentry/browser"; +import {HttpClient as HttpClientIntegration} from "@sentry/integrations"; +import {BrowserTracing} from "@sentry/tracing"; +import _ from "lodash"; + +import {page_params} from "./page_params"; + +type UserInfo = { + id?: string; + realm: string; + role: string; +}; + +if (page_params.server_sentry_dsn) { + const url_regex = new RegExp("^" + _.escapeRegExp(page_params.realm_uri) + "/"); + const user_info: UserInfo = { + realm: page_params.realm_sentry_key!, + role: page_params.is_owner + ? "Organization owner" + : page_params.is_admin + ? "Organization administrator" + : page_params.is_moderator + ? "Moderator" + : page_params.is_guest + ? "Guest" + : page_params.is_spectator + ? "Spectator" + : "Member", + }; + if (page_params.user_id) { + user_info.id = page_params.user_id.toString(); + } + + Sentry.init({ + dsn: page_params.server_sentry_dsn, + environment: page_params.server_sentry_environment || "development", + + release: "zulip-server@" + ZULIP_VERSION, + integrations: [ + new BrowserTracing({ + tracePropagationTargets: [url_regex], + }), + new HttpClientIntegration({ + failedRequestStatusCodes: [500, 502, 503, 504], + failedRequestTargets: [url_regex], + }), + ], + allowUrls: [url_regex, page_params.webpack_public_path], + sampleRate: page_params.server_sentry_sample_rate || 0, + tracesSampleRate: page_params.server_sentry_trace_rate || 0, + initialScope: { + tags: { + realm: page_params.realm_sentry_key || "(root)", + server_version: page_params.zulip_version, + }, + user: user_info, + }, + }); +} diff --git a/yarn.lock b/yarn.lock index db29732ce7..96b08686f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1608,6 +1608,68 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== +"@sentry/browser@^7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.41.0.tgz#f4e789417e3037cbc9cd15f3a000064b1873b964" + integrity sha512-ZEtgTXPOHZ9/Qn42rr9ZAPTKCV6fAjyDC4FFWMGP4HoUqJqr2woRddP9O5n1jvjsoIPAFOmGzbCuZwFrPVVnpQ== + dependencies: + "@sentry/core" "7.41.0" + "@sentry/replay" "7.41.0" + "@sentry/types" "7.41.0" + "@sentry/utils" "7.41.0" + tslib "^1.9.3" + +"@sentry/core@7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.41.0.tgz#a4a8291ef4e65f40c28fc38318e7dae721d5d609" + integrity sha512-yT3wl3wMfPymstIZRWNjuov4xhieIEPD0z9MIW9VmoemqkD5BEZsgPuvGaVIyQVMyx61GsN4H4xd0JCyNqNvLg== + dependencies: + "@sentry/types" "7.41.0" + "@sentry/utils" "7.41.0" + tslib "^1.9.3" + +"@sentry/integrations@7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.41.0.tgz#1f4ac60c3905a87023e0c3c493befced687b49ca" + integrity sha512-wyVsDxTn/lslPSt02JS4Kw5iRBau+GYst1r7z55VKBl7YJm0XCaLnGsqv68qweaK9SI7PX8rj/+GmRl8G86wOg== + dependencies: + "@sentry/types" "7.41.0" + "@sentry/utils" "7.41.0" + localforage "^1.8.1" + tslib "^1.9.3" + +"@sentry/replay@7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.41.0.tgz#73168d659b0e78ca58574831a6672a77ce9727ee" + integrity sha512-/vxuO17AysCoBbCl9wCwjsCFBD4lEbYgfC1GJm8ayWwPU1uhvZcEx6reUwi0rEFpWYGHSHh3+gi+QsOcY/EmnQ== + dependencies: + "@sentry/core" "7.41.0" + "@sentry/types" "7.41.0" + "@sentry/utils" "7.41.0" + +"@sentry/tracing@^7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.41.0.tgz#28f57667a13b95cb8bce5af0809e7727b645e6b7" + integrity sha512-zh1ceuwQ8NzE5n8r4y78QrYD/alJl4qlkiEX9lAL6PnLMWJkVWM02BBu+x75yPFWSSDfDA/kZ9WqKkHNdjGpDw== + dependencies: + "@sentry/core" "7.41.0" + "@sentry/types" "7.41.0" + "@sentry/utils" "7.41.0" + tslib "^1.9.3" + +"@sentry/types@7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.41.0.tgz#3d53432a3d7693a31b606d3083ab9203c56f5aec" + integrity sha512-4z9VdObynwd64i0VHCqkeIAHmsFzapL21qN41Brzb7jY/eGxjn/0rxInDGH+vkoE9qacGqiYfWj4vRNPLsC/bw== + +"@sentry/utils@7.41.0": + version "7.41.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.41.0.tgz#54224dba668dd8c8feb0ff1b4f39938b8fdefd3b" + integrity sha512-SL+MGitvkakbkrOTb48rDuJp9GYx/veB6EOzYygh49+zwz4DGM7dD4/rvf/mVlgmXUzPgdGDgkVmxgX3nT7I7g== + dependencies: + "@sentry/types" "7.41.0" + tslib "^1.9.3" + "@sinclair/typebox@^0.25.16": version "0.25.21" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" @@ -6090,6 +6152,11 @@ image-palette@^2.1.0: pxls "^2.0.0" quantize "^1.0.2" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -6909,6 +6976,13 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== + dependencies: + immediate "~3.0.5" + lilconfig@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" @@ -6942,6 +7016,13 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +localforage@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== + dependencies: + lie "3.1.1" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -10388,7 +10469,7 @@ tslib@2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@^1.8.1: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== diff --git a/zerver/lib/home.py b/zerver/lib/home.py index 59e92cdb18..9baf46fe5a 100644 --- a/zerver/lib/home.py +++ b/zerver/lib/home.py @@ -19,6 +19,7 @@ from zerver.lib.realm_description import get_realm_rendered_description from zerver.lib.request import RequestNotes from zerver.models import Message, Realm, Stream, UserProfile from zerver.views.message_flags import get_latest_update_message_flag_activity +from zproject.config import get_config @dataclass @@ -208,8 +209,17 @@ def build_page_params_for_home_page_load( # There is no event queue for spectators since # events support for spectators is not implemented yet. no_event_queue=user_profile is None, + server_sentry_dsn=settings.SENTRY_FRONTEND_DSN, ) + if settings.SENTRY_FRONTEND_DSN is not None: + page_params["realm_sentry_key"] = realm.string_id + page_params["server_sentry_environment"] = get_config( + "machine", "deploy_type", "development" + ) + page_params["server_sentry_sample_rate"] = settings.SENTRY_FRONTEND_SAMPLE_RATE + page_params["server_sentry_trace_rate"] = settings.SENTRY_FRONTEND_TRACE_RATE + for field_name in register_ret: page_params[field_name] = register_ret[field_name] diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index e67464bc7f..aaa8f3dbfb 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -2,7 +2,7 @@ import calendar import datetime import urllib from datetime import timedelta -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Dict from unittest.mock import patch import orjson @@ -198,6 +198,7 @@ class HomeTest(ZulipTestCase): "server_needs_upgrade", "server_presence_offline_threshold_seconds", "server_presence_ping_interval_seconds", + "server_sentry_dsn", "server_timestamp", "server_web_public_streams_enabled", "settings_send_digest_emails", @@ -355,6 +356,7 @@ class HomeTest(ZulipTestCase): "realm_rendered_description", "request_language", "search_pills_enabled", + "server_sentry_dsn", "show_billing", "show_plans", "show_webathena", @@ -367,6 +369,49 @@ class HomeTest(ZulipTestCase): ] self.assertEqual(actual_keys, expected_keys) + def test_sentry_keys(self) -> None: + def home_params() -> Dict[str, Any]: + result = self._get_home_page() + self.assertEqual(result.status_code, 200) + return self._get_page_params(result) + + self.login("hamlet") + page_params = home_params() + self.assertEqual(page_params["server_sentry_dsn"], None) + self.assertEqual( + [], [key for key in page_params if key != "server_sentry_dsn" and "sentry" in key] + ) + + with self.settings(SENTRY_FRONTEND_DSN="https://aaa@bbb.ingest.sentry.io/1234"): + page_params = home_params() + self.assertEqual( + page_params["server_sentry_dsn"], "https://aaa@bbb.ingest.sentry.io/1234" + ) + self.assertEqual(page_params["realm_sentry_key"], "zulip") + self.assertEqual(page_params["server_sentry_environment"], "development") + self.assertEqual(page_params["server_sentry_sample_rate"], 1.0) + self.assertEqual(page_params["server_sentry_trace_rate"], 0.1) + + # Make sure these still exist for logged-out users as well + realm = get_realm("zulip") + do_set_realm_property(realm, "enable_spectator_access", True, acting_user=None) + self.logout() + page_params = home_params() + self.assertEqual(page_params["server_sentry_dsn"], None) + self.assertEqual( + [], [key for key in page_params if key != "server_sentry_dsn" and "sentry" in key] + ) + + with self.settings(SENTRY_FRONTEND_DSN="https://aaa@bbb.ingest.sentry.io/1234"): + page_params = home_params() + self.assertEqual( + page_params["server_sentry_dsn"], "https://aaa@bbb.ingest.sentry.io/1234" + ) + self.assertEqual(page_params["realm_sentry_key"], "zulip") + self.assertEqual(page_params["server_sentry_environment"], "development") + self.assertEqual(page_params["server_sentry_sample_rate"], 1.0) + self.assertEqual(page_params["server_sentry_trace_rate"], 0.1) + def test_home_under_2fa_without_otp_device(self) -> None: with self.settings(TWO_FACTOR_AUTHENTICATION_ENABLED=True): self.login("iago") diff --git a/zproject/default_settings.py b/zproject/default_settings.py index 1effcbdaad..c0a11e1890 100644 --- a/zproject/default_settings.py +++ b/zproject/default_settings.py @@ -132,6 +132,9 @@ LOGGING_SHOW_PID = False # Sentry.io error defaults to off SENTRY_DSN: Optional[str] = None +SENTRY_FRONTEND_DSN: Optional[str] = None +SENTRY_FRONTEND_SAMPLE_RATE: float = 1.0 +SENTRY_FRONTEND_TRACE_RATE: float = 0.1 # File uploads and avatars # TODO: Rename MAX_FILE_UPLOAD_SIZE to have unit in name. diff --git a/zproject/prod_settings_template.py b/zproject/prod_settings_template.py index 68f6d504b5..2175b6a0c1 100644 --- a/zproject/prod_settings_template.py +++ b/zproject/prod_settings_template.py @@ -652,7 +652,11 @@ SOCIAL_AUTH_SAML_SUPPORT_CONTACT = { # BROWSER_ERROR_REPORTING = False ## Controls the DSN used to report errors to Sentry.io -# SENTRY_DSN = "https://bbb@bbb.ingest.sentry.io/1235" +# SENTRY_DSN = "https://aaa@bbb.ingest.sentry.io/1234" +# SENTRY_FRONTEND_DSN = "https://aaa@bbb.ingest.sentry.io/1234" +## What portion of events are sampled (https://docs.sentry.io/platforms/javascript/configuration/sampling/): +# SENTRY_FRONTEND_SAMPLE_RATE = 1.0 +# SENTRY_FRONTEND_TRACE_RATE = 0.1 ## If True, each log message in the server logs will identify the ## Python module where it came from. Useful for tracking down a