-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
ref(replay): Extract some functions out from class #6448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
737adb2
a558935
db1ab04
edd132a
391a22c
229bd51
721e682
d7a3e3a
e026241
3791c69
37efedb
efc9525
432da15
f7caf64
cf54a67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { Event } from '@sentry/types'; | ||
|
||
import { REPLAY_EVENT_NAME, UNABLE_TO_SEND_REPLAY } from '../constants'; | ||
import type { ReplayContainer } from '../types'; | ||
import { addInternalBreadcrumb } from '../util/addInternalBreadcrumb'; | ||
|
||
/** | ||
* Returns a listener to be added to `addGlobalEventProcessor(listener)`. | ||
*/ | ||
export function handleGlobalEventListener(replay: ReplayContainer): (event: Event) => Event { | ||
return (event: Event) => { | ||
// Do not apply replayId to the root event | ||
if ( | ||
// @ts-ignore new event type | ||
event.type === REPLAY_EVENT_NAME | ||
) { | ||
// Replays have separate set of breadcrumbs, do not include breadcrumbs | ||
// from core SDK | ||
delete event.breadcrumbs; | ||
return event; | ||
} | ||
|
||
// Only tag transactions with replayId if not waiting for an error | ||
// @ts-ignore private | ||
if (event.type !== 'transaction' || !replay._waitForError) { | ||
event.tags = { ...event.tags, replayId: replay.session?.id }; | ||
} | ||
|
||
// Collect traceIds in _context regardless of `_waitForError` - if it's true, | ||
// _context gets cleared on every checkout | ||
if (event.type === 'transaction' && event.contexts && event.contexts.trace && event.contexts.trace.trace_id) { | ||
replay.getContext().traceIds.add(event.contexts.trace.trace_id as string); | ||
return event; | ||
} | ||
|
||
// no event type means error | ||
if (!event.type) { | ||
replay.getContext().errorIds.add(event.event_id as string); | ||
Comment on lines
+32
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be good to add methods to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I would like to keep refining the public API surface of this over time. there are a few more places where IMHO we can/should tighten this up! I'll leave this as is for now, but note down to revisit this! |
||
} | ||
|
||
const exc = event.exception?.values?.[0]; | ||
addInternalBreadcrumb({ | ||
message: `Tagging event (${event.event_id}) - ${event.message} - ${exc?.type || 'Unknown'}: ${ | ||
exc?.value || 'n/a' | ||
}`, | ||
}); | ||
|
||
// Need to be very careful that this does not cause an infinite loop | ||
if ( | ||
// @ts-ignore private | ||
replay._waitForError && | ||
event.exception && | ||
event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing | ||
) { | ||
setTimeout(async () => { | ||
// Allow flush to complete before resuming as a session recording, otherwise | ||
// the checkout from `startRecording` may be included in the payload. | ||
// Prefer to keep the error replay as a separate (and smaller) segment | ||
// than the session replay. | ||
await replay.flushImmediate(); | ||
|
||
if (replay.stopRecording()) { | ||
// Reset all "capture on error" configuration before | ||
// starting a new recording | ||
// @ts-ignore private | ||
replay._waitForError = false; | ||
replay.startRecording(); | ||
} | ||
}); | ||
} | ||
|
||
return event; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
import { ReplayPerformanceEntry } from '../createPerformanceEntry'; | ||
import type { ReplayContainer } from '../types'; | ||
import { createPerformanceSpans } from '../util/createPerformanceSpans'; | ||
|
||
export interface HistoryHandlerData { | ||
interface HistoryHandlerData { | ||
from: string; | ||
to: string; | ||
} | ||
|
||
export function handleHistory(handlerData: HistoryHandlerData): ReplayPerformanceEntry { | ||
function handleHistory(handlerData: HistoryHandlerData): ReplayPerformanceEntry { | ||
const { from, to } = handlerData; | ||
|
||
const now = new Date().getTime() / 1000; | ||
|
@@ -20,3 +22,30 @@ export function handleHistory(handlerData: HistoryHandlerData): ReplayPerformanc | |
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Returns a listener to be added to `addInstrumentationHandler('history', listener)`. | ||
*/ | ||
export function handleHistorySpanListener(replay: ReplayContainer): (handlerData: HistoryHandlerData) => void { | ||
return (handlerData: HistoryHandlerData) => { | ||
if (!replay.isEnabled()) { | ||
return; | ||
} | ||
|
||
const result = handleHistory(handlerData); | ||
|
||
if (result === null) { | ||
return; | ||
} | ||
|
||
// Need to collect visited URLs | ||
replay.getContext().urls.push(result.name); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar thing to above with trace/error ids. |
||
replay.triggerUserActivity(); | ||
|
||
replay.addUpdate(() => { | ||
createPerformanceSpans(replay, [result]); | ||
// Returning false to flush | ||
return false; | ||
}); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import type { AllPerformanceEntry, ReplayContainer } from '../types'; | ||
import { dedupePerformanceEntries } from '../util/dedupePerformanceEntries'; | ||
|
||
/** | ||
* Sets up a PerformanceObserver to listen to all performance entry types. | ||
*/ | ||
export function setupPerformanceObserver(replay: ReplayContainer): PerformanceObserver { | ||
const performanceObserverHandler = (list: PerformanceObserverEntryList): void => { | ||
// For whatever reason the observer was returning duplicate navigation | ||
// entries (the other entry types were not duplicated). | ||
const newPerformanceEntries = dedupePerformanceEntries( | ||
replay.performanceEvents, | ||
list.getEntries() as AllPerformanceEntry[], | ||
); | ||
replay.performanceEvents = newPerformanceEntries; | ||
}; | ||
|
||
const performanceObserver = new PerformanceObserver(performanceObserverHandler); | ||
|
||
[ | ||
'element', | ||
'event', | ||
'first-input', | ||
'largest-contentful-paint', | ||
'layout-shift', | ||
'longtask', | ||
'navigation', | ||
'paint', | ||
'resource', | ||
].forEach(type => { | ||
try { | ||
performanceObserver.observe({ | ||
type, | ||
buffered: true, | ||
}); | ||
} catch { | ||
// This can throw if an entry type is not supported in the browser. | ||
// Ignore these errors. | ||
} | ||
}); | ||
|
||
return performanceObserver; | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m: can we create a issue or add a todo somewhere that adds this event type to the TS types?
Also @JonasBa do we need to do the same for profiling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is planned as next step - need to write up an issue!