Commit de1d9ec4 authored by Mike Greiling's avatar Mike Greiling

Merge branch '26138-replace-raven-js-with-sentry-browser' into 'master'

Resolve "Replace raven-js with @sentry/browser"

Closes #26138

See merge request gitlab-org/gitlab!17715
parents b1545353 35022c0f
import RavenConfig from './raven_config';
import SentryConfig from './sentry_config';
const index = function index() {
RavenConfig.init({
sentryDsn: gon.sentry_dsn,
SentryConfig.init({
dsn: gon.sentry_dsn,
currentUserId: gon.current_user_id,
whitelistUrls:
process.env.NODE_ENV === 'production'
......@@ -15,7 +15,7 @@ const index = function index() {
},
});
return RavenConfig;
return SentryConfig;
};
index();
......
import Raven from 'raven-js';
import * as Sentry from '@sentry/browser';
import $ from 'jquery';
import { __ } from '~/locale';
......@@ -26,7 +26,7 @@ const IGNORE_ERRORS = [
'conduitPage',
];
const IGNORE_URLS = [
const BLACKLIST_URLS = [
// Facebook flakiness
/graph\.facebook\.com/i,
// Facebook blocked
......@@ -43,62 +43,62 @@ const IGNORE_URLS = [
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
];
const SAMPLE_RATE = 95;
const SAMPLE_RATE = 0.95;
const RavenConfig = {
const SentryConfig = {
IGNORE_ERRORS,
IGNORE_URLS,
BLACKLIST_URLS,
SAMPLE_RATE,
init(options = {}) {
this.options = options;
this.configure();
this.bindRavenErrors();
this.bindSentryErrors();
if (this.options.currentUserId) this.setUser();
},
configure() {
Raven.config(this.options.sentryDsn, {
release: this.options.release,
tags: this.options.tags,
whitelistUrls: this.options.whitelistUrls,
environment: this.options.environment,
ignoreErrors: this.IGNORE_ERRORS,
ignoreUrls: this.IGNORE_URLS,
shouldSendCallback: this.shouldSendSample.bind(this),
}).install();
const { dsn, release, tags, whitelistUrls, environment } = this.options;
Sentry.init({
dsn,
release,
tags,
whitelistUrls,
environment,
ignoreErrors: this.IGNORE_ERRORS, // TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
blacklistUrls: this.BLACKLIST_URLS,
sampleRate: SAMPLE_RATE,
});
},
setUser() {
Raven.setUserContext({
Sentry.setUser({
id: this.options.currentUserId,
});
},
bindRavenErrors() {
$(document).on('ajaxError.raven', this.handleRavenErrors);
bindSentryErrors() {
$(document).on('ajaxError.sentry', this.handleSentryErrors);
},
handleRavenErrors(event, req, config, err) {
handleSentryErrors(event, req, config, err) {
const error = err || req.statusText;
const responseText = req.responseText || __('Unknown response text');
const { responseText = __('Unknown response text') } = req;
const { type, url, data } = config;
const { status } = req;
Raven.captureMessage(error, {
Sentry.captureMessage(error, {
extra: {
type: config.type,
url: config.url,
data: config.data,
status: req.status,
type,
url,
data,
status,
response: responseText,
error,
event,
},
});
},
shouldSendSample() {
return Math.random() * 100 <= this.SAMPLE_RATE;
},
};
export default RavenConfig;
export default SentryConfig;
......@@ -57,7 +57,7 @@
= yield :library_javascripts
= javascript_include_tag locale_path unless I18n.locale == :en
= webpack_bundle_tag "raven" if Gitlab.config.sentry.enabled
= webpack_bundle_tag "sentry" if Gitlab.config.sentry.enabled
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
......
---
title: Replace raven-js with @sentry/browser
merge_request: 17715
author:
type: changed
......@@ -73,7 +73,7 @@ function generateEntries() {
const manualEntries = {
default: defaultEntries,
raven: './raven/index.js',
sentry: './sentry/index.js',
};
return Object.assign(manualEntries, autoEntries);
......
......@@ -2,26 +2,27 @@
require 'spec_helper'
describe 'RavenJS' do
let(:raven_path) { '/raven.chunk.js' }
describe 'Sentry' do
let(:sentry_path) { '/sentry.chunk.js' }
it 'does not load raven if sentry is disabled' do
it 'does not load sentry if sentry is disabled' do
allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
visit new_user_session_path
expect(has_requested_raven).to eq(false)
expect(has_requested_sentry).to eq(false)
end
it 'loads raven if sentry is enabled' do
it 'loads sentry if sentry is enabled' do
stub_sentry_settings
visit new_user_session_path
expect(has_requested_raven).to eq(true)
expect(has_requested_sentry).to eq(true)
end
def has_requested_raven
def has_requested_sentry
page.all('script', visible: false).one? do |elm|
elm[:src] =~ /#{raven_path}$/
elm[:src] =~ /#{sentry_path}$/
end
end
end
import RavenConfig from '~/raven/raven_config';
import index from '~/raven/index';
import SentryConfig from '~/sentry/sentry_config';
import index from '~/sentry/index';
describe('RavenConfig options', () => {
const sentryDsn = 'sentryDsn';
describe('SentryConfig options', () => {
const dsn = 'https://123@sentry.gitlab.test/123';
const currentUserId = 'currentUserId';
const gitlabUrl = 'gitlabUrl';
const environment = 'test';
......@@ -11,7 +11,7 @@ describe('RavenConfig options', () => {
beforeEach(() => {
window.gon = {
sentry_dsn: sentryDsn,
sentry_dsn: dsn,
sentry_environment: environment,
current_user_id: currentUserId,
gitlab_url: gitlabUrl,
......@@ -20,14 +20,14 @@ describe('RavenConfig options', () => {
process.env.HEAD_COMMIT_SHA = revision;
spyOn(RavenConfig, 'init');
jest.spyOn(SentryConfig, 'init').mockImplementation();
indexReturnValue = index();
});
it('should init with .sentryDsn, .currentUserId, .whitelistUrls and environment', () => {
expect(RavenConfig.init).toHaveBeenCalledWith({
sentryDsn,
expect(SentryConfig.init).toHaveBeenCalledWith({
dsn,
currentUserId,
whitelistUrls: [gitlabUrl, 'webpack-internal://'],
environment,
......@@ -38,7 +38,7 @@ describe('RavenConfig options', () => {
});
});
it('should return RavenConfig', () => {
expect(indexReturnValue).toBe(RavenConfig);
it('should return SentryConfig', () => {
expect(indexReturnValue).toBe(SentryConfig);
});
});
import Raven from 'raven-js';
import RavenConfig from '~/raven/raven_config';
import * as Sentry from '@sentry/browser';
import SentryConfig from '~/sentry/sentry_config';
describe('RavenConfig', () => {
describe('SentryConfig', () => {
describe('IGNORE_ERRORS', () => {
it('should be an array of strings', () => {
const areStrings = RavenConfig.IGNORE_ERRORS.every(error => typeof error === 'string');
const areStrings = SentryConfig.IGNORE_ERRORS.every(error => typeof error === 'string');
expect(areStrings).toBe(true);
});
});
describe('IGNORE_URLS', () => {
describe('BLACKLIST_URLS', () => {
it('should be an array of regexps', () => {
const areRegExps = RavenConfig.IGNORE_URLS.every(url => url instanceof RegExp);
const areRegExps = SentryConfig.BLACKLIST_URLS.every(url => url instanceof RegExp);
expect(areRegExps).toBe(true);
});
......@@ -20,7 +20,7 @@ describe('RavenConfig', () => {
describe('SAMPLE_RATE', () => {
it('should be a finite number', () => {
expect(typeof RavenConfig.SAMPLE_RATE).toEqual('number');
expect(typeof SentryConfig.SAMPLE_RATE).toEqual('number');
});
});
......@@ -30,45 +30,44 @@ describe('RavenConfig', () => {
};
beforeEach(() => {
spyOn(RavenConfig, 'configure');
spyOn(RavenConfig, 'bindRavenErrors');
spyOn(RavenConfig, 'setUser');
jest.spyOn(SentryConfig, 'configure');
jest.spyOn(SentryConfig, 'bindSentryErrors');
jest.spyOn(SentryConfig, 'setUser');
RavenConfig.init(options);
SentryConfig.init(options);
});
it('should set the options property', () => {
expect(RavenConfig.options).toEqual(options);
expect(SentryConfig.options).toEqual(options);
});
it('should call the configure method', () => {
expect(RavenConfig.configure).toHaveBeenCalled();
expect(SentryConfig.configure).toHaveBeenCalled();
});
it('should call the error bindings method', () => {
expect(RavenConfig.bindRavenErrors).toHaveBeenCalled();
expect(SentryConfig.bindSentryErrors).toHaveBeenCalled();
});
it('should call setUser', () => {
expect(RavenConfig.setUser).toHaveBeenCalled();
expect(SentryConfig.setUser).toHaveBeenCalled();
});
it('should not call setUser if there is no current user ID', () => {
RavenConfig.setUser.calls.reset();
jest.clearAllMocks();
options.currentUserId = undefined;
RavenConfig.init(options);
SentryConfig.init(options);
expect(RavenConfig.setUser).not.toHaveBeenCalled();
expect(SentryConfig.setUser).not.toHaveBeenCalled();
});
});
describe('configure', () => {
let raven;
let ravenConfig;
const sentryConfig = {};
const options = {
sentryDsn: '//sentryDsn',
dsn: 'https://123@sentry.gitlab.test/123',
whitelistUrls: ['//gitlabUrl', 'webpack-internal://'],
environment: 'test',
release: 'revision',
......@@ -78,69 +77,64 @@ describe('RavenConfig', () => {
};
beforeEach(() => {
ravenConfig = jasmine.createSpyObj('ravenConfig', ['shouldSendSample']);
raven = jasmine.createSpyObj('raven', ['install']);
jest.spyOn(Sentry, 'init').mockImplementation();
spyOn(Raven, 'config').and.returnValue(raven);
sentryConfig.options = options;
sentryConfig.IGNORE_ERRORS = 'ignore_errors';
sentryConfig.BLACKLIST_URLS = 'blacklist_urls';
ravenConfig.options = options;
ravenConfig.IGNORE_ERRORS = 'ignore_errors';
ravenConfig.IGNORE_URLS = 'ignore_urls';
RavenConfig.configure.call(ravenConfig);
SentryConfig.configure.call(sentryConfig);
});
it('should call Raven.config', () => {
expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, {
it('should call Sentry.init', () => {
expect(Sentry.init).toHaveBeenCalledWith({
dsn: options.dsn,
release: options.release,
tags: options.tags,
sampleRate: 0.95,
whitelistUrls: options.whitelistUrls,
environment: 'test',
ignoreErrors: ravenConfig.IGNORE_ERRORS,
ignoreUrls: ravenConfig.IGNORE_URLS,
shouldSendCallback: jasmine.any(Function),
ignoreErrors: sentryConfig.IGNORE_ERRORS,
blacklistUrls: sentryConfig.BLACKLIST_URLS,
});
});
it('should call Raven.install', () => {
expect(raven.install).toHaveBeenCalled();
});
it('should set environment from options', () => {
ravenConfig.options.environment = 'development';
sentryConfig.options.environment = 'development';
RavenConfig.configure.call(ravenConfig);
SentryConfig.configure.call(sentryConfig);
expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, {
expect(Sentry.init).toHaveBeenCalledWith({
dsn: options.dsn,
release: options.release,
tags: options.tags,
sampleRate: 0.95,
whitelistUrls: options.whitelistUrls,
environment: 'development',
ignoreErrors: ravenConfig.IGNORE_ERRORS,
ignoreUrls: ravenConfig.IGNORE_URLS,
shouldSendCallback: jasmine.any(Function),
ignoreErrors: sentryConfig.IGNORE_ERRORS,
blacklistUrls: sentryConfig.BLACKLIST_URLS,
});
});
});
describe('setUser', () => {
let ravenConfig;
let sentryConfig;
beforeEach(() => {
ravenConfig = { options: { currentUserId: 1 } };
spyOn(Raven, 'setUserContext');
sentryConfig = { options: { currentUserId: 1 } };
jest.spyOn(Sentry, 'setUser');
RavenConfig.setUser.call(ravenConfig);
SentryConfig.setUser.call(sentryConfig);
});
it('should call .setUserContext', function() {
expect(Raven.setUserContext).toHaveBeenCalledWith({
id: ravenConfig.options.currentUserId,
it('should call .setUser', () => {
expect(Sentry.setUser).toHaveBeenCalledWith({
id: sentryConfig.options.currentUserId,
});
});
});
describe('handleRavenErrors', () => {
describe('handleSentryErrors', () => {
let event;
let req;
let config;
......@@ -148,17 +142,17 @@ describe('RavenConfig', () => {
beforeEach(() => {
event = {};
req = { status: 'status', responseText: 'responseText', statusText: 'statusText' };
req = { status: 'status', responseText: 'Unknown response text', statusText: 'statusText' };
config = { type: 'type', url: 'url', data: 'data' };
err = {};
spyOn(Raven, 'captureMessage');
jest.spyOn(Sentry, 'captureMessage');
RavenConfig.handleRavenErrors(event, req, config, err);
SentryConfig.handleSentryErrors(event, req, config, err);
});
it('should call Raven.captureMessage', () => {
expect(Raven.captureMessage).toHaveBeenCalledWith(err, {
it('should call Sentry.captureMessage', () => {
expect(Sentry.captureMessage).toHaveBeenCalledWith(err, {
extra: {
type: config.type,
url: config.url,
......@@ -173,13 +167,13 @@ describe('RavenConfig', () => {
describe('if no err is provided', () => {
beforeEach(() => {
Raven.captureMessage.calls.reset();
jest.clearAllMocks();
RavenConfig.handleRavenErrors(event, req, config);
SentryConfig.handleSentryErrors(event, req, config);
});
it('should use req.statusText as the error value', () => {
expect(Raven.captureMessage).toHaveBeenCalledWith(req.statusText, {
expect(Sentry.captureMessage).toHaveBeenCalledWith(req.statusText, {
extra: {
type: config.type,
url: config.url,
......@@ -197,13 +191,13 @@ describe('RavenConfig', () => {
beforeEach(() => {
req.responseText = undefined;
Raven.captureMessage.calls.reset();
jest.clearAllMocks();
RavenConfig.handleRavenErrors(event, req, config, err);
SentryConfig.handleSentryErrors(event, req, config, err);
});
it('should use `Unknown response text` as the response', () => {
expect(Raven.captureMessage).toHaveBeenCalledWith(err, {
expect(Sentry.captureMessage).toHaveBeenCalledWith(err, {
extra: {
type: config.type,
url: config.url,
......@@ -217,38 +211,4 @@ describe('RavenConfig', () => {
});
});
});
describe('shouldSendSample', () => {
let randomNumber;
beforeEach(() => {
RavenConfig.SAMPLE_RATE = 50;
spyOn(Math, 'random').and.callFake(() => randomNumber);
});
it('should call Math.random', () => {
RavenConfig.shouldSendSample();
expect(Math.random).toHaveBeenCalled();
});
it('should return true if the sample rate is greater than the random number * 100', () => {
randomNumber = 0.1;
expect(RavenConfig.shouldSendSample()).toBe(true);
});
it('should return false if the sample rate is less than the random number * 100', () => {
randomNumber = 0.9;
expect(RavenConfig.shouldSendSample()).toBe(false);
});
it('should return true if the sample rate is equal to the random number * 100', () => {
randomNumber = 0.5;
expect(RavenConfig.shouldSendSample()).toBe(true);
});
});
});
......@@ -1191,6 +1191,58 @@
consola "^2.3.0"
node-fetch "^2.3.0"
"@sentry/browser@^5.7.1":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.7.1.tgz#1f8435e2a325d7a09f830065ebce40a2b3c708a4"
integrity sha512-K0x1XhsHS8PPdtlVOLrKZyYvi5Vexs9WApdd214bO6KaGF296gJvH1mG8XOY0+7aA5i2A7T3ttcaJNDYS49lzw==
dependencies:
"@sentry/core" "5.7.1"
"@sentry/types" "5.7.1"
"@sentry/utils" "5.7.1"
tslib "^1.9.3"
"@sentry/core@5.7.1":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.7.1.tgz#3eb2b7662cac68245931ee939ec809bf7a639d0e"
integrity sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg==
dependencies:
"@sentry/hub" "5.7.1"
"@sentry/minimal" "5.7.1"
"@sentry/types" "5.7.1"
"@sentry/utils" "5.7.1"
tslib "^1.9.3"
"@sentry/hub@5.7.1":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.7.1.tgz#a52acd9fead7f3779d96e9965c6978aecc8b9cad"
integrity sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig==
dependencies:
"@sentry/types" "5.7.1"
"@sentry/utils" "5.7.1"
tslib "^1.9.3"
"@sentry/minimal@5.7.1":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.7.1.tgz#56afc537737586929e25349765e37a367958c1e1"
integrity sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g==
dependencies:
"@sentry/hub" "5.7.1"
"@sentry/types" "5.7.1"
tslib "^1.9.3"
"@sentry/types@5.7.1":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.7.1.tgz#4c4c1d4d891b6b8c2c3c7b367d306a8b1350f090"
integrity sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ==
"@sentry/utils@5.7.1":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.7.1.tgz#cf37ad55f78e317665cd8680f202d307fa77f1d0"
integrity sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q==
dependencies:
"@sentry/types" "5.7.1"
tslib "^1.9.3"
"@types/anymatch@*":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.0.tgz#d1d55958d1fccc5527d4aba29fc9c4b942f563ff"
......@@ -9989,11 +10041,6 @@ raphael@^2.2.7:
dependencies:
eve-raphael "0.5.0"
raven-js@^3.22.1:
version "3.22.1"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
integrity sha512-2Y8czUl5a9usbvXbpV8a+GPAiDXjxQjaHImZL0TyJWI5k5jV/6o+wceaBAg9g6RpO9OOJp0/w2mMs6pBoqOyDA==
raw-body@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment