Add Sentry instrumentation for JavaScript

This commit is contained in:
Sebastian Serth
2023-05-09 21:15:23 +02:00
parent aa05fcadf8
commit 240fbc5a3b
12 changed files with 166 additions and 59 deletions

View File

@ -45,27 +45,23 @@ $(document).on('turbolinks:load', function() {
// Initialize Sentry
const sentrySettings = $('meta[name="sentry"]')
if (sentrySettings.data()['enabled']) {
// Workaround for Turbolinks: We must not re-initialize the Relay object when visiting another page
window.SentryReplay ||= new Sentry.Replay();
Sentry.init({
dsn: sentrySettings.data('dsn'),
attachStacktrace: true,
release: sentrySettings.data('release'),
environment: sentrySettings.data('environment'),
autoSessionTracking: false,
autoSessionTracking: true,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.0,
replaysOnErrorSampleRate: 1.0,
integrations: [
SentryReplay,
],
});
integrations: window.SentryIntegrations,
initialScope: scope =>{
const user = $('meta[name="current-user"]').attr('content');
Sentry.configureScope(function (scope) {
const user = $('meta[name="current-user"]').attr('content');
if (user) {
scope.setUser(JSON.parse(user));
if (user) {
scope.setUser(JSON.parse(user));
}
return scope;
}
});
}

View File

@ -212,6 +212,16 @@ var CodeOceanEditor = {
$('button i.fa-spin').removeClass('d-inline-block').addClass('d-none');
},
startSentryTransaction: function (initiator) {
const cause = initiator.data('cause') || initiator.prop('id');
this.sentryTransaction = window.SentryUtils.startIdleTransaction(
Sentry.getCurrentHub(),
{ name: cause, op: "transaction" },
0, // Idle Timeout
window.SentryUtils.TRACING_DEFAULTS.finalTimeout,
true); // onContext
Sentry.getCurrentHub().configureScope(scope => scope.setSpan(this.sentryTransaction));
},
resizeAceEditors: function (own_solution = false) {
let editorSelector;

View File

@ -3,16 +3,19 @@ CodeOceanEditorEvaluation = {
// A list of non-printable characters that are not allowed in the code output.
// Taken from https://stackoverflow.com/a/69024306
nonPrintableRegEx: /[\u0000-\u0008\u000B\u000C\u000F-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF]/g,
sentryTransaction: null,
/**
* Scoring-Functions
*/
scoreCode: function (event) {
const cause = $('#assess');
this.startSentryTransaction(cause);
event.preventDefault();
this.stopCode(event);
this.clearScoringOutput();
$('#submit').addClass("d-none");
this.createSubmission('#assess', null, function (response) {
this.createSubmission(cause, null, function (response) {
this.showSpinner($('#assess'));
$('#score_div').removeClass('d-none');
var url = response.score_url;

View File

@ -1,7 +1,7 @@
CodeOceanEditorWebsocket = {
websocket: null,
createSocketUrl: function(url) {
createSocketUrl: function(url, span) {
const sockURL = new URL(url, window.location);
// not needed any longer, we put it directly into the url: sockURL.pathname = url;
@ -11,17 +11,27 @@ CodeOceanEditorWebsocket = {
// strip anchor if it is in the url
sockURL.hash = '';
sockURL.searchParams.set('HTTP_SENTRY_TRACE', span.toTraceparent());
const dynamicContext = this.sentryTransaction.getDynamicSamplingContext();
const baggage = SentryUtils.dynamicSamplingContextToSentryBaggageHeader(dynamicContext);
sockURL.searchParams.set('HTTP_BAGGAGE', baggage);
return sockURL.toString();
},
initializeSocket: function(url) {
this.websocket = new CommandSocket(this.createSocketUrl(url),
const cleanedPath = url.replace(/\/\d+\//, '/*/').replace(/\/[^\/]+$/, '/*');
const websocketHost = window.location.origin.replace(/^http/, 'ws');
const sentryDescription = `WebSocket ${websocketHost}${cleanedPath}`;
const span = this.sentryTransaction.startChild({op: 'websocket.client', description: sentryDescription})
this.websocket = new CommandSocket(this.createSocketUrl(url, span),
function (evt) {
this.resetOutputTab();
}.bind(this)
);
CodeOceanEditorWebsocket.websocket = this.websocket;
this.websocket.onError(this.showWebsocketError.bind(this));
this.websocket.onClose(span.finish.bind(span));
},
initializeSocketForTesting: function(url) {

View File

@ -112,6 +112,7 @@ CodeOceanEditorSubmissions = {
},
resetCode: function(initiator, onlyActiveFile = false) {
this.startSentryTransaction(initiator);
this.showSpinner(initiator);
this.ajax({
method: 'GET',
@ -131,9 +132,11 @@ CodeOceanEditorSubmissions = {
},
renderCode: function(event) {
const cause = $('#render');
this.startSentryTransaction(cause);
event.preventDefault();
if ($('#render').is(':visible')) {
this.createSubmission('#render', null, function (response) {
this.createSubmission(cause, null, function (response) {
if (response.render_url === undefined) return;
const active_file = CodeOceanEditor.active_file.filename.replace(/#$/,''); // remove # if it is the last character, this is not part of the filename and just an anchor
@ -162,10 +165,12 @@ CodeOceanEditorSubmissions = {
* Execution-Logic
*/
runCode: function(event) {
const cause = $('#run');
this.startSentryTransaction(cause);
event.preventDefault();
this.stopCode(event);
if ($('#run').is(':visible')) {
this.createSubmission('#run', null, this.runSubmission.bind(this));
this.createSubmission(cause, null, this.runSubmission.bind(this));
}
},
@ -189,9 +194,11 @@ CodeOceanEditorSubmissions = {
},
testCode: function(event) {
const cause = $('#test');
this.startSentryTransaction(cause);
event.preventDefault();
if ($('#test').is(':visible')) {
this.createSubmission('#test', null, function(response) {
this.createSubmission(cause, null, function(response) {
this.showSpinner($('#test'));
$('#score_div').addClass('d-none');
var url = response.test_url.replace(this.FILENAME_URL_PLACEHOLDER, CodeOceanEditor.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor
@ -202,6 +209,7 @@ CodeOceanEditorSubmissions = {
submitCode: function(event) {
const button = $(event.target) || $('#submit');
this.startSentryTransaction(button);
this.teardownEventHandlers();
this.createSubmission(button, null, function (response) {
if (response.redirect) {

View File

@ -14,6 +14,10 @@ CommandSocket.prototype.onError = function(callback){
this.websocket.onerror = callback
};
CommandSocket.prototype.onClose = function(callback){
this.websocket.onclose = callback
};
/**
* Allows it to register an event-handler on the given cmd.
* The handler needs to accept one argument, the message.

View File

@ -16,11 +16,22 @@ import 'jstree';
import * as _ from 'underscore';
import * as d3 from 'd3';
import * as Sentry from '@sentry/browser';
import * as SentryIntegration from '@sentry/integrations';
import { startIdleTransaction, TRACING_DEFAULTS } from '@sentry/core';
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
import 'sorttable';
window.bootstrap = bootstrap; // Publish bootstrap in global namespace
window._ = _; // Publish underscore's `_` in global namespace
window.d3 = d3; // Publish d3 in global namespace
window.Sentry = Sentry; // Publish sentry in global namespace
window.SentryIntegrations = [ // Publish sentry integration in global namespace
new SentryIntegration.ReportingObserver(),
new SentryIntegration.ExtraErrorData(),
new SentryIntegration.HttpClient(),
new Sentry.BrowserTracing(),
new Sentry.Replay(),
];
window.SentryUtils = { dynamicSamplingContextToSentryBaggageHeader, startIdleTransaction, TRACING_DEFAULTS };
// CSS
import 'chosen-js/chosen.css';