Merge branch 'sentry' into 'main'

Make Sentry integration actually useful

See merge request soapbox-pub/soapbox!2777
This commit is contained in:
Alex Gleason 2023-10-05 22:47:56 +00:00
commit 232f9547b5
7 changed files with 81 additions and 52 deletions

View File

@ -28,8 +28,9 @@ import {
useAppSelector,
useAppDispatch,
useOwnAccount,
useSoapboxConfig,
useSentry,
useSettings,
useSoapboxConfig,
useTheme,
useLocale,
} from 'soapbox/hooks';
@ -224,6 +225,8 @@ const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
'demetricator': settings.get('demetricator'),
});
useSentry(soapboxConfig.sentryDsn);
return (
<>
<Helmet>

View File

@ -54,6 +54,8 @@ const messages = defineMessages({
tileServerAttributionLabel: { id: 'soapbox_config.tile_server_attribution_label', defaultMessage: 'Map tiles attribution' },
redirectRootNoLoginLabel: { id: 'soapbox_config.redirect_root_no_login_label', defaultMessage: 'Redirect homepage' },
redirectRootNoLoginHint: { id: 'soapbox_config.redirect_root_no_login_hint', defaultMessage: 'Path to redirect the homepage when a user is not logged in.' },
sentryDsnLabel: { id: 'soapbox_config.sentry_dsn_label', defaultMessage: 'Sentry DSN' },
sentryDsnHint: { id: 'soapbox_config.sentry_dsn_hint', defaultMessage: 'DSN URL for error reporting. Works with Sentry and GlitchTip.' },
});
type ValueGetter<T = Element> = (e: React.ChangeEvent<T>) => any;
@ -285,6 +287,18 @@ const SoapboxConfig: React.FC = () => {
onChange={handleChange(['redirectRootNoLogin'], (e) => e.target.value)}
/>
</ListItem>
<ListItem
label={intl.formatMessage(messages.sentryDsnLabel)}
hint={intl.formatMessage(messages.sentryDsnHint)}
>
<Input
type='text'
placeholder='https://01234abcdef@glitch.tip.tld/5678'
value={String(data.get('sentryDsn', ''))}
onChange={handleChange(['sentryDsn'], (e) => e.target.value)}
/>
</ListItem>
</List>
<CardHeader>

View File

@ -19,7 +19,8 @@ export { useOwnAccount } from './useOwnAccount';
export { usePrevious } from './usePrevious';
export { useRefEventHandler } from './useRefEventHandler';
export { useRegistrationStatus } from './useRegistrationStatus';
export { useSentry } from './useSentry';
export { useSettings } from './useSettings';
export { useSoapboxConfig } from './useSoapboxConfig';
export { useSystemTheme } from './useSystemTheme';
export { useTheme } from './useTheme';
export { useTheme } from './useTheme';

50
src/hooks/useSentry.ts Normal file
View File

@ -0,0 +1,50 @@
import { useEffect } from 'react';
/** Hook to start Sentry. Should only be called once. */
function useSentry(dsn: string | undefined) {
useEffect(() => {
if (dsn) {
startSentry(dsn).catch(console.error);
}
}, [dsn]);
}
/** Start Sentry. */
async function startSentry(dsn: string): Promise<void> {
const [Sentry, { Integrations: Integrations }] = await Promise.all([
import('@sentry/react'),
import('@sentry/tracing'),
]);
Sentry.init({
dsn,
debug: false,
integrations: [new Integrations.BrowserTracing()],
// Filter events.
// https://docs.sentry.io/platforms/javascript/configuration/filtering/
ignoreErrors: [
// Network errors.
'AxiosError',
// sw.js couldn't be downloaded.
'Failed to update a ServiceWorker for scope',
// Useful for try/catch, useless as a Sentry error.
'AbortError',
// localForage error in FireFox private browsing mode (which doesn't support IndexedDB).
// We only use IndexedDB as a cache, so we can safely ignore the error.
'No available storage method found',
],
denyUrls: [
// Browser extensions.
/extensions\//i,
/^chrome:\/\//i,
/^moz-extension:\/\//i,
],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});
}
export { useSentry };

View File

@ -23,13 +23,9 @@ import './styles/tailwind.css';
import './precheck';
import { default as Soapbox } from './containers/soapbox';
import * as monitoring from './monitoring';
import ready from './ready';
import { registerSW } from './utils/sw';
// Sentry
monitoring.start();
if (BuildConfig.NODE_ENV === 'production') {
printConsoleWarning();
registerSW('/sw.js');

View File

@ -1,49 +1,13 @@
import * as BuildConfig from 'soapbox/build-config';
import type { CaptureContext } from '@sentry/types';
export const start = (): void => {
Promise.all([
import('@sentry/react'),
import('@sentry/tracing'),
]).then(([Sentry, { Integrations: Integrations }]) => {
Sentry.init({
dsn: BuildConfig.SENTRY_DSN,
environment: BuildConfig.NODE_ENV,
debug: false,
integrations: [new Integrations.BrowserTracing()],
/** Capture the exception and report it to Sentry. */
async function captureException (exception: any, captureContext?: CaptureContext | undefined): Promise<void> {
try {
const Sentry = await import('@sentry/react');
Sentry.captureException(exception, captureContext);
} catch (e) {
console.error(e);
}
}
// Filter events.
// https://docs.sentry.io/platforms/javascript/configuration/filtering/
ignoreErrors: [
// Network errors.
'AxiosError',
// sw.js couldn't be downloaded.
'Failed to update a ServiceWorker for scope',
// Useful for try/catch, useless as a Sentry error.
'AbortError',
// localForage error in FireFox private browsing mode (which doesn't support IndexedDB).
// We only use IndexedDB as a cache, so we can safely ignore the error.
'No available storage method found',
],
denyUrls: [
// Browser extensions.
/extensions\//i,
/^chrome:\/\//i,
/^moz-extension:\/\//i,
],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});
}).catch(console.error);
};
export const captureException = (exception: any, captureContext?: CaptureContext | undefined): void => {
import('@sentry/react')
.then(Sentry => {
Sentry.captureException(exception, captureContext);
})
.catch(console.error);
};
export { captureException };

View File

@ -116,6 +116,7 @@ export const SoapboxConfigRecord = ImmutableRecord({
* On some platforms this can be too blurry without additional configuration.
*/
mediaPreview: false,
sentryDsn: undefined as string | undefined,
}, 'SoapboxConfig');
type SoapboxConfigMap = ImmutableMap<string, any>;