diff --git a/frontend/__snapshots__/components-command-bar--actions.png b/frontend/__snapshots__/components-command-bar--actions.png index 585c01020c1c5..302fb86f50402 100644 Binary files a/frontend/__snapshots__/components-command-bar--actions.png and b/frontend/__snapshots__/components-command-bar--actions.png differ diff --git a/frontend/__snapshots__/components-networkrequesttiming--basic.png b/frontend/__snapshots__/components-networkrequesttiming--basic.png index 8247b433f71c8..effc91c21a0a6 100644 Binary files a/frontend/__snapshots__/components-networkrequesttiming--basic.png and b/frontend/__snapshots__/components-networkrequesttiming--basic.png differ diff --git a/frontend/src/loadPostHogJS.tsx b/frontend/src/loadPostHogJS.tsx index 2acd266241f82..bcd03f327260a 100644 --- a/frontend/src/loadPostHogJS.tsx +++ b/frontend/src/loadPostHogJS.tsx @@ -27,8 +27,8 @@ export function loadPostHogJS(): void { bootstrap: window.POSTHOG_USER_IDENTITY_WITH_FLAGS ? window.POSTHOG_USER_IDENTITY_WITH_FLAGS : {}, opt_in_site_apps: true, loaded: (posthog) => { - if (posthog.webPerformance) { - posthog.webPerformance._forceAllowLocalhost = true + if (posthog.sessionRecording) { + posthog.sessionRecording._forceAllowLocalhostNetworkCapture = true } if (window.IMPERSONATED_SESSION) { diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx index 96c4a9e01a143..592d926958224 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx @@ -140,7 +140,7 @@ export function ItemPerformanceEvent({ expanded, setExpanded, }: ItemPerformanceEvent): JSX.Element { - const [activeTab, setActiveTab] = useState<'timings' | 'headers' | 'payload' | 'response_body'>('timings') + const [activeTab, setActiveTab] = useState<'timings' | 'headers' | 'payload' | 'response_body' | 'raw'>('timings') const bytes = humanizeBytes(item.encoded_body_size || item.decoded_body_size || 0) const startTime = item.start_time || item.fetch_start || 0 @@ -176,7 +176,11 @@ export function ItemPerformanceEvent({ return acc } - if (['response_headers', 'request_headers', 'request_body', 'response_body', 'response_status'].includes(key)) { + if ( + ['response_headers', 'request_headers', 'request_body', 'response_body', 'response_status', 'raw'].includes( + key + ) + ) { return acc } @@ -392,6 +396,17 @@ export function ItemPerformanceEvent({ ), } : false, + // raw is only available if the feature flag is enabled + // TODO before proper release we should put raw behind its own flag + { + key: 'raw', + label: 'Json', + content: ( + + {JSON.stringify(item.raw, null, 2)} + + ), + }, ]} /> @@ -470,6 +485,11 @@ function StatusRow({ item }: { item: PerformanceEvent }): JSX.Element | null { let statusRow = null let methodRow = null + let fromDiskCache = false + if (item.transfer_size === 0 && item.response_body && item.response_status && item.response_status < 400) { + fromDiskCache = true + } + if (item.response_status) { const statusDescription = `${item.response_status} ${friendlyHttpStatus[item.response_status] || ''}` @@ -483,7 +503,10 @@ function StatusRow({ item }: { item: PerformanceEvent }): JSX.Element | null { statusRow = (
Status code
- {statusDescription} +
+ {statusDescription} + {fromDiskCache && (from cache)} +
) } diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx index a02e9bf3dce03..815eefed8bdfb 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx @@ -19,27 +19,30 @@ export function Basic(): JSX.Element { + /** * There are defined sections to performance measurement. We may have data for some or all of them * @@ -109,85 +110,114 @@ function colorForSection(section: (typeof perfSections)[number]): string { * * see https://nicj.net/resourcetiming-in-practice/ */ -function calculatePerformanceParts(perfEntry: PerformanceEvent): Record { +export function calculatePerformanceParts(perfEntry: PerformanceEvent): PerformanceMeasures { const performanceParts: Record = {} - if (perfEntry.redirect_start && perfEntry.redirect_end) { - performanceParts['redirect'] = { - start: perfEntry.redirect_start, - end: perfEntry.redirect_end, - color: colorForSection('redirect'), + if (isPresent(perfEntry.redirect_start) && isPresent(perfEntry.redirect_end)) { + if (perfEntry.redirect_end - perfEntry.redirect_start > 0) { + performanceParts['redirect'] = { + start: perfEntry.redirect_start, + end: perfEntry.redirect_end, + color: colorForSection('redirect'), + } } } - if (perfEntry.fetch_start && perfEntry.domain_lookup_start) { - performanceParts['app cache'] = { - start: perfEntry.fetch_start, - end: perfEntry.domain_lookup_start, - color: colorForSection('app cache'), + if (isPresent(perfEntry.fetch_start) && isPresent(perfEntry.domain_lookup_start)) { + if (perfEntry.domain_lookup_start - perfEntry.fetch_start > 0) { + performanceParts['app cache'] = { + start: perfEntry.fetch_start, + end: perfEntry.domain_lookup_start, + color: colorForSection('app cache'), + } } } - if (perfEntry.domain_lookup_end && perfEntry.domain_lookup_start) { - performanceParts['dns lookup'] = { - start: perfEntry.domain_lookup_start, - end: perfEntry.domain_lookup_end, - color: colorForSection('dns lookup'), + if (isPresent(perfEntry.domain_lookup_end) && isPresent(perfEntry.domain_lookup_start)) { + if (perfEntry.domain_lookup_end - perfEntry.domain_lookup_start > 0) { + performanceParts['dns lookup'] = { + start: perfEntry.domain_lookup_start, + end: perfEntry.domain_lookup_end, + color: colorForSection('dns lookup'), + } } } - if (perfEntry.connect_end && perfEntry.connect_start) { - performanceParts['connection time'] = { - start: perfEntry.connect_start, - end: perfEntry.connect_end, - color: colorForSection('connection time'), - } - - if (perfEntry.secure_connection_start) { - performanceParts['tls time'] = { - start: perfEntry.secure_connection_start, + if (isPresent(perfEntry.connect_end) && isPresent(perfEntry.connect_start)) { + if (perfEntry.connect_end - perfEntry.connect_start > 0) { + performanceParts['connection time'] = { + start: perfEntry.connect_start, end: perfEntry.connect_end, - color: colorForSection('tls time'), - reducedHeight: true, + color: colorForSection('connection time'), + } + + if (isPresent(perfEntry.secure_connection_start) && perfEntry.secure_connection_start > 0) { + performanceParts['tls time'] = { + start: perfEntry.secure_connection_start, + end: perfEntry.connect_end, + color: colorForSection('tls time'), + reducedHeight: true, + } } } } - if (perfEntry.connect_end && perfEntry.request_start && perfEntry.connect_end !== perfEntry.request_start) { - performanceParts['request queuing time'] = { - start: perfEntry.connect_end, - end: perfEntry.request_start, - color: colorForSection('request queuing time'), + if ( + isPresent(perfEntry.connect_end) && + isPresent(perfEntry.request_start) && + perfEntry.connect_end !== perfEntry.request_start + ) { + if (perfEntry.request_start - perfEntry.connect_end > 0) { + performanceParts['request queuing time'] = { + start: perfEntry.connect_end, + end: perfEntry.request_start, + color: colorForSection('request queuing time'), + } } } - if (perfEntry.response_start && perfEntry.request_start) { - performanceParts['waiting for first byte'] = { - start: perfEntry.request_start, - end: perfEntry.response_start, - color: colorForSection('waiting for first byte'), + if (isPresent(perfEntry.response_start) && isPresent(perfEntry.request_start)) { + if (perfEntry.response_start - perfEntry.request_start > 0) { + performanceParts['waiting for first byte'] = { + start: perfEntry.request_start, + end: perfEntry.response_start, + color: colorForSection('waiting for first byte'), + } } } - if (perfEntry.response_start && perfEntry.response_end) { - performanceParts['receiving response'] = { - start: perfEntry.response_start, - end: perfEntry.response_end, - color: colorForSection('receiving response'), + if (isPresent(perfEntry.response_start) && isPresent(perfEntry.response_end)) { + if (perfEntry.response_end - perfEntry.response_start > 0) { + // if loading from disk cache then response_start is 0 but fetch_start is not + let start = perfEntry.response_start + if (perfEntry.response_start === 0 && isPresent(perfEntry.fetch_start)) { + start = perfEntry.fetch_start + } + performanceParts['receiving response'] = { + start: start, + end: perfEntry.response_end, + color: colorForSection('receiving response'), + } } } - if (perfEntry.response_end && perfEntry.load_event_end) { - performanceParts['document processing'] = { - start: perfEntry.response_end, - end: perfEntry.load_event_end, - color: colorForSection('document processing'), + if (isPresent(perfEntry.response_end) && isPresent(perfEntry.load_event_end)) { + if (perfEntry.load_event_end - perfEntry.response_end > 0) { + performanceParts['document processing'] = { + start: perfEntry.response_end, + end: perfEntry.load_event_end, + color: colorForSection('document processing'), + } } } return performanceParts } +function percentage(partDuration: number, totalDuration: number, min: number): number { + return Math.min(Math.max(min, (partDuration / totalDuration) * 100), 100) +} + function percentagesWithinEventRange({ partStart, partEnd, @@ -203,20 +233,20 @@ function percentagesWithinEventRange({ const partStartRelativeToTimeline = partStart - rangeStart const partDuration = partEnd - partStart - const partPercentage = Math.max(0.1, (partDuration / totalDuration) * 100) //less than 0.1% is not visible - const partStartPercentage = (partStartRelativeToTimeline / totalDuration) * 100 + const partPercentage = percentage(partDuration, totalDuration, 0.1) + const partStartPercentage = percentage(partStartRelativeToTimeline, totalDuration, 0) return { startPercentage: `${partStartPercentage}%`, widthPercentage: `${partPercentage}%` } } -const TimeLineView = ({ performanceEvent }: { performanceEvent: PerformanceEvent }): JSX.Element => { +const TimeLineView = ({ performanceEvent }: { performanceEvent: PerformanceEvent }): JSX.Element | null => { const rangeStart = performanceEvent.start_time - const rangeEnd = performanceEvent.response_end + const rangeEnd = performanceEvent.load_event_end ? performanceEvent.load_event_end : performanceEvent.response_end if (typeof rangeStart === 'number' && typeof rangeEnd === 'number') { - const performanceParts = calculatePerformanceParts(performanceEvent) + const timings = calculatePerformanceParts(performanceEvent) return (
{perfSections.map((section) => { - const matchedSection = performanceParts[section] + const matchedSection = timings[section] const start = matchedSection?.start const end = matchedSection?.end const partDuration = end - start @@ -263,7 +293,7 @@ const TimeLineView = ({ performanceEvent }: { performanceEvent: PerformanceEvent
) } - return Cannot render performance timeline for this request + return null } const TableView = ({ performanceEvent }: { performanceEvent: PerformanceEvent }): JSX.Element => { @@ -283,11 +313,15 @@ export const NetworkRequestTiming = ({ }): JSX.Element | null => { const [timelineMode, setTimelineMode] = useState(true) + // if timeline view renders null then we fall back to table view + const timelineView = timelineMode ? : null + return (
setTimelineMode(!timelineMode)} data-attr={`switch-timing-to-${timelineMode ? 'table' : 'timeline'}-view`} @@ -296,11 +330,11 @@ export const NetworkRequestTiming = ({
- {timelineMode ? ( - - ) : ( - - )} + {timelineMode && timelineView ? timelineView : }
) } + +function isPresent(x: number | undefined): x is number { + return typeof x === 'number' +} diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/Timing/calculatePerformanceParts.test.ts b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/calculatePerformanceParts.test.ts new file mode 100644 index 0000000000000..24f59d7f4af3d --- /dev/null +++ b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/calculatePerformanceParts.test.ts @@ -0,0 +1,192 @@ +import { mapRRWebNetworkRequest } from 'scenes/session-recordings/player/inspector/performance-event-utils' +import { InitiatorType } from 'posthog-js' +import { calculatePerformanceParts } from 'scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming' + +jest.mock('lib/colors', () => { + return { + getSeriesColor: jest.fn(() => '#000000'), + } +}) + +describe('calculatePerformanceParts', () => { + it('can calculate TTFB', () => { + const perfEvent = { + connect_end: 9525.599999964237, + connect_start: 9525.599999964237, + decoded_body_size: 18260, + domain_lookup_end: 9525.599999964237, + domain_lookup_start: 9525.599999964237, + duration: 935.5, + encoded_body_size: 18260, + entry_type: 'resource', + fetch_start: 9525.599999964237, + initiator_type: 'fetch', + name: 'http://localhost:8000/api/organizations/@current/plugins/repository/', + next_hop_protocol: 'http/1.1', + redirect_end: 0, + redirect_start: 0, + render_blocking_status: 'non-blocking', + request_start: 9803.099999964237, + response_end: 10461.099999964237, + response_start: 10428.399999976158, + response_status: 200, + secure_connection_start: 0, + start_time: 9525.599999964237, + time_origin: '1699990397357', + timestamp: 1699990406882, + transfer_size: 18560, + window_id: '018bcf51-b1f0-7fe0-ac05-10543621f4f2', + worker_start: 0, + uuid: '12345', + distinct_id: '23456', + session_id: 'abcde', + pageview_id: 'fghij', + current_url: 'http://localhost:8000/insights', + } + + expect(calculatePerformanceParts(perfEvent)).toEqual({ + 'request queuing time': { + color: '#000000', + end: 9803.099999964237, + start: 9525.599999964237, + }, + + 'waiting for first byte': { + color: '#000000', + end: 10428.399999976158, + start: 9803.099999964237, + }, + 'receiving response': { + color: '#000000', + end: 10461.099999964237, + start: 10428.399999976158, + }, + }) + }) + + it('can handle gravatar timings', () => { + const gravatarReqRes = { + name: 'https://www.gravatar.com/avatar/2e7d95b60efbe947f71009a1af1ba8d0?s=96&d=404', + entryType: 'resource', + initiatorType: 'fetch' as InitiatorType, + deliveryType: '', + nextHopProtocol: '', + renderBlockingStatus: 'non-blocking', + workerStart: 0, + redirectStart: 0, + redirectEnd: 0, + domainLookupStart: 0, + domainLookupEnd: 0, + connectStart: 0, + secureConnectionStart: 0, + connectEnd: 0, + requestStart: 0, + responseStart: 0, + firstInterimResponseStart: 0, + // only fetch start and response end + // and transfer size is 0 + // loaded from disk cache + startTime: 18229, + fetchStart: 18228.5, + responseEnd: 18267.5, + endTime: 18268, + duration: 39, + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0, + responseStatus: 200, + serverTiming: [], + timeOrigin: 1700296048424, + timestamp: 1700296066652, + method: 'GET', + status: 200, + requestHeaders: {}, + requestBody: null, + responseHeaders: { + 'cache-control': 'max-age=300', + 'content-length': '13127', + 'content-type': 'image/png', + expires: 'Sat, 18 Nov 2023 08:32:46 GMT', + 'last-modified': 'Wed, 02 Feb 2022 09:11:05 GMT', + }, + responseBody: '�PNGblah', + } + const mappedToPerfEvent = mapRRWebNetworkRequest(gravatarReqRes, 'windowId', 1700296066652) + expect(calculatePerformanceParts(mappedToPerfEvent)).toEqual({ + // 'app cache' not included - end would be before beginning + // 'connection time' has 0 length + // 'dns lookup' has 0 length + // 'redirect has 0 length + // 'tls time' has 0 length + // TTFB has 0 length + 'receiving response': { + color: '#000000', + end: 18267.5, + start: 18228.5, + }, + }) + }) + + it('can handle no TLS connection timing', () => { + const tlsFreeReqRes = { + name: 'http://localhost:8000/decide/?v=3&ip=1&_=1700319068450&ver=1.91.1', + entryType: 'resource', + startTime: 6648, + duration: 93.40000003576279, + initiatorType: 'xmlhttprequest' as InitiatorType, + deliveryType: '', + nextHopProtocol: 'http/1.1', + renderBlockingStatus: 'non-blocking', + workerStart: 0, + redirectStart: 0, + redirectEnd: 0, + fetchStart: 6647.699999988079, + domainLookupStart: 6648.800000011921, + domainLookupEnd: 6648.800000011921, + connectStart: 6648.800000011921, + secureConnectionStart: 0, + connectEnd: 6649.300000011921, + requestStart: 6649.5, + responseStart: 6740.800000011921, + firstInterimResponseStart: 0, + responseEnd: 6741.100000023842, + transferSize: 2383, + encodedBodySize: 2083, + decodedBodySize: 2083, + responseStatus: 200, + serverTiming: [], + endTime: 6741, + timeOrigin: 1700319061802, + timestamp: 1700319068449, + isInitial: true, + } + const mappedToPerfEvent = mapRRWebNetworkRequest(tlsFreeReqRes, 'windowId', 1700319068449) + expect(calculatePerformanceParts(mappedToPerfEvent)).toEqual({ + 'app cache': { + color: '#000000', + end: 6648.800000011921, + start: 6647.699999988079, + }, + 'connection time': { + color: '#000000', + end: 6649.300000011921, + start: 6648.800000011921, + }, + 'waiting for first byte': { + color: '#000000', + end: 6740.800000011921, + start: 6649.5, + }, + 'receiving response': { + color: '#000000', + end: 6741.100000023842, + start: 6740.800000011921, + }, + 'request queuing time': { + color: '#000000', + end: 6649.5, + start: 6649.300000011921, + }, + }) + }) +}) diff --git a/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts b/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts index 43d0b2ef616b8..564b96fb865db 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts +++ b/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts @@ -1,10 +1,9 @@ import { eventWithTime } from '@rrweb/types' -// import posthog from 'posthog-js' +import { CapturedNetworkRequest } from 'posthog-js' import { PerformanceEvent } from '~/types' const NETWORK_PLUGIN_NAME = 'posthog/network@1' -// const RRWEB_NETWORK_PLUGIN_NAME = 'rrweb/network@1' -// const IGNORED_POSTHOG_PATHS = ['/s/', '/e/', '/i/v0/e/'] +const RRWEB_NETWORK_PLUGIN_NAME = 'rrweb/network@1' export const PerformanceEventReverseMapping: { [key: number]: keyof PerformanceEvent } = { // BASE_PERFORMANCE_EVENT_COLUMNS @@ -58,8 +57,92 @@ export const PerformanceEventReverseMapping: { [key: number]: keyof PerformanceE 40: 'timestamp', } +export const RRWebPerformanceEventReverseMapping: Record = { + // BASE_PERFORMANCE_EVENT_COLUMNS + entryType: 'entry_type', + timeOrigin: 'time_origin', + name: 'name', + + // RESOURCE_EVENT_COLUMNS + startTime: 'start_time', + redirectStart: 'redirect_start', + redirectEnd: 'redirect_end', + workerStart: 'worker_start', + fetchStart: 'fetch_start', + domainLookupStart: 'domain_lookup_start', + domainLookupEnd: 'domain_lookup_end', + connectStart: 'connect_start', + secureConnectionStart: 'secure_connection_start', + connectEnd: 'connect_end', + requestStart: 'request_start', + responseStart: 'response_start', + responseEnd: 'response_end', + decodedBodySize: 'decoded_body_size', + encodedBodySize: 'encoded_body_size', + initiatorType: 'initiator_type', + nextHopProtocol: 'next_hop_protocol', + renderBlockingStatus: 'render_blocking_status', + responseStatus: 'response_status', + transferSize: 'transfer_size', + + // LARGEST_CONTENTFUL_PAINT_EVENT_COLUMNS + largestContentfulPaintElement: 'largest_contentful_paint_element', + largestContentfulPaintRenderTime: 'largest_contentful_paint_render_time', + largestContentfulPaintLoadTime: 'largest_contentful_paint_load_time', + largestContentfulPaintSize: 'largest_contentful_paint_size', + largestContentfulPaintId: 'largest_contentful_paint_id', + largestContentfulPaintUrl: 'largest_contentful_paint_url', + + // NAVIGATION_EVENT_COLUMNS + domComplete: 'dom_complete', + domContentLoadedEvent: 'dom_content_loaded_event', + domInteractive: 'dom_interactive', + loadEventEnd: 'load_event_end', + loadEventStart: 'load_event_start', + redirectCount: 'redirect_count', + navigationType: 'navigation_type', + unloadEventEnd: 'unload_event_end', + unloadEventStart: 'unload_event_start', + + // Added after v1 + duration: 'duration', + timestamp: 'timestamp', + + //rrweb/network@1 + isInitial: 'is_initial', + requestHeaders: 'request_headers', + responseHeaders: 'response_headers', + requestBody: 'request_body', + responseBody: 'response_body', + method: 'method', +} + +export function mapRRWebNetworkRequest( + capturedRequest: CapturedNetworkRequest, + windowId: string, + timestamp: PerformanceEvent['timestamp'] +): PerformanceEvent { + const data: Partial = { + timestamp: timestamp, + window_id: windowId, + raw: capturedRequest, + } + + Object.entries(RRWebPerformanceEventReverseMapping).forEach(([key, value]) => { + if (key in capturedRequest) { + data[value] = capturedRequest[key] + } + }) + + return data as PerformanceEvent +} + export function matchNetworkEvents(snapshotsByWindowId: Record): PerformanceEvent[] { - const eventsMapping: Record> = {} + // we only support rrweb/network@1 events or posthog/network@1 events in any one recording + // apart from during testing, where we might have both + // if we have both, we only display posthog/network@1 events + const events: PerformanceEvent[] = [] + const rrwebEvents: PerformanceEvent[] = [] // we could do this in one pass, but it's easier to log missing events // when we have all the posthog/network@1 events first @@ -83,93 +166,27 @@ export function matchNetworkEvents(snapshotsByWindowId: Record { + const data: PerformanceEvent = mapRRWebNetworkRequest(capturedRequest, windowId, snapshot.timestamp) - eventsMapping[eventName] = eventsMapping[eventName] || {} - eventsMapping[eventName][startTime] = eventsMapping[eventName][startTime] || [] - eventsMapping[eventName][startTime].push(mappedData) + rrwebEvents.push(data) + }) } }) }) - // // now we have all the posthog/network@1 events we can try to match any rrweb/network@1 events - // Object.entries(snapshotsByWindowId).forEach((snapshotsByWindowId) => { - // const snapshots = snapshotsByWindowId[1] - // snapshots.forEach((snapshot: eventWithTime) => { - // if ( - // snapshot.type === 6 && // RRWeb plugin event type - // snapshot.data.plugin === RRWEB_NETWORK_PLUGIN_NAME - // ) { - // const payload = snapshot.data.payload as any - // if (!Array.isArray(payload.requests) || payload.requests.length === 0) { - // return - // } - // - // payload.requests.forEach((capturedRequest: any) => { - // const matchedURL = eventsMapping[capturedRequest.url] - // - // const matchedStartTime = matchedURL ? matchedURL[capturedRequest.startTime] : null - // - // if (matchedStartTime && matchedStartTime.length === 1) { - // matchedStartTime[0].response_status = capturedRequest.status - // matchedStartTime[0].request_headers = capturedRequest.requestHeaders - // matchedStartTime[0].request_body = capturedRequest.requestBody - // matchedStartTime[0].response_headers = capturedRequest.responseHeaders - // matchedStartTime[0].response_body = capturedRequest.responseBody - // matchedStartTime[0].method = capturedRequest.method - // } else if (matchedStartTime && matchedStartTime.length > 1) { - // // find in eventsMapping[capturedRequest.url][capturedRequest.startTime] by matching capturedRequest.endTime and element.response_end - // const matchedEndTime = matchedStartTime.find( - // (x) => - // typeof x.response_end === 'number' && - // Math.round(x.response_end) === capturedRequest.endTime - // ) - // if (matchedEndTime) { - // matchedEndTime.response_status = capturedRequest.status - // matchedEndTime.request_headers = capturedRequest.requestHeaders - // matchedEndTime.request_body = capturedRequest.requestBody - // matchedEndTime.response_headers = capturedRequest.responseHeaders - // matchedEndTime.response_body = capturedRequest.responseBody - // matchedEndTime.method = capturedRequest.method - // } else { - // const capturedURL = new URL(capturedRequest.url) - // const capturedPath = capturedURL.pathname - // - // if (!IGNORED_POSTHOG_PATHS.some((x) => capturedPath === x)) { - // posthog.capture('Had matches but still could not match rrweb/network@1 event', { - // rrwebNetworkEvent: payload, - // possibleMatches: matchedStartTime, - // totalMatchedURLs: Object.keys(eventsMapping).length, - // }) - // } - // } - // } else { - // const capturedURL = new URL(capturedRequest.url) - // const capturedPath = capturedURL.pathname - // if (!IGNORED_POSTHOG_PATHS.some((x) => capturedPath === x)) { - // posthog.capture('Could not match rrweb/network@1 event', { - // rrwebNetworkEvent: payload, - // possibleMatches: eventsMapping[capturedRequest.url], - // totalMatchedURLs: Object.keys(eventsMapping).length, - // }) - // } - // } - // }) - // } - // }) - // }) - - // now flatten the eventsMapping into a single array - return Object.values(eventsMapping).reduce((acc: PerformanceEvent[], eventsByURL) => { - Object.values(eventsByURL).forEach((eventsByTime) => { - acc.push(...eventsByTime) - }) - return acc - }, []) + return events.length ? events : rrwebEvents } diff --git a/frontend/src/types.ts b/frontend/src/types.ts index e10cf5628fc73..1273d98ef07e7 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -1151,6 +1151,10 @@ export interface PerformanceEvent { request_body?: Body response_body?: Body method?: string + + //rrweb/network@1 - i.e. not in ClickHouse table + is_initial?: boolean + raw?: Record } export interface CurrentBillCycleType { diff --git a/package.json b/package.json index 4587978c3a728..e510b90210c08 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "monaco-editor": "^0.39.0", "papaparse": "^5.4.1", "pmtiles": "^2.11.0", - "posthog-js": "1.92.0", + "posthog-js": "1.92.1", "posthog-js-lite": "2.0.0-alpha5", "prettier": "^2.8.8", "prop-types": "^15.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 730433090dd94..29c30c7e95f81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -216,8 +216,8 @@ dependencies: specifier: ^2.11.0 version: 2.11.0 posthog-js: - specifier: 1.92.0 - version: 1.92.0 + specifier: 1.92.1 + version: 1.92.1 posthog-js-lite: specifier: 2.0.0-alpha5 version: 2.0.0-alpha5 @@ -15793,8 +15793,8 @@ packages: resolution: {integrity: sha512-tlkBdypJuvK/s00n4EiQjwYVfuuZv6vt8BF3g1ooIQa2Gz9Vz80p8q3qsPLZ0V5ErGRy6i3Q4fWC9TDzR7GNRQ==} dev: false - /posthog-js@1.92.0: - resolution: {integrity: sha512-87bZ/qwBbIqvkIV4YYn65oIPEsRcWihA3jX7WV33LvZWaU1InlE6cwj95SleIVLiND4Ofm+cKXZeWwcRnrXkKA==} + /posthog-js@1.92.1: + resolution: {integrity: sha512-xtuTfM/acfDauiEfIdKF6d911KUZQ7RLii2COAYEoPWr3cVUFoNUoRQz9QJvgDlV2j22Zwl+mnXacUeua+Yi1A==} dependencies: fflate: 0.4.8 dev: false