Merged
Show file tree
Hide file tree
Changes from all commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Failed to load files.
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,7 +52,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration'),
gzip: true,
limit: '71 KB',
limit: '70.1 KB',
modifyWebpackConfig: function (config) {
const webpack = require('webpack');

Expand DownExpand Up@@ -206,7 +206,7 @@ module.exports = [
import: createImport('init'),
ignore: ['next/router', 'next/constants'],
gzip: true,
limit: '42.5 KB',
limit: '42 KB',
},
// SvelteKit SDK (ESM)
{
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
/* eslint-disable max-lines */
import type { Measurements, Span, SpanAttributes, SpanAttributeValue, StartSpanOptions } from '@sentry/core';
import type { Measurements, Span, SpanAttributes, StartSpanOptions } from '@sentry/core';
import {
browserPerformanceTimeOrigin,
getActiveSpan,
getComponentName,
htmlTreeAsString,
isPrimitive,
parseUrl,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
setMeasurement,
Expand DownExpand Up@@ -340,7 +339,7 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries
case 'mark':
case 'paint':
case 'measure': {
_addMeasureSpans(span, entry as PerformanceMeasure, startTime, duration, timeOrigin);
_addMeasureSpans(span, entry, startTime, duration, timeOrigin);

// capture web vitals
const firstHidden = getVisibilityWatcher();
Expand DownExpand Up@@ -422,7 +421,7 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries
*/
export function _addMeasureSpans(
span: Span,
entry: PerformanceMeasure,
entry: PerformanceEntry,
startTime: number,
duration: number,
timeOrigin: number,
Expand DownExpand Up@@ -451,34 +450,6 @@ export function _addMeasureSpans(
attributes['sentry.browser.measure_start_time'] = measureStartTimestamp;
}

// https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure#detail
if (entry.detail) {
// Handle detail as an object
if (typeof entry.detail === 'object') {
for (const [key, value] of Object.entries(entry.detail)) {
if (value && isPrimitive(value)) {
attributes[`sentry.browser.measure.detail.${key}`] = value as SpanAttributeValue;
} else {
try {
// This is user defined so we can't guarantee it's serializable
attributes[`sentry.browser.measure.detail.${key}`] = JSON.stringify(value);
} catch {
// skip
}
}
}
} else if (isPrimitive(entry.detail)) {
attributes['sentry.browser.measure.detail'] = entry.detail as SpanAttributeValue;
} else {
// This is user defined so we can't guarantee it's serializable
try {
attributes['sentry.browser.measure.detail'] = JSON.stringify(entry.detail);
} catch {
// skip
}
}
}

// Measurements from third parties can be off, which would create invalid spans, dropping transactions in the process.
if (measureStartTimestamp <= measureEndTimestamp) {
startAndEndSpan(span, measureStartTimestamp, measureEndTimestamp, {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -70,8 +70,7 @@ describe('_addMeasureSpans', () => {
name: 'measure-1',
duration: 10,
startTime: 12,
detail: null,
} as PerformanceMeasure;
} as PerformanceEntry;

const timeOrigin = 100;
const startTime = 23;
Expand DownExpand Up@@ -107,8 +106,7 @@ describe('_addMeasureSpans', () => {
name: 'measure-1',
duration: 10,
startTime: 12,
detail: null,
} as PerformanceMeasure;
} as PerformanceEntry;

const timeOrigin = 100;
const startTime = 23;
Expand All@@ -118,165 +116,6 @@ describe('_addMeasureSpans', () => {

expect(spans).toHaveLength(0);
});

it('adds measure spans with primitive detail', () => {
const spans: Span[] = [];

getClient()?.on('spanEnd', span => {
spans.push(span);
});

const entry = {
entryType: 'measure',
name: 'measure-1',
duration: 10,
startTime: 12,
detail: 'test-detail',
} as PerformanceMeasure;

const timeOrigin = 100;
const startTime = 23;
const duration = 356;

_addMeasureSpans(span, entry, startTime, duration, timeOrigin);

expect(spans).toHaveLength(1);
expect(spanToJSON(spans[0]!)).toEqual(
expect.objectContaining({
description: 'measure-1',
start_timestamp: timeOrigin + startTime,
timestamp: timeOrigin + startTime + duration,
op: 'measure',
origin: 'auto.resource.browser.metrics',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'measure',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',
'sentry.browser.measure.detail': 'test-detail',
},
}),
);
});

it('adds measure spans with object detail', () => {
const spans: Span[] = [];

getClient()?.on('spanEnd', span => {
spans.push(span);
});

const detail = {
component: 'Button',
action: 'click',
metadata: { id: 123 },
};

const entry = {
entryType: 'measure',
name: 'measure-1',
duration: 10,
startTime: 12,
detail,
} as PerformanceMeasure;

const timeOrigin = 100;
const startTime = 23;
const duration = 356;

_addMeasureSpans(span, entry, startTime, duration, timeOrigin);

expect(spans).toHaveLength(1);
expect(spanToJSON(spans[0]!)).toEqual(
expect.objectContaining({
description: 'measure-1',
start_timestamp: timeOrigin + startTime,
timestamp: timeOrigin + startTime + duration,
op: 'measure',
origin: 'auto.resource.browser.metrics',
data: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'measure',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',
'sentry.browser.measure.detail.component': 'Button',
'sentry.browser.measure.detail.action': 'click',
'sentry.browser.measure.detail.metadata': JSON.stringify({ id: 123 }),
},
}),
);
});

it('handles non-primitive detail values by stringifying them', () => {
const spans: Span[] = [];

getClient()?.on('spanEnd', span => {
spans.push(span);
});

const detail = {
component: 'Button',
action: 'click',
metadata: { id: 123 },
callback: () => {},
};

const entry = {
entryType: 'measure',
name: 'measure-1',
duration: 10,
startTime: 12,
detail,
} as PerformanceMeasure;

const timeOrigin = 100;
const startTime = 23;
const duration = 356;

_addMeasureSpans(span, entry, startTime, duration, timeOrigin);

expect(spans).toHaveLength(1);
const spanData = spanToJSON(spans[0]!).data;
expect(spanData['sentry.browser.measure.detail.component']).toBe('Button');
expect(spanData['sentry.browser.measure.detail.action']).toBe('click');
expect(spanData['sentry.browser.measure.detail.metadata']).toBe(JSON.stringify({ id: 123 }));
expect(spanData['sentry.browser.measure.detail.callback']).toBe(JSON.stringify(detail.callback));
});

it('handles errors in object detail value stringification', () => {
const spans: Span[] = [];

getClient()?.on('spanEnd', span => {
spans.push(span);
});

const circular: any = {};
circular.self = circular;

const detail = {
component: 'Button',
action: 'click',
circular,
};

const entry = {
entryType: 'measure',
name: 'measure-1',
duration: 10,
startTime: 12,
detail,
} as PerformanceMeasure;

const timeOrigin = 100;
const startTime = 23;
const duration = 356;

// Should not throw
_addMeasureSpans(span, entry, startTime, duration, timeOrigin);

expect(spans).toHaveLength(1);
const spanData = spanToJSON(spans[0]!).data;
expect(spanData['sentry.browser.measure.detail.component']).toBe('Button');
expect(spanData['sentry.browser.measure.detail.action']).toBe('click');
// The circular reference should be skipped
expect(spanData['sentry.browser.measure.detail.circular']).toBeUndefined();
});
});

describe('_addResourceSpans', () => {
Expand DownExpand Up@@ -625,6 +464,7 @@ describe('_addNavigationSpans', () => {
transferSize: 14726,
encodedBodySize: 14426,
decodedBodySize: 67232,
responseStatus: 200,
serverTiming: [],
unloadEventStart: 0,
unloadEventEnd: 0,
Expand Down
Loading