Skip to content

Commit ef6396a

Browse files
feat(tracing): Change resource span op name and add data (#2816)
Co-authored-by: Rodolfo Carvalho <[email protected]>
1 parent aa28800 commit ef6396a

File tree

3 files changed

+158
-5
lines changed

3 files changed

+158
-5
lines changed

packages/eslint-config-sdk/src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ module.exports = {
3939
// Enforce type annotations to maintain consistency. This is especially important as
4040
// we have a public API, so we want changes to be very explicit.
4141
'@typescript-eslint/typedef': ['error', { arrowParameter: false }],
42+
43+
// Although for most codebases inferencing the return type is fine, we explicitly ask to annotate
44+
// all functions with a return type. This is so that intent is as clear as possible. We are guarding against
45+
// cases where you accidently refactor a function's return type to be the wrong type.
4246
'@typescript-eslint/explicit-function-return-type': ['error', { allowExpressions: true }],
4347

4448
// Consistent ordering of fields, methods and constructors for classes should be enforced

packages/tracing/src/browser/metrics.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable max-lines */
12
/* eslint-disable @typescript-eslint/no-explicit-any */
23
import { SpanContext } from '@sentry/types';
34
import { getGlobalObject, logger } from '@sentry/utils';
@@ -211,10 +212,17 @@ function addMeasureSpans(
211212
return measureStartTimestamp;
212213
}
213214

215+
export interface ResourceEntry extends Record<string, unknown> {
216+
initiatorType?: string;
217+
transferSize?: number;
218+
encodedBodySize?: number;
219+
decodedBodySize?: number;
220+
}
221+
214222
/** Create resource related spans */
215-
function addResourceSpans(
223+
export function addResourceSpans(
216224
transaction: Transaction,
217-
entry: Record<string, any>,
225+
entry: ResourceEntry,
218226
resourceName: string,
219227
startTime: number,
220228
duration: number,
@@ -226,14 +234,26 @@ function addResourceSpans(
226234
return undefined;
227235
}
228236

237+
const data: Record<string, any> = {};
238+
if ('transferSize' in entry) {
239+
data['Transfer Size'] = entry.transferSize;
240+
}
241+
if ('encodedBodySize' in entry) {
242+
data['Encoded Body Size'] = entry.encodedBodySize;
243+
}
244+
if ('decodedBodySize' in entry) {
245+
data['Decoded Body Size'] = entry.decodedBodySize;
246+
}
247+
229248
const startTimestamp = timeOrigin + startTime;
230249
const endTimestamp = startTimestamp + duration;
231250

232251
_startChild(transaction, {
233-
description: `${entry.initiatorType} ${resourceName}`,
252+
description: resourceName,
234253
endTimestamp,
235-
op: 'resource',
254+
op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource',
236255
startTimestamp,
256+
data,
237257
});
238258

239259
return endTimestamp;

packages/tracing/test/browser/metrics.test.ts

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Span, Transaction } from '../../src';
2-
import { _startChild } from '../../src/browser/metrics';
2+
import { _startChild, addResourceSpans, ResourceEntry } from '../../src/browser/metrics';
33

44
describe('_startChild()', () => {
55
it('creates a span with given properties', () => {
@@ -38,3 +38,132 @@ describe('_startChild()', () => {
3838
expect(transaction.startTimestamp).toEqual(123);
3939
});
4040
});
41+
42+
describe('addResourceSpans', () => {
43+
const transaction = new Transaction({ name: 'hello' });
44+
beforeEach(() => {
45+
transaction.startChild = jest.fn();
46+
});
47+
48+
// We already track xhr, we don't need to use
49+
it('does not create spans for xmlhttprequest', () => {
50+
const entry: ResourceEntry = {
51+
initiatorType: 'xmlhttprequest',
52+
transferSize: 256,
53+
encodedBodySize: 256,
54+
decodedBodySize: 256,
55+
};
56+
addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100);
57+
58+
// eslint-disable-next-line @typescript-eslint/unbound-method
59+
expect(transaction.startChild).toHaveBeenCalledTimes(0);
60+
});
61+
62+
it('does not create spans for fetch', () => {
63+
const entry: ResourceEntry = {
64+
initiatorType: 'fetch',
65+
transferSize: 256,
66+
encodedBodySize: 256,
67+
decodedBodySize: 256,
68+
};
69+
addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100);
70+
71+
// eslint-disable-next-line @typescript-eslint/unbound-method
72+
expect(transaction.startChild).toHaveBeenCalledTimes(0);
73+
});
74+
75+
it('creates spans for resource spans', () => {
76+
const entry: ResourceEntry = {
77+
initiatorType: 'css',
78+
transferSize: 256,
79+
encodedBodySize: 456,
80+
decodedBodySize: 593,
81+
};
82+
83+
const timeOrigin = 100;
84+
const startTime = 23;
85+
const duration = 356;
86+
87+
const endTimestamp = addResourceSpans(transaction, entry, '/assets/to/css', startTime, duration, timeOrigin);
88+
89+
// eslint-disable-next-line @typescript-eslint/unbound-method
90+
expect(transaction.startChild).toHaveBeenCalledTimes(1);
91+
// eslint-disable-next-line @typescript-eslint/unbound-method
92+
expect(transaction.startChild).toHaveBeenLastCalledWith({
93+
data: {
94+
['Decoded Body Size']: entry.decodedBodySize,
95+
['Encoded Body Size']: entry.encodedBodySize,
96+
['Transfer Size']: entry.transferSize,
97+
},
98+
description: '/assets/to/css',
99+
endTimestamp: timeOrigin + startTime + duration,
100+
op: 'resource.css',
101+
startTimestamp: timeOrigin + startTime,
102+
});
103+
104+
expect(endTimestamp).toBe(timeOrigin + startTime + duration);
105+
});
106+
107+
it('creates a variety of resource spans', () => {
108+
const table = [
109+
{
110+
initiatorType: undefined,
111+
op: 'resource',
112+
},
113+
{
114+
initiatorType: '',
115+
op: 'resource',
116+
},
117+
{
118+
initiatorType: 'css',
119+
op: 'resource.css',
120+
},
121+
{
122+
initiatorType: 'image',
123+
op: 'resource.image',
124+
},
125+
{
126+
initiatorType: 'script',
127+
op: 'resource.script',
128+
},
129+
];
130+
131+
for (const { initiatorType, op } of table) {
132+
const entry: ResourceEntry = {
133+
initiatorType,
134+
};
135+
addResourceSpans(transaction, entry, '/assets/to/me', 123, 234, 465);
136+
137+
// eslint-disable-next-line @typescript-eslint/unbound-method
138+
expect(transaction.startChild).toHaveBeenLastCalledWith(
139+
expect.objectContaining({
140+
op,
141+
}),
142+
);
143+
}
144+
});
145+
146+
it('allows for enter size of 0', () => {
147+
const entry: ResourceEntry = {
148+
initiatorType: 'css',
149+
transferSize: 0,
150+
encodedBodySize: 0,
151+
decodedBodySize: 0,
152+
};
153+
154+
addResourceSpans(transaction, entry, '/assets/to/css', 100, 23, 345);
155+
156+
// eslint-disable-next-line @typescript-eslint/unbound-method
157+
expect(transaction.startChild).toHaveBeenCalledTimes(1);
158+
// eslint-disable-next-line @typescript-eslint/unbound-method
159+
expect(transaction.startChild).toHaveBeenLastCalledWith(
160+
expect.objectContaining({
161+
data: {
162+
['Decoded Body Size']: entry.decodedBodySize,
163+
['Encoded Body Size']: entry.encodedBodySize,
164+
['Transfer Size']: entry.transferSize,
165+
},
166+
}),
167+
);
168+
});
169+
});

0 commit comments

Comments
 (0)