Session Replay
Lessons Learned from Building
a DOM capturing product
Wey Wey Web 2023
Francesco Novy
Francesco Novy
hello@fnovy.com
mydea
- Living in Vienna, Austria
- 8+ years of building web UIs
- At Sentry since 2022
- Working on the JavaScript SDKs
- Sentry & Session Replay
- How does Session Replay work?
- Lessons Learned:
- Know your Use Case
- Expect the Unexpected
- How to Optimize Bundle Size
- How to Compress Data
Overview
- Error Monitoring
- Performance Monitoring
- Profiling
- Session Replay
What does Sentry do?
- Capture what's happening in the user's browser
- See what happened leading up to an error
- In-depth debugging similar to the Browser DevTools
Session Replay
Session Replay
Session Replay
import * as Sentry from '@sentry/browser';
Sentry.init({
integrations: [
new Sentry.Replay({
unmask: ['.show-this-class']
})
],
// Always capture when an error happens
replaysOnErrorSampleRate: 1.0,
// Capture 10% of sessions generally
replaysSessionSampleRate: 0.1,
});
How to use Session Replay?
- We use a fork of rrweb
- Mutation Observers
- Monkey Patching* Stylesheet APIs
- Monkey Patching* Canvas APIs
How does it work?
* What is Monkey Patching?
const originalFetch = window.fetch;
window.fetch = function() {
console.log("A fetch happened!");
return originalFetch.apply(window, arguments);
}
// Later somewhere in the application
window.fetch('https://example.com');
// Will show the console log
"Monkey patching is a technique used to dynamically update the behavior of a piece of code at run-time."
- Know your Use Case
- Expect the Unexpected
- How to Optimize Bundle Size
- How to Compress Data
Lessons Learned
Know your Use Case
Do we really need that?
Know your Use Case
- What do you really need?
- Hide text & user input by default
- Opt-in to show certain text
- Defaults matter: Make the best way the easy way
- Make the worst things impossible
Expect the Unexpected
Everything that can go wrong, will go wrong.
Expect the Unexpected
- Low Level APIs are dangerous to tinker with
- Global APIs may not be available
- Browser Extensions can do anything
- try-catch everything
How to Optimize Bundle Size
Ship as little code as necessary.
Session Replay Bundle Size
- Bundle Size v7.73.0: ~53 KB
- Bundle Size v7.78.0: ~35 KB
- Still large 😱
- ... but how?
Optimizing Bundle Size
- Remove unused code from rrweb
- Audit dependencies
- Make certain recording features opt-in
- Optimize for Tree Shaking!
What is Tree Shaking?
-
Tree Shaking describes the ability to automatically remove unused code from your build.
- When code is written in a tree-shakeable way, bundlers like Webpack can optimize your application based on what is actually used.
Tree Shaking: Simple Example
// SDK
import { large, small } from './my-code';
export function largeOrSmall(config) {
return config.useLarge ? large() : small();
}
// Application
import { largeOrSmall } from 'sdk';
largeOrSmall({ useLarge: false });
❌ Not tree shakeable
// SDK
export { large, small } from './my-code';
// Application
import { small } from 'sdk';
small();
✅ Tree shakeable
Tree Shaking: Simple Example
// SDK
import {
CanvasManager
} from './canvas-manager';
export function record(options) {
if (options.recordCanvas) {
new CanvasManager();
}
}
// Application
import { record } from 'sdk';
record({ recordCanvas: false });
❌ Not tree shakeable
Tree Shaking: Actual Example
// Application A
import {
record
} from 'sdk';
record({ getCanvasManager: undefined });
✅ Tree shakeable
Tree Shaking: Actual Example
// SDK
import {
CanvasManager
} from './canvas-manager';
export function getCanvasManager() {
return new CanvasManager();
}
export function record(options) {
if (options.getCanvasManager) {
options.getCanvasManager();
}
}
// Application B
import {
record,
getCanvasManager
} from 'sdk';
record({ getCanvasManager });
How to Compress Data
Avoid unnecessary network traffic, where possible.
Compressing Data
- Compress data in a web worker
- Gracefully handle errors
- Make sure to compare libraries (e.g. fflate)
Web Workers
Setting up the web worker
// worker.js
import { compressSync } from 'fflate';
function handleMessage(e) {
const { input, id } = e.data;
const compressed = compressSync(input);
// Send compressed data back to main thread
postMessage({ id, output: compressed });
}
// Receive uncompressed data from main thread
addEventListener('message', handleMessage);
Web Workers
Using the web worker from your application
// Application
const worker = new Worker('/worker.js');
function compressData(data) {
const id = generateUuid();
return new Promise(function (resolve) {
function listener(response) {
if (response.data.id === id) {
worker.removeEventListener('message', listener);
resolve(response.data.output);
}
}
worker.addEventListener('message', listener);
worker.postMessage({ id, input: data });
});
}
The Sentry SDK is Open Source!
- Everything we do is open source!
- Look at the code, PRs, etc.
- We love feedback!
Thank you!
hello@fnovy.com
mydea
Francesco Novy
Session Replay Wey Wey Web 2023
By Francesco Novy
Session Replay Wey Wey Web 2023
- 159