mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 03:42:27 +03:00
98c06f8126
no issue - filtering was previously added to breadcrumbs but that wasn't enough to clean up Sentry reports - added filtering to the `beforeSend` hook too so reports don't get cluttered with unhelpful XHR noise
151 lines
5.9 KiB
JavaScript
151 lines
5.9 KiB
JavaScript
import {Debug} from '@sentry/integrations';
|
|
import {Replay} from '@sentry/replay';
|
|
import {isAjaxError} from 'ember-ajax/errors';
|
|
|
|
const FILTERED_URL_REGEX = /\/e\.ghost\.org|plausible\.io/;
|
|
|
|
export function getSentryConfig(dsn, environment, appVersion, transport) {
|
|
const extraIntegrations = [];
|
|
|
|
const config = {
|
|
dsn,
|
|
transport,
|
|
environment,
|
|
release: `ghost@${appVersion}`,
|
|
beforeSend,
|
|
ignoreErrors: [
|
|
// Browser autoplay policies (this regex covers a few)
|
|
/The play\(\) request was interrupted.*/,
|
|
/The request is not allowed by the user agent or the platform in the current context/,
|
|
|
|
// Network errors that we don't control
|
|
/Server was unreachable/,
|
|
/NetworkError when attempting to fetch resource./,
|
|
/Failed to fetch/,
|
|
/Load failed/,
|
|
/The operation was aborted./,
|
|
|
|
// TransitionAborted errors surface from normal application behaviour
|
|
// - https://github.com/emberjs/ember.js/issues/12505
|
|
/^TransitionAborted$/,
|
|
// ResizeObserver loop errors occur often from extensions and
|
|
// embedded content, generally harmless and not useful to report
|
|
/^ResizeObserver loop completed with undelivered notifications/,
|
|
/^ResizeObserver loop limit exceeded/,
|
|
// When tasks in ember-concurrency are canceled, they sometimes lead to unhandled Promise rejections
|
|
// This doesn't affect the application and is not useful to report
|
|
// - http://ember-concurrency.com/docs/cancelation
|
|
'TaskCancelation'
|
|
],
|
|
integrations: function (integrations) {
|
|
// integrations will be all default integrations
|
|
const defaultIntegrations = integrations.filter((integration) => {
|
|
// Don't dedupe events when testing
|
|
if (environment === 'testing' && integration.name === 'Dedupe') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return [...defaultIntegrations, ...extraIntegrations];
|
|
},
|
|
beforeBreadcrumb(breadcrumb) {
|
|
// ignore breadcrumbs for event tracking to reduce noise in error reports
|
|
if (breadcrumb.category === 'http' && breadcrumb.data?.url?.match(FILTERED_URL_REGEX)) {
|
|
return null;
|
|
}
|
|
return breadcrumb;
|
|
}
|
|
};
|
|
|
|
if (environment !== 'testing') {
|
|
try {
|
|
// Session Replay on errors
|
|
// Docs: https://docs.sentry.io/platforms/javascript/session-replay
|
|
config.replaysOnErrorSampleRate = 0.5;
|
|
extraIntegrations.push(
|
|
// Replace with `Sentry.replayIntegration()` once we've migrated to @sentry/ember 8.x
|
|
// Docs: https://docs.sentry.io/platforms/javascript/migration/v7-to-v8/#removal-of-sentryreplay-package
|
|
new Replay({
|
|
mask: ['.koenig-lexical', '.gh-dashboard'],
|
|
unmask: ['[role="menu"]', '[data-testid="settings-panel"]', '.gh-nav'],
|
|
maskAllText: false,
|
|
maskAllInputs: true,
|
|
blockAllMedia: true
|
|
})
|
|
);
|
|
} catch (e) {
|
|
// no-op, Session Replay is not critical
|
|
console.error('Error enabling Sentry Replay:', e); // eslint-disable-line no-console
|
|
}
|
|
}
|
|
|
|
if (environment === 'development') {
|
|
extraIntegrations.push(new Debug());
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
export function getSentryTestConfig(transport) {
|
|
return getSentryConfig(
|
|
'https://abcdef0123456789abcdef0123456789@o12345.ingest.sentry.io/1234567',
|
|
'testing',
|
|
'5.0.0',
|
|
transport
|
|
);
|
|
}
|
|
|
|
export function beforeSend(event, hint) {
|
|
try {
|
|
const originalException = hint?.originalException;
|
|
event.contexts = event.contexts || {};
|
|
event.tags = event.tags || {};
|
|
event.tags.shown_to_user = event.tags.shown_to_user || false;
|
|
event.tags.grammarly = !!document.querySelector('[data-gr-ext-installed]');
|
|
|
|
// Do not report "handled" errors to Sentry
|
|
if (event.tags.shown_to_user === true) {
|
|
return null;
|
|
}
|
|
|
|
// Do not report requests to our event tracking endpoints to reduce noise
|
|
if (event.request?.url?.match(FILTERED_URL_REGEX)) {
|
|
return null;
|
|
}
|
|
|
|
// if the error value includes a model id then overwrite it to improve grouping
|
|
if (event.exception && event.exception.values && event.exception.values.length > 0) {
|
|
const pattern = /<(post|page):[a-f0-9]+>/;
|
|
const replacement = '<$1:ID>';
|
|
event.exception.values[0].value = event.exception.values[0].value.replace(pattern, replacement);
|
|
}
|
|
|
|
// ajax errors — improve logging and add context for debugging
|
|
if (isAjaxError(originalException) && originalException.payload && originalException.payload.errors && originalException.payload.errors.length > 0) {
|
|
const error = originalException.payload.errors[0];
|
|
event.exception.values[0].type = `${error.type}: ${error.context}`;
|
|
event.exception.values[0].value = error.message;
|
|
event.exception.values[0].context = error.context;
|
|
} else {
|
|
delete event.contexts.ajax;
|
|
delete event.tags.ajax_status;
|
|
delete event.tags.ajax_method;
|
|
delete event.tags.ajax_url;
|
|
}
|
|
|
|
// Do not report posthog-js errors to Sentry
|
|
if (originalException?.stack?.includes('/posthog-js/')) {
|
|
return null;
|
|
}
|
|
|
|
return event;
|
|
} catch (error) {
|
|
console.error('Error in beforeSend:', error); // eslint-disable-line no-console
|
|
// If any errors occur in beforeSend, send the original event to Sentry
|
|
// Better to have some information than no information
|
|
return event;
|
|
}
|
|
}
|