Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration'),
gzip: true,
limit: '83 KB',
limit: '84 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags',
Expand Down Expand Up @@ -138,7 +138,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'metrics', 'logger'),
gzip: true,
limit: '28 KB',
limit: '29 KB',
},
// React SDK (ESM)
{
Expand Down Expand Up @@ -197,7 +197,7 @@ module.exports = [
name: 'CDN Bundle (incl. Logs, Metrics)',
path: createCDNPath('bundle.logs.metrics.min.js'),
gzip: true,
limit: '30 KB',
limit: '31 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Logs, Metrics)',
Expand Down Expand Up @@ -283,21 +283,21 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'),
gzip: false,
brotli: false,
limit: '258.5 KB',
limit: '259 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed',
path: createCDNPath('bundle.tracing.replay.feedback.min.js'),
gzip: false,
brotli: false,
limit: '268 KB',
limit: '269 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed',
path: createCDNPath('bundle.tracing.replay.feedback.logs.metrics.min.js'),
gzip: false,
brotli: false,
limit: '271.5 KB',
limit: '272 KB',
},
// Next.js SDK (ESM)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.spanStreamingIntegration(), Sentry.browserTracingIntegration()],
tracesSampleRate: 1.0,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { shouldSkipTracingTest } from '../../../utils/helpers';
import { getSpanOp, waitForStreamedSpans } from '../../../utils/spanUtils';

sentryTest('httpContextIntegration captures url, user-agent, and referer', async ({ getLocalTestUrl, page }) => {
sentryTest.skip(shouldSkipTracingTest());
const url = await getLocalTestUrl({ testDir: __dirname });

const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload'));

await page.goto(url, { referer: 'https://sentry.io/' });

const spans = await spansPromise;

const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload');

expect(pageloadSpan!.attributes?.['url.full']).toEqual({ type: 'string', value: expect.any(String) });
expect(pageloadSpan!.attributes?.['http.request.header.user_agent']).toEqual({
type: 'string',
value: expect.any(String),
});
expect(pageloadSpan!.attributes?.['http.request.header.referer']).toEqual({
type: 'string',
value: 'https://sentry.io/',
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ sentryTest(
type: 'string',
value: expect.any(String),
},
'http.request.header.user_agent': {
type: 'string',
value: expect.any(String),
},
'url.full': {
type: 'string',
value: expect.any(String),
},
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: {
type: 'string',
value: 'test',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ sentryTest('captures streamed interaction span tree. @firefox', async ({ browser
type: 'string',
value: expect.any(String),
},
'http.request.header.user_agent': {
type: 'string',
value: expect.any(String),
},
'url.full': {
type: 'string',
value: expect.any(String),
},
[SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]: {
type: 'string',
value: 'idleTimeout',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow
type: 'string',
value: expect.any(String),
},
'http.request.header.user_agent': {
type: 'string',
value: expect.any(String),
},
'url.full': {
type: 'string',
value: expect.any(String),
},
'device.processor_count': {
type: expect.stringMatching(/^(integer)|(double)$/),
value: expect.any(Number),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ sentryTest(
type: 'string',
value: expect.any(String),
},
'http.request.header.user_agent': {
type: 'string',
value: expect.any(String),
},
'url.full': {
type: 'string',
value: expect.any(String),
},
// formerly known as 'hardwareConcurrency'
'device.processor_count': {
type: expect.stringMatching(/^(integer)|(double)$/),
Expand Down
15 changes: 15 additions & 0 deletions packages/browser/src/integrations/httpcontext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,20 @@ export const httpContextIntegration = defineIntegration(() => {
headers,
};
},
processSegmentSpan(span) {
// if none of the information we want exists, don't bother
if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {
return;
}

const reqData = getHttpRequestData();

span.attributes = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: instead of spreading this here, can we just set the attributes we want to set directly? IMHO we can just have a utility we keep using like setIfNotExists(span.attributes, 'url.full', reqData.url), or similar? Not 100% sure but I'd suspect this is faster at scale than creating a new object all the time when processing spans like this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a utility @sentry/core that does exactly what we would want I think (ref). We could export that from core and then use it in any integrations we need to port. I copied the approach here from how Lukas did the cultureContext integration (ref), not sure if he had a specific reason in mind not to use this. Wdyt?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good to me (exporting this and using it). FWIW the approach from lukas and in this PR are not bad I just think it is slightly better (probably) to avoid creating new objects for these things all the time - and this will likely happen quite a lot if there are multiple event processors all just adding one or two attributes!

'url.full': reqData.url,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The code unconditionally sets span.attributes['url.full'] even when the URL is an empty string, violating the Sentry span protocol which requires omitting empty attributes.
Severity: MEDIUM

Suggested Fix

Conditionally set the 'url.full' attribute only when reqData.url is a non-empty string. Add a check, such as if (reqData.url) { span.attributes['url.full'] = reqData.url; }, to ensure empty attributes are not sent, aligning with the protocol and the handling of other optional attributes.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/browser/src/integrations/httpcontext.ts#L38

Potential issue: In environments where `window.document.location.href` is inaccessible,
such as sandboxed iframes, the `getLocationHref()` function returns an empty string.
Consequently, `getHttpRequestData()` provides an empty string for the URL. The new code
at `processSegmentSpan` unconditionally assigns this empty string to
`span.attributes['url.full']`. This violates the Sentry span protocol, which mandates
that empty attributes must be omitted. This behavior is inconsistent with how other
optional attributes like `User-Agent` are handled, which are only added if they have a
value, and could lead to data validation issues.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^ this also seems legitimate, we likely need to make sure that passing in undefined as a value does not result in this being set on the attributes object.

'http.request.header.user_agent': reqData.headers['User-Agent'],
'http.request.header.referer': reqData.headers['Referer'],
Comment thread
nicohrubec marked this conversation as resolved.
...span.attributes,
};
Comment thread
nicohrubec marked this conversation as resolved.
},
};
});
Loading