mirror of https://github.com/zulip/zulip.git
sentry: Untangle from page_params.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
3db05666ac
commit
f7eecb0e03
|
@ -23,6 +23,10 @@
|
||||||
{% include 'zerver/meta_tags.html' %}
|
{% include 'zerver/meta_tags.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if sentry_params is defined %}
|
||||||
|
<script id="sentry-params" type="text/json">{{ sentry_params|tojson }}</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% block webpack %}
|
{% block webpack %}
|
||||||
{% for filename in webpack_entry(entrypoint) -%}
|
{% for filename in webpack_entry(entrypoint) -%}
|
||||||
{% if filename.endswith(".css") -%}
|
{% if filename.endswith(".css") -%}
|
||||||
|
|
|
@ -10,12 +10,7 @@ const default_params_schema = z.object({
|
||||||
page_type: z.literal("default"),
|
page_type: z.literal("default"),
|
||||||
development_environment: z.boolean(),
|
development_environment: z.boolean(),
|
||||||
google_analytics_id: z.optional(z.string()),
|
google_analytics_id: z.optional(z.string()),
|
||||||
realm_sentry_key: z.optional(z.string()),
|
|
||||||
request_language: z.string(),
|
request_language: z.string(),
|
||||||
server_sentry_dsn: z.nullable(z.string()),
|
|
||||||
server_sentry_environment: z.optional(z.string()),
|
|
||||||
server_sentry_sample_rate: z.optional(z.number()),
|
|
||||||
server_sentry_trace_rate: z.optional(z.number()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// These parameters are sent in #page-params for both users and spectators.
|
// These parameters are sent in #page-params for both users and spectators.
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import * as Sentry from "@sentry/browser";
|
import * as Sentry from "@sentry/browser";
|
||||||
import assert from "minimalistic-assert";
|
import {z} from "zod";
|
||||||
|
|
||||||
import {page_params} from "./base_page_params";
|
|
||||||
import {current_user, realm} from "./state_data";
|
|
||||||
|
|
||||||
type UserInfo = {
|
type UserInfo = {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -10,13 +7,22 @@ type UserInfo = {
|
||||||
role?: string;
|
role?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sentry_key =
|
const sentry_params_schema = z.object({
|
||||||
// No parameter is the portico pages, empty string is the empty realm
|
dsn: z.string(),
|
||||||
page_params.realm_sentry_key === undefined
|
environment: z.string(),
|
||||||
? "www"
|
realm_key: z.string(),
|
||||||
: page_params.realm_sentry_key === ""
|
sample_rate: z.number(),
|
||||||
? "(root)"
|
server_version: z.string(),
|
||||||
: page_params.realm_sentry_key;
|
trace_rate: z.number(),
|
||||||
|
user: z.object({id: z.number(), role: z.string()}).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const sentry_params_json =
|
||||||
|
window.document?.querySelector("script#sentry-params")?.textContent ?? undefined;
|
||||||
|
const sentry_params =
|
||||||
|
sentry_params_json === undefined
|
||||||
|
? undefined
|
||||||
|
: sentry_params_schema.parse(JSON.parse(sentry_params_json));
|
||||||
|
|
||||||
export function normalize_path(path: string, is_portico = false): string {
|
export function normalize_path(path: string, is_portico = false): string {
|
||||||
if (path === undefined) {
|
if (path === undefined) {
|
||||||
|
@ -39,36 +45,7 @@ export function shouldCreateSpanForRequest(url: string): boolean {
|
||||||
return parsed.pathname !== "/json/events";
|
return parsed.pathname !== "/json/events";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize(): void {
|
if (sentry_params !== undefined) {
|
||||||
// The current_user and realm structures are not available until this is
|
|
||||||
// called from ui_init.initialize_everything.
|
|
||||||
assert(page_params.page_type === "home");
|
|
||||||
|
|
||||||
const user_role = current_user.is_owner
|
|
||||||
? "Organization owner"
|
|
||||||
: current_user.is_admin
|
|
||||||
? "Organization administrator"
|
|
||||||
: current_user.is_moderator
|
|
||||||
? "Moderator"
|
|
||||||
: current_user.is_guest
|
|
||||||
? "Guest"
|
|
||||||
: page_params.is_spectator
|
|
||||||
? "Spectator"
|
|
||||||
: current_user.user_id
|
|
||||||
? "Member"
|
|
||||||
: "Logged out";
|
|
||||||
const user_info: UserInfo = {realm: sentry_key, role: user_role};
|
|
||||||
if (current_user.user_id) {
|
|
||||||
user_info.id = current_user.user_id.toString();
|
|
||||||
}
|
|
||||||
Sentry.setTags({
|
|
||||||
user_role,
|
|
||||||
server_version: realm.zulip_version,
|
|
||||||
});
|
|
||||||
Sentry.setUser(user_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page_params.server_sentry_dsn) {
|
|
||||||
const sample_rates = new Map([
|
const sample_rates = new Map([
|
||||||
// This is controlled by shouldCreateSpanForRequest, above, but also put here for consistency
|
// This is controlled by shouldCreateSpanForRequest, above, but also put here for consistency
|
||||||
["call GET /json/events", 0],
|
["call GET /json/events", 0],
|
||||||
|
@ -78,8 +55,8 @@ if (page_params.server_sentry_dsn) {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: page_params.server_sentry_dsn,
|
dsn: sentry_params.dsn,
|
||||||
environment: page_params.server_sentry_environment ?? "development",
|
environment: sentry_params.environment,
|
||||||
tunnel: "/error_tracing",
|
tunnel: "/error_tracing",
|
||||||
|
|
||||||
release: "zulip-server@" + ZULIP_VERSION,
|
release: "zulip-server@" + ZULIP_VERSION,
|
||||||
|
@ -90,25 +67,34 @@ if (page_params.server_sentry_dsn) {
|
||||||
return {
|
return {
|
||||||
...context,
|
...context,
|
||||||
metadata: {source: "custom"},
|
metadata: {source: "custom"},
|
||||||
name: normalize_path(window.location.pathname, sentry_key === "www"),
|
name: normalize_path(
|
||||||
|
window.location.pathname,
|
||||||
|
sentry_params.realm_key === "www",
|
||||||
|
),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
shouldCreateSpanForRequest,
|
shouldCreateSpanForRequest,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
sampleRate: page_params.server_sentry_sample_rate ?? 0,
|
sampleRate: sentry_params.sample_rate,
|
||||||
tracesSampler(samplingContext) {
|
tracesSampler(samplingContext) {
|
||||||
const base_rate = page_params.server_sentry_trace_rate ?? 0;
|
const base_rate = sentry_params.trace_rate;
|
||||||
const name = samplingContext.transactionContext.name;
|
const name = samplingContext.transactionContext.name;
|
||||||
return base_rate * (sample_rates.get(name) ?? 1);
|
return base_rate * (sample_rates.get(name) ?? 1);
|
||||||
},
|
},
|
||||||
initialScope(scope) {
|
initialScope(scope) {
|
||||||
|
const user_role = sentry_params.user?.role ?? "Logged out";
|
||||||
const user_info: UserInfo = {
|
const user_info: UserInfo = {
|
||||||
realm: sentry_key,
|
realm: sentry_params.realm_key,
|
||||||
|
role: user_role,
|
||||||
};
|
};
|
||||||
|
if (sentry_params.user !== undefined) {
|
||||||
|
user_info.id = sentry_params.user.id.toString();
|
||||||
|
}
|
||||||
scope.setTags({
|
scope.setTags({
|
||||||
realm: sentry_key,
|
realm: sentry_params.realm_key,
|
||||||
user_role: "Browser",
|
server_version: sentry_params.server_version,
|
||||||
|
user_role,
|
||||||
});
|
});
|
||||||
scope.setUser(user_info);
|
scope.setUser(user_info);
|
||||||
return scope;
|
return scope;
|
||||||
|
|
|
@ -103,7 +103,6 @@ import * as scheduled_messages_ui from "./scheduled_messages_ui";
|
||||||
import * as scroll_bar from "./scroll_bar";
|
import * as scroll_bar from "./scroll_bar";
|
||||||
import * as scroll_util from "./scroll_util";
|
import * as scroll_util from "./scroll_util";
|
||||||
import * as search from "./search";
|
import * as search from "./search";
|
||||||
import * as sentry from "./sentry";
|
|
||||||
import * as server_events from "./server_events";
|
import * as server_events from "./server_events";
|
||||||
import * as settings from "./settings";
|
import * as settings from "./settings";
|
||||||
import * as settings_data from "./settings_data";
|
import * as settings_data from "./settings_data";
|
||||||
|
@ -414,7 +413,6 @@ export function initialize_everything(state_data) {
|
||||||
|
|
||||||
set_current_user(state_data.current_user);
|
set_current_user(state_data.current_user);
|
||||||
set_realm(state_data.realm);
|
set_realm(state_data.realm);
|
||||||
sentry.initialize();
|
|
||||||
|
|
||||||
/* To store theme data for spectators, we need to initialize
|
/* To store theme data for spectators, we need to initialize
|
||||||
user_settings before setting the theme. Because information
|
user_settings before setting the theme. Because information
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.http import HttpRequest
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import SafeString
|
from django.utils.safestring import SafeString
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
|
from django.utils.translation import override as override_language
|
||||||
|
|
||||||
from version import (
|
from version import (
|
||||||
LATEST_MAJOR_VERSION,
|
LATEST_MAJOR_VERSION,
|
||||||
|
@ -168,17 +169,8 @@ def zulip_default_context(request: HttpRequest) -> Dict[str, Any]:
|
||||||
# Sync this with default_params_schema in base_page_params.ts.
|
# Sync this with default_params_schema in base_page_params.ts.
|
||||||
default_page_params: Dict[str, Any] = {
|
default_page_params: Dict[str, Any] = {
|
||||||
**DEFAULT_PAGE_PARAMS,
|
**DEFAULT_PAGE_PARAMS,
|
||||||
"server_sentry_dsn": settings.SENTRY_FRONTEND_DSN,
|
|
||||||
"request_language": get_language(),
|
"request_language": get_language(),
|
||||||
}
|
}
|
||||||
if settings.SENTRY_FRONTEND_DSN is not None:
|
|
||||||
if realm is not None:
|
|
||||||
default_page_params["realm_sentry_key"] = realm.string_id
|
|
||||||
default_page_params["server_sentry_environment"] = get_config(
|
|
||||||
"machine", "deploy_type", "development"
|
|
||||||
)
|
|
||||||
default_page_params["server_sentry_sample_rate"] = settings.SENTRY_FRONTEND_SAMPLE_RATE
|
|
||||||
default_page_params["server_sentry_trace_rate"] = settings.SENTRY_FRONTEND_TRACE_RATE
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"root_domain_landing_page": settings.ROOT_DOMAIN_LANDING_PAGE,
|
"root_domain_landing_page": settings.ROOT_DOMAIN_LANDING_PAGE,
|
||||||
|
@ -217,6 +209,23 @@ def zulip_default_context(request: HttpRequest) -> Dict[str, Any]:
|
||||||
"corporate_enabled": corporate_enabled,
|
"corporate_enabled": corporate_enabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.SENTRY_FRONTEND_DSN is not None:
|
||||||
|
sentry_params = {
|
||||||
|
"dsn": settings.SENTRY_FRONTEND_DSN,
|
||||||
|
"environment": get_config("machine", "deploy_type", "development"),
|
||||||
|
"realm_key": "www" if realm is None else realm.string_id or "(root)",
|
||||||
|
"sample_rate": settings.SENTRY_FRONTEND_SAMPLE_RATE,
|
||||||
|
"server_version": ZULIP_VERSION,
|
||||||
|
"trace_rate": settings.SENTRY_FRONTEND_TRACE_RATE,
|
||||||
|
}
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
with override_language(None):
|
||||||
|
sentry_params["user"] = {
|
||||||
|
"id": request.user.id,
|
||||||
|
"role": request.user.get_role_name(),
|
||||||
|
}
|
||||||
|
context["sentry_params"] = sentry_params
|
||||||
|
|
||||||
context["PAGE_METADATA_URL"] = f"{realm_url}{request.path}"
|
context["PAGE_METADATA_URL"] = f"{realm_url}{request.path}"
|
||||||
if realm is not None and realm.icon_source == realm.ICON_UPLOADED:
|
if realm is not None and realm.icon_source == realm.ICON_UPLOADED:
|
||||||
context["PAGE_METADATA_IMAGE"] = urljoin(realm_url, realm_icon)
|
context["PAGE_METADATA_IMAGE"] = urljoin(realm_url, realm_icon)
|
||||||
|
|
|
@ -20,7 +20,6 @@ from zerver.lib.realm_description import get_realm_rendered_description
|
||||||
from zerver.lib.request import RequestNotes
|
from zerver.lib.request import RequestNotes
|
||||||
from zerver.models import Message, Realm, Stream, UserProfile
|
from zerver.models import Message, Realm, Stream, UserProfile
|
||||||
from zerver.views.message_flags import get_latest_update_message_flag_activity
|
from zerver.views.message_flags import get_latest_update_message_flag_activity
|
||||||
from zproject.config import get_config
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -230,17 +229,8 @@ def build_page_params_for_home_page_load(
|
||||||
# There is no event queue for spectators since
|
# There is no event queue for spectators since
|
||||||
# events support for spectators is not implemented yet.
|
# events support for spectators is not implemented yet.
|
||||||
no_event_queue=user_profile is None,
|
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
|
|
||||||
|
|
||||||
page_params["state_data"] = state_data
|
page_params["state_data"] = state_data
|
||||||
|
|
||||||
if narrow_stream is not None and state_data is not None:
|
if narrow_stream is not None and state_data is not None:
|
||||||
|
|
|
@ -699,6 +699,15 @@ Output:
|
||||||
page_params = orjson.loads(page_params_json)
|
page_params = orjson.loads(page_params_json)
|
||||||
return page_params
|
return page_params
|
||||||
|
|
||||||
|
def _get_sentry_params(self, response: "TestHttpResponse") -> Optional[Dict[str, Any]]:
|
||||||
|
doc = lxml.html.document_fromstring(response.content)
|
||||||
|
try:
|
||||||
|
script = cast(lxml.html.HtmlMixin, doc).get_element_by_id("sentry-params")
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
assert script is not None and script.text is not None
|
||||||
|
return orjson.loads(script.text)
|
||||||
|
|
||||||
def check_rendered_logged_in_app(self, result: "TestHttpResponse") -> None:
|
def check_rendered_logged_in_app(self, result: "TestHttpResponse") -> None:
|
||||||
"""Verifies that a visit of / was a 200 that rendered page_params
|
"""Verifies that a visit of / was a 200 that rendered page_params
|
||||||
and not for a (logged-out) spectator."""
|
and not for a (logged-out) spectator."""
|
||||||
|
|
|
@ -11,6 +11,7 @@ from django.test import override_settings
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
|
|
||||||
from corporate.models import Customer, CustomerPlan
|
from corporate.models import Customer, CustomerPlan
|
||||||
|
from version import ZULIP_VERSION
|
||||||
from zerver.actions.create_user import do_create_user
|
from zerver.actions.create_user import do_create_user
|
||||||
from zerver.actions.realm_settings import do_change_realm_plan_type, do_set_realm_property
|
from zerver.actions.realm_settings import do_change_realm_plan_type, do_set_realm_property
|
||||||
from zerver.actions.users import change_user_is_active
|
from zerver.actions.users import change_user_is_active
|
||||||
|
@ -55,7 +56,6 @@ class HomeTest(ZulipTestCase):
|
||||||
"page_type",
|
"page_type",
|
||||||
"promote_sponsoring_zulip",
|
"promote_sponsoring_zulip",
|
||||||
"request_language",
|
"request_language",
|
||||||
"server_sentry_dsn",
|
|
||||||
"show_billing",
|
"show_billing",
|
||||||
"show_plans",
|
"show_plans",
|
||||||
"show_remote_billing",
|
"show_remote_billing",
|
||||||
|
@ -358,7 +358,6 @@ class HomeTest(ZulipTestCase):
|
||||||
"promote_sponsoring_zulip",
|
"promote_sponsoring_zulip",
|
||||||
"realm_rendered_description",
|
"realm_rendered_description",
|
||||||
"request_language",
|
"request_language",
|
||||||
"server_sentry_dsn",
|
|
||||||
"show_billing",
|
"show_billing",
|
||||||
"show_plans",
|
"show_plans",
|
||||||
"show_remote_billing",
|
"show_remote_billing",
|
||||||
|
@ -497,47 +496,47 @@ class HomeTest(ZulipTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_sentry_keys(self) -> None:
|
def test_sentry_keys(self) -> None:
|
||||||
def home_params() -> Dict[str, Any]:
|
def sentry_params() -> Dict[str, Any] | None:
|
||||||
result = self._get_home_page()
|
result = self._get_home_page()
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
return self._get_page_params(result)
|
return self._get_sentry_params(result)
|
||||||
|
|
||||||
self.login("hamlet")
|
user = self.example_user("hamlet")
|
||||||
page_params = home_params()
|
self.login_user(user)
|
||||||
self.assertEqual(page_params["server_sentry_dsn"], None)
|
self.assertIsNone(sentry_params())
|
||||||
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"):
|
with self.settings(SENTRY_FRONTEND_DSN="https://aaa@bbb.ingest.sentry.io/1234"):
|
||||||
page_params = home_params()
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
page_params["server_sentry_dsn"], "https://aaa@bbb.ingest.sentry.io/1234"
|
sentry_params(),
|
||||||
|
{
|
||||||
|
"dsn": "https://aaa@bbb.ingest.sentry.io/1234",
|
||||||
|
"environment": "development",
|
||||||
|
"realm_key": "zulip",
|
||||||
|
"sample_rate": 1.0,
|
||||||
|
"server_version": ZULIP_VERSION,
|
||||||
|
"trace_rate": 0.1,
|
||||||
|
"user": {"id": user.id, "role": "Member"},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
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
|
# Make sure these still exist for logged-out users as well
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
do_set_realm_property(realm, "enable_spectator_access", True, acting_user=None)
|
do_set_realm_property(realm, "enable_spectator_access", True, acting_user=None)
|
||||||
self.logout()
|
self.logout()
|
||||||
page_params = home_params()
|
self.assertIsNone(sentry_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"):
|
with self.settings(SENTRY_FRONTEND_DSN="https://aaa@bbb.ingest.sentry.io/1234"):
|
||||||
page_params = home_params()
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
page_params["server_sentry_dsn"], "https://aaa@bbb.ingest.sentry.io/1234"
|
sentry_params(),
|
||||||
|
{
|
||||||
|
"dsn": "https://aaa@bbb.ingest.sentry.io/1234",
|
||||||
|
"environment": "development",
|
||||||
|
"realm_key": "zulip",
|
||||||
|
"sample_rate": 1.0,
|
||||||
|
"server_version": ZULIP_VERSION,
|
||||||
|
"trace_rate": 0.1,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
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:
|
def test_home_under_2fa_without_otp_device(self) -> None:
|
||||||
with self.settings(TWO_FACTOR_AUTHENTICATION_ENABLED=True):
|
with self.settings(TWO_FACTOR_AUTHENTICATION_ENABLED=True):
|
||||||
|
|
Loading…
Reference in New Issue