Skip to content

Commit

Permalink
Sentry 7 update, profiling, and session replays (#28)
Browse files Browse the repository at this point in the history
* Update Sentry to v7; generate tracing & non-tracing bundles

* Create custom Sentry client to tree-shake properly (reduces dist by ~200K)

* Add support for Session Replay

* Add support for Sentry PHP performance profiling

* Show whether Excimer is enabled in admin

* Only report email in PHP if enabled
  • Loading branch information
dsevillamartin authored Jun 25, 2023
1 parent 828b02b commit 0fe0703
Show file tree
Hide file tree
Showing 10 changed files with 1,714 additions and 4,168 deletions.
6 changes: 5 additions & 1 deletion extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace FoF\Sentry;

use Flarum\Extend as Flarum;
use Flarum\Frontend\Document;
use Flarum\Frontend\RecompileFrontendAssets;
use Flarum\Locale\LocaleManager;
use Flarum\Settings\Event\Saved;
Expand All @@ -28,7 +29,10 @@
->content(Content\SentryJavaScript::class),

(new Flarum\Frontend('admin'))
->js(__DIR__.'/js/dist/admin.js'),
->js(__DIR__.'/js/dist/admin.js')
->content(function (Document $document) {
$document->payload['hasExcimer'] = extension_loaded('excimer');
}),

new Flarum\Locales(__DIR__.'/resources/locale'),

Expand Down
5,521 changes: 1,445 additions & 4,076 deletions js/package-lock.json

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
"private": true,
"prettier": "@flarum/prettier-config",
"dependencies": {
"@sentry/browser": "^6.7.2",
"@sentry/integrations": "^6.7.2",
"@sentry/tracing": "^6.7.2",
"flarum-webpack-config": "^2.0.0",
"@flarum/prettier-config": "^1.0.0",
"@sentry/browser": "^7.56.0",
"@sentry/integrations": "^7.56.0",
"@sentry/tracing": "^7.56.0",
"flarum-tsconfig": "^1.0.2",
"flarum-webpack-config": "^2.0.0",
"webpack": "^5.76.0",
"webpack-cli": "^4.9.2"
"webpack-cli": "^5.1.4"
},
"scripts": {
"dev": "webpack --mode development --watch",
Expand All @@ -19,6 +20,8 @@
"format-check": "prettier --check src"
},
"devDependencies": {
"prettier": "^2.6.2"
"prettier": "^2.6.2",
"webpack-bundle-analyzer": "^4.9.0",
"webpack-merge": "^5.9.0"
}
}
39 changes: 36 additions & 3 deletions js/src/admin/index.ts → js/src/admin/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import app from 'flarum/admin/app';

app.initializers.add('fof/sentry', () => {
const hasExcimer = app.data['hasExcimer'];

app.extensionData
.for('fof-sentry')
.registerSetting({
Expand All @@ -18,13 +20,32 @@ app.initializers.add('fof/sentry', () => {
setting: 'fof-sentry.user_feedback',
type: 'boolean',
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.send_user_emails_label'),
setting: 'fof-sentry.send_emails_with_sentry_reports',
type: 'boolean',
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.monitor_performance_label'),
setting: 'fof-sentry.monitor_performance',
type: 'number',
min: 0,
max: 100,
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.profile_rate_label'),
help: app.translator.trans('fof-sentry.admin.settings.profile_rate_help', {
br: <br />,
bold: hasExcimer ? null : <b />,
icon: hasExcimer ? '✔' : '✖',
a: <a href="https://docs.sentry.io/platforms/php/profiling/#improve-response-time" target="_blank" />,
}),
setting: 'fof-sentry.profile_rate',
type: 'number',
min: 0,
max: 100,
disabled: !hasExcimer, // requires PHP extension
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.javascript_label'),
setting: 'fof-sentry.javascript',
Expand All @@ -37,14 +58,26 @@ app.initializers.add('fof/sentry', () => {
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.javascript_trace_sample_rate'),
help: app.translator.trans('fof-sentry.admin.settings.javascript_trace_sample_rate_help', { br: <br /> }),
setting: 'fof-sentry.javascript.trace_sample_rate',
type: 'number',
min: 0,
max: 100,
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.send_user_emails_label'),
setting: 'fof-sentry.send_emails_with_sentry_reports',
type: 'boolean',
label: app.translator.trans('fof-sentry.admin.settings.javascript_replays_session_sample_rate'),
help: app.translator.trans('fof-sentry.admin.settings.javascript_replays_session_sample_rate_help', { br: <br /> }),
setting: 'fof-sentry.javascript.replays_session_sample_rate',
type: 'number',
min: 0,
max: 100,
})
.registerSetting({
label: app.translator.trans('fof-sentry.admin.settings.javascript_replays_error_sample_rate'),
help: app.translator.trans('fof-sentry.admin.settings.javascript_replays_error_sample_rate_help', { br: <br /> }),
setting: 'fof-sentry.javascript.replays_error_sample_rate',
type: 'number',
min: 0,
max: 100,
});
});
100 changes: 90 additions & 10 deletions js/src/forum/index.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,103 @@
import app from 'flarum/forum/app';
import * as Sentry from '@sentry/browser';

import {
BrowserClient,
defaultStackParser,
getCurrentHub,
makeFetchTransport,
showReportDialog,
Breadcrumbs,
GlobalHandlers,
InboundFilters,
FunctionToString,
LinkedErrors,
HttpContext,
TryCatch,
BrowserTracing,
Replay,
} from '@sentry/browser';

import { CaptureConsole } from '@sentry/integrations';
import { Integrations as TracingIntegrations } from '@sentry/tracing';

window.Sentry = Sentry;
window.Sentry.Integrations.CaptureConsole = CaptureConsole;
window.Sentry.TracingIntegrations = TracingIntegrations;
const integrations = [
new InboundFilters(),
new FunctionToString(),
new TryCatch(),
new GlobalHandlers({
onerror: true,
onunhandledrejection: true,
}),
new Breadcrumbs({
console: true,
dom: true,
fetch: true,
history: true,
sentry: true,
xhr: true,
}),
new LinkedErrors({
key: 'cause',
limit: 5,
}),
new HttpContext(),
];

if (__SENTRY_TRACING__) {
integrations.push(new BrowserTracing());
}

if (__SENTRY_SESSION_REPLAY__) {
integrations.push(new Replay());
}

const createClient = (config) =>
new BrowserClient({
dsn: config.dsn,

transport: makeFetchTransport,
stackParser: defaultStackParser,

beforeSend: (event) => {
event.logger = 'javascript';

if (config.scrubEmails && event.user?.email) {
delete event.user.email;
}

// All Sentry initialisation happens in `src/Content/SentryJavaScript.php`
if (config.showFeedback && event.exception) {
showReportDialog({ eventId: event.event_id, user: Sentry.getUserData('name') });
}

return event;
},

tracesSampleRate: config.tracesSampleRate,
replaysSessionSampleRate: config.replaysSessionSampleRate,
replaysOnErrorSampleRate: config.replaysOnErrorSampleRate,

integrations: [...integrations, config.captureConsole && new CaptureConsole()].filter(Boolean),
});

window.Sentry = { createClient, getCurrentHub, showReportDialog };

window.Sentry.getUserData = (nameAttr = 'username') => {
/** @type {Sentry.User} */
let userData = {};

// Depending on when the error occurs, `app` might not be defined
if (app) {
if (app.session && app.session.user && app.session.user.id() != 0) {
const user = app.session?.user;

if (app.session && user && user.id() != 0) {
userData = {
ip_address: '{{auto}}',
id: app.session.user.id(),
email: app.session.user.email(),
[nameAttr]: app.session.user.username(),
id: user.id(),
[nameAttr]: user.username(),
};

if (app.data['fof-sentry.scrub-emails']) {
userData[email] = user.email();
}
} else if (app.data.session && app.data.session.userId != 0) {
userData = {
id: app.data.session.userId,
Expand All @@ -31,3 +107,7 @@ window.Sentry.getUserData = (nameAttr = 'username') => {

return userData;
};

app.initializers.add('fof/sentry', () => {
getCurrentHub().setUser(Sentry.getUserData());
});
65 changes: 64 additions & 1 deletion js/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1 +1,64 @@
module.exports = require('flarum-webpack-config')();
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

const config = merge(
require('flarum-webpack-config')(),
{
plugins: [
new webpack.DefinePlugin({
__SENTRY_DEBUG__: false,
}),
],
}
);

const buildDist = (filename, env, define = {}, buildAdmin = false) => merge(
config,
{
entry: () => {
const entries = {
forum: config.entry.forum,
};

// No need to build admin JS multiple times
if (buildAdmin) {
entries.admin = config.entry.admin;
}

return entries;
},
output: {
filename,
},
plugins: [
new webpack.DefinePlugin({
__SENTRY_SESSION_REPLAY__: false,
__SENTRY_TRACING__: false,
...define,
}),
env.analyze && new BundleAnalyzerPlugin({
analyzerPort: 'auto',
}),
].filter(Boolean),
}
);

module.exports = env => {
const plain = buildDist('[name].js', env, {}, true);

const tracing = buildDist('[name].tracing.js', env, {
__SENTRY_TRACING__: true,
});

const replay = buildDist('[name].replay.js', env, {
__SENTRY_SESSION_REPLAY__: true,
});

const tracingAndReplay = buildDist('[name].tracing.replay.js', env, {
__SENTRY_TRACING__: true,
__SENTRY_SESSION_REPLAY__: true,
});

return [plain, tracing, replay, tracingAndReplay];
};
17 changes: 15 additions & 2 deletions resources/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@ fof-sentry:
dsn_label: Sentry DSN
environment_label: Sentry Environment
user_feedback_label: User Feedback
send_user_emails_label: Send user email addresses with Sentry reports?

monitor_performance_label: Back-end Performance Monitoring Rate (0 - 100)
profile_rate_label: Back-end Profiling Rate (0 - 100)
profile_rate_help: |
This is relative to the performance monitoring rate. Performance monitoring must be enabled for this to work.
{br}✱ Enabling this feature affects response time due to the overhead of profiling and requests being sent to Sentry. Learn how to offset this @ <a>https://docs.sentry.io/platforms/php/profiling/#improve-response-time</a>.
{br}{icon} <bold>This feature requires Wikipedia's sampling profiler Excimer, which is only available for Linux.</bold>
javascript_label: Report JavaScript Errors
javascript_console_label: Capture JavaScript Console
javascript_trace_sample_rate: Front-end Performance Monitoring Rate (0 - 100)
monitor_performance_label: Back-end Performance Monitoring Rate (0 - 100)
send_user_emails_label: Send user email addresses with Sentry reports?
javascript_trace_sample_rate_help: The sample rate for all transactions. {br}✱ Enabling this feature increases bundle size by ~30 KB.
javascript_replays_session_sample_rate: Front-end Session Replays Rate (0 - 100)
javascript_replays_session_sample_rate_help: The sample rate for replays that begin recording immediately and last the entirety of the user's session. {br}✱ Having any replay option enabled increases bundle size by ~150 KB.
javascript_replays_error_sample_rate: Front-end Error Replays Rate (0 - 100)
javascript_replays_error_sample_rate_help: The sample rate for replays that are recorded when an error happens. Up to 1 minute before the error will be recorded and continue until the session ends. {br}✱ Having any replay option enabled increases bundle size by ~150 KB.
Loading

0 comments on commit 0fe0703

Please sign in to comment.