sentry: Address Sentry JavaScript 7.x deprecations.

https://docs.sentry.io/platforms/javascript/migration/v7-to-v8/

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2024-09-24 20:23:57 -07:00 committed by Tim Abbott
parent e9b031c003
commit a8304fb324
7 changed files with 78 additions and 93 deletions

View File

@ -93,7 +93,7 @@ export function error(msg: string, more_info?: object, original_error?: unknown)
// Note that original_error could be of any type, because you can "raise" // Note that original_error could be of any type, because you can "raise"
// any type -- something we do see in practice with the error // any type -- something we do see in practice with the error
// object being "dead": https://github.com/zulip/zulip/issues/18374 // object being "dead": https://github.com/zulip/zulip/issues/18374
Sentry.getCurrentHub().captureException(new Error(msg, {cause: original_error})); Sentry.captureException(new Error(msg, {cause: original_error}));
const args = build_arg_list(msg, more_info); const args = build_arg_list(msg, more_info);
logger.error(...args); logger.error(...args);

View File

@ -48,28 +48,32 @@ function call(args: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefine
return undefined; return undefined;
} }
const existing_span = Sentry.getCurrentHub().getScope().getSpan();
const txn_title = `call ${args.type} ${normalize_path(args.url)}`; const txn_title = `call ${args.type} ${normalize_path(args.url)}`;
const span_data = { const span_data = {
op: "function", op: "function",
description: txn_title,
data: { data: {
url: args.url, url: args.url,
method: args.type, method: args.type,
}, },
}; };
let span: Sentry.Span | undefined; /* istanbul ignore if */
if (!shouldCreateSpanForRequest(args.url)) { if (!shouldCreateSpanForRequest(args.url)) {
// Leave the span unset, so we don't record a transaction return call_in_span(undefined, args);
} else {
if (!existing_span) {
span = Sentry.startTransaction({...span_data, name: txn_title});
} else {
/* istanbul ignore next */
span = existing_span.startChild(span_data);
}
} }
return Sentry.startSpanManual({...span_data, name: txn_title}, (span) => {
try {
return call_in_span(span, args);
} catch (error) /* istanbul ignore next */ {
span?.end();
throw error;
}
});
}
function call_in_span(
span: Sentry.Span | undefined,
args: AjaxRequestHandlerOptions,
): JQuery.jqXHR<unknown> {
// Remember the number of completed password changes when the // Remember the number of completed password changes when the
// request was initiated. This allows us to detect race // request was initiated. This allows us to detect race
// situations where a password change occurred before we got a // situations where a password change occurred before we got a
@ -84,9 +88,10 @@ function call(args: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefine
// Ignore errors by default // Ignore errors by default
}); });
args.error = function wrapped_error(xhr, error_type, xhn) { args.error = function wrapped_error(xhr, error_type, xhn) {
/* istanbul ignore if */
if (span !== undefined) { if (span !== undefined) {
span.setHttpStatus(xhr.status); Sentry.setHttpStatus(span, xhr.status);
span.finish(); span.end();
} }
if (reload_state.is_in_progress()) { if (reload_state.is_in_progress()) {
// If we're in the process of reloading the browser, // If we're in the process of reloading the browser,
@ -149,9 +154,10 @@ function call(args: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefine
// Do nothing by default // Do nothing by default
}); });
args.success = function wrapped_success(data, textStatus, jqXHR) { args.success = function wrapped_success(data, textStatus, jqXHR) {
/* istanbul ignore if */
if (span !== undefined) { if (span !== undefined) {
span.setHttpStatus(jqXHR.status); Sentry.setHttpStatus(span, jqXHR.status);
span.finish(); span.end();
} }
if (reload_state.is_in_progress()) { if (reload_state.is_in_progress()) {
// If we're in the process of reloading the browser, // If we're in the process of reloading the browser,
@ -165,15 +171,7 @@ function call(args: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefine
orig_success(data, textStatus, jqXHR); orig_success(data, textStatus, jqXHR);
}; };
try { return $.ajax(args);
const scope = Sentry.getCurrentHub().pushScope();
if (span !== undefined) {
scope.setSpan(span);
}
return $.ajax(args);
} finally {
Sentry.getCurrentHub().popScope();
}
} }
export function get(options: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined { export function get(options: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined {

View File

@ -446,23 +446,11 @@ export function show(raw_terms: NarrowTerm[], show_opts: ShowMessageViewOpts): v
...show_opts, ...show_opts,
}; };
const existing_span = Sentry.getCurrentHub().getScope().getSpan();
const span_data = { const span_data = {
op: "function", op: "function",
description: "narrow",
data: {raw_terms, trigger: opts.trigger}, data: {raw_terms, trigger: opts.trigger},
}; };
let span; void Sentry.startSpan({...span_data, name: "narrow"}, async (span) => {
if (!existing_span) {
span = Sentry.startTransaction({...span_data, name: "narrow"});
} else {
span = existing_span.startChild(span_data);
}
let do_close_span = true;
try {
const scope = Sentry.getCurrentHub().pushScope();
scope.setSpan(span);
const id_info: TargetMessageIdInfo = { const id_info: TargetMessageIdInfo = {
target_id: undefined, target_id: undefined,
local_select_id: undefined, local_select_id: undefined,
@ -779,26 +767,16 @@ export function show(raw_terms: NarrowTerm[], show_opts: ShowMessageViewOpts): v
then_select_offset, then_select_offset,
); );
const post_span = span.startChild({ const post_span_context = {
name: "post-narrow busy time",
op: "function", op: "function",
description: "post-narrow busy time", };
}); await Sentry.startSpan(post_span_context, async () => {
do_close_span = false; span?.setStatus("ok");
span.setStatus("ok"); await new Promise((resolve) => setTimeout(resolve, 0));
setTimeout(() => {
resize.resize_stream_filters_container(); resize.resize_stream_filters_container();
post_span.finish(); });
span.finish(); });
}, 0);
} catch (error) {
span.setStatus("unknown_error");
throw error;
} finally {
if (do_close_span) {
span.finish();
}
Sentry.getCurrentHub().popScope();
}
} }
function navigate_to_anchor_message(opts: { function navigate_to_anchor_message(opts: {

View File

@ -19,7 +19,7 @@ export class MessageState {
server_acked = false; server_acked = false;
saw_event = false; saw_event = false;
txn: Sentry.Transaction | undefined = undefined; span: Sentry.Span | undefined = undefined;
event_span: Sentry.Span | undefined = undefined; event_span: Sentry.Span | undefined = undefined;
constructor(opts: {local_id: string; locally_echoed: boolean}) { constructor(opts: {local_id: string; locally_echoed: boolean}) {
@ -27,17 +27,27 @@ export class MessageState {
this.locally_echoed = opts.locally_echoed; this.locally_echoed = opts.locally_echoed;
} }
start_send(): Sentry.Transaction { wrap_send(callback: () => void): void {
this.txn = Sentry.startTransaction({ Sentry.startSpanManual(
op: "function", {
description: "message send", op: "function",
name: "message send", name: "message send",
}); },
this.event_span = this.txn.startChild({ (span) => {
op: "function", try {
description: "message send (server event loop)", this.span = span;
}); this.event_span = Sentry.startInactiveSpan({
return this.txn; op: "function",
name: "message send (server event loop)",
});
callback();
} catch (error) {
this.event_span?.end();
span?.end();
throw error;
}
},
);
} }
mark_disparity(): void { mark_disparity(): void {
@ -54,22 +64,27 @@ export class MessageState {
return; return;
} }
this.saw_event = true; this.saw_event = true;
this.event_span.finish(); this.event_span?.end();
this.maybe_finish_txn(); this.maybe_finish_txn();
} }
report_error(): void {
this.event_span?.end();
this.span?.end();
}
maybe_finish_txn(): void { maybe_finish_txn(): void {
if (!this.saw_event || !this.server_acked) { if (!this.saw_event || !this.server_acked) {
return; return;
} }
const setTag = (name: string, val: boolean): void => { const setTag = (name: string, val: boolean): void => {
const str_val = val ? "true" : "false"; const str_val = val ? "true" : "false";
this.event_span!.setTag(name, str_val); this.event_span?.setAttribute(name, str_val);
this.txn!.setTag(name, str_val); this.span?.setAttribute(name, str_val);
}; };
setTag("rendered_changed", this.rendered_changed); setTag("rendered_changed", this.rendered_changed);
setTag("locally_echoed", this.locally_echoed); setTag("locally_echoed", this.locally_echoed);
this.txn!.finish(); this.span?.end();
messages.delete(this.local_id); messages.delete(this.local_id);
} }
} }
@ -102,13 +117,13 @@ export function get_message_state(local_id: string): MessageState | undefined {
return state; return state;
} }
export function start_send(local_id: string): Sentry.Transaction | undefined { export function wrap_send(local_id: string, callback: () => void): void {
const state = get_message_state(local_id); const state = get_message_state(local_id);
if (!state) { if (state) {
return undefined; state.wrap_send(callback);
} else {
callback();
} }
return state.start_send();
} }
export function mark_disparity(local_id: string): void { export function mark_disparity(local_id: string): void {

View File

@ -61,9 +61,9 @@ if (sentry_params !== undefined) {
release: "zulip-server@" + ZULIP_VERSION, release: "zulip-server@" + ZULIP_VERSION,
integrations: [ integrations: [
new Sentry.BrowserTracing({ Sentry.browserTracingIntegration({
startTransactionOnLocationChange: false, instrumentNavigation: false,
beforeNavigate(context) { beforeStartSpan(context) {
return { return {
...context, ...context,
metadata: {source: "custom"}, metadata: {source: "custom"},
@ -101,8 +101,5 @@ if (sentry_params !== undefined) {
}, },
}); });
} else { } else {
// Always add the tracing extensions, so Sentry doesn't throw runtime errors if one calls
// startTransaction without having created the Sentry.BrowserTracing object.
Sentry.addTracingExtensions();
Sentry.init({}); Sentry.init({});
} }

View File

@ -1,5 +1,3 @@
import * as Sentry from "@sentry/browser";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import * as channel from "./channel"; import * as channel from "./channel";
import * as people from "./people"; import * as people from "./people";
@ -17,10 +15,7 @@ export function send_message(request, on_success, error) {
locally_echoed: request.locally_echoed, locally_echoed: request.locally_echoed,
}); });
} }
const txn = sent_messages.start_send(request.local_id); sent_messages.wrap_send(request.local_id, () => {
try {
const scope = Sentry.getCurrentHub().pushScope();
scope.setSpan(txn);
channel.post({ channel.post({
url: "/json/messages", url: "/json/messages",
data: request, data: request,
@ -58,6 +53,7 @@ export function send_message(request, on_success, error) {
} }
}, },
error(xhr, error_type) { error(xhr, error_type) {
sent_messages.get_message_state(request.local_id)?.report_error();
if (error_type !== "timeout" && reload_state.is_pending()) { if (error_type !== "timeout" && reload_state.is_pending()) {
// The error might be due to the server changing // The error might be due to the server changing
reload.initiate({ reload.initiate({
@ -72,9 +68,7 @@ export function send_message(request, on_success, error) {
error(response, xhr.responseJSON?.code); error(response, xhr.responseJSON?.code);
}, },
}); });
} finally { });
Sentry.getCurrentHub().popScope();
}
} }
export function reply_message(opts) { export function reply_message(opts) {

View File

@ -14,9 +14,12 @@ const sent_messages = mock_esm("../src/sent_messages", {
start_tracking_message: noop, start_tracking_message: noop,
get_message_state: () => ({ get_message_state: () => ({
report_server_ack: noop, report_server_ack: noop,
report_error: noop,
saw_event: true, saw_event: true,
}), }),
start_send: noop, wrap_send(_local_id, callback) {
callback();
},
}); });
const server_events = mock_esm("../src/server_events"); const server_events = mock_esm("../src/server_events");