/* global WP_DEFINE_IS_NODE, WP_DEFINE_DEVELOPMENT */
// Yes, this is global state, but couldn't find another method
// the raven client should only be created once when starting the server
// and could be used in all other places to log errors
import isLocalOrDevelopment from '../../../server/util/isLocalOrDevelopment';
let ravenClient = null;
/** @module */
/**
* Gets either the server or browser version of the Raven client
* Can be called from any isomorphic code
* @return {*}
*/
export function getClient() {
return ravenClient;
}
/**
* Create/config a raven client for nodejs.
*
* @function createClientOnServer
* @param raven The nodejs raven client
* @param ravenConfig The nodejs raven config
* @param buildInfo Will be added to all exceptions to track in which builds the errors
* are happening
* @return {*}
* @category tracking
*/
export function createClientOnServer(raven, ravenConfig, buildInfo) {
if (isLocalOrDevelopment()) {
return null;
}
raven
.config(ravenConfig.dsn, {
dataCallback: data => {
// remove references to the environment
/* eslint-disable no-param-reassign */
if (data && data.request) {
delete data.request.env;
}
/* eslint-enable */
return data;
},
release: buildInfo.release,
commit: buildInfo.commit,
branch: buildInfo.branch,
tag: buildInfo.tag,
fingerprint: ['{{ default }}', process.env.NODE_ENV],
autoBreadcrumbs: {
console: true,
http: true,
postgres: false,
},
captureUnhandledRejections: true,
})
.install();
raven.setContext({
tags: {
env: process.env.NODE_ENV,
},
});
// globall error handler
raven.install((a, err) => {
/* eslint-disable no-console */
console.error(err.stack.replace('\\n', '\n'));
/* eslint-enable */
process.exit(1);
});
ravenClient = raven;
return ravenClient;
}
/**
* Create/config a raven client for the browser.
*
* @function configureRavenOnClient
* @param Raven The javascript raven client
* @param ravenConfig The nodejs raven config
* @param buildInfo Will be added to all exceptions to track in which builds the errors
* are happening
* @category tracking
*/
export function configureRavenOnClient(Raven, ravenConfig, buildInfo) {
if (isLocalOrDevelopment()) {
return;
}
Raven.config(ravenConfig.dsn, {
// we highly recommend restricting exceptions to a domain in order to filter out clutter
whitelistUrls: ravenConfig.whitelistUrls.split(','),
ignoreErrors: [
// Random plugins/extensions
'top.GLOBALS',
// See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error. html
'originalCreateNotification',
'canvas.contentDocument',
'MyApp_RemoveAllHighlights',
'http://tt.epicplay.com',
"Can't find variable: ZiteReader",
'jigsaw is not defined',
'ComboSearch is not defined',
'http://loading.retry.widdit.com/',
'atomicFindClose',
// Facebook borked
'fb_xd_fragment',
// ISP "optimizing" proxy - `Cache-Control: no-transform` seems to
// reduce this. (thanks @acdha)
// See http://stackoverflow.com/questions/4113268
'bmi_SafeAddOnload',
'EBCallBackMessageReceived',
// See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
'conduitPage',
],
ignoreUrls: [
// Facebook flakiness
/graph\.facebook\.com/i,
// Facebook blocked
/connect\.facebook\.net\/en_US\/all\.js/i,
// Woopra flakiness
/eatdifferent\.com\.woopra-ns\.com/i,
/static\.woopra\.com\/js\/woopra\.js/i,
// Chrome extensions
/extensions\//i,
/^chrome:\/\//i,
// Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
],
release: buildInfo.release,
commit: buildInfo.commit,
branch: buildInfo.branch,
tag: buildInfo.tag,
autoBreadcrumbs: true,
}).install();
ravenClient = Raven;
}
/**
* Used for capturing XHR errors from any of the API Gateways
* @param error
*/
export function captureXhrError(error) {
const client = getClient();
if (client) {
const blacklist = {
'weigh-ins/current': [404],
'competition-entries/banner': [404],
favourites: [404],
'accept-lock': [202],
features: [404],
groups: [404],
};
// hide all 4xx errors (Client Error), and others that are blacklisted above
const ignoreError =
(error.response.status >= 400 && error.response.status < 500) ||
Object.keys(blacklist).some(
key =>
error.request.url.includes(key) &&
(blacklist[key] === '*' || blacklist[key].includes(error.response.status)),
);
if (!ignoreError) {
// by default, set the error message to the message thrown by Gateway
let errorMessage = error.message;
// look on the response if we have a better error
if (error.response.parsed) {
const parsedResponse = error.response.parsed;
errorMessage =
parsedResponse.message ||
(typeof parsedResponse.error === 'string' && parsedResponse.error) ||
(parsedResponse.error && parsedResponse.error.code);
} else if (error.response.text) {
errorMessage = error.response.text;
}
client.captureMessage(errorMessage, {
extra: {
type: error.request.method,
url: error.request.url,
data: error.request.body,
status: error.response.status,
error: `${errorMessage}-${error.request.url}`,
response: error.response.text.substring(0, 255),
},
fingerprint: ['{{ default }}', error.request.url],
});
} else {
// eslint-disable-next-line no-console
console.warn('[Sentry] Ignored error: ', error);
}
}
}