Skip to content

Commit d6dedf6

Browse files
committed
feat(browser): Add support for streamed spans in cultureContextIntegration
1 parent 5f72df5 commit d6dedf6

File tree

6 files changed

+71
-1
lines changed

6 files changed

+71
-1
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
integrations: [Sentry.spanStreamingIntegration(), Sentry.browserTracingIntegration()],
8+
tracesSampleRate: 1.0,
9+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../utils/fixtures';
3+
import { getSpanOp, waitForStreamedSpans } from '../../../utils/spanUtils';
4+
import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers';
5+
6+
sentryTest('cultureContextIntegration captures locale, timezone, and calendar', async ({ getLocalTestUrl, page }) => {
7+
sentryTest.skip(shouldSkipTracingTest() || testingCdnBundle());
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
10+
const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload'));
11+
12+
await page.goto(url);
13+
14+
const spans = await spansPromise;
15+
16+
const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload');
17+
18+
expect(pageloadSpan!.attributes?.['culture.locale']).toEqual({ type: 'string', value: expect.any(String) });
19+
expect(pageloadSpan!.attributes?.['culture.timezone']).toEqual({ type: 'string', value: expect.any(String) });
20+
expect(pageloadSpan!.attributes?.['culture.calendar']).toEqual({ type: 'string', value: expect.any(String) });
21+
});

packages/browser/src/integrations/culturecontext.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ const _cultureContextIntegration = (() => {
1717
};
1818
}
1919
},
20+
processSegmentSpan(span) {
21+
const culture = getCultureContext();
22+
23+
if (culture) {
24+
span.attributes = {
25+
'culture.locale': culture.locale,
26+
'culture.timezone': culture.timezone,
27+
'culture.calendar': culture.calendar,
28+
xxxDeleteMe: undefined,
29+
...span.attributes,
30+
};
31+
}
32+
},
2033
};
2134
}) satisfies IntegrationFn;
2235

packages/core/src/integration.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ export function setupIntegration(client: Client, integration: Integration, integ
138138
client.addEventProcessor(processor);
139139
}
140140

141+
if (typeof integration.processSpan === 'function') {
142+
const callback = integration.processSpan.bind(integration) as typeof integration.processSpan;
143+
client.on('processSpan', span => callback(span, client));
144+
}
145+
146+
if (typeof integration.processSegmentSpan === 'function') {
147+
const callback = integration.processSegmentSpan.bind(integration) as typeof integration.processSegmentSpan;
148+
client.on('processSegmentSpan', span => callback(span, client));
149+
}
150+
141151
DEBUG_BUILD && debug.log(`Integration installed: ${integration.name}`);
142152
}
143153

packages/core/src/tracing/spans/captureSpan.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ export function captureSpan(span: Span, client: Client): SerializedStreamedSpanW
5454
if (spanJSON.is_segment) {
5555
applyScopeToSegmentSpan(spanJSON, finalScopeData);
5656
// Allow hook subscribers to mutate the segment span JSON
57+
// This also invokes the `processSegmentSpan` hook of all integrations
5758
client.emit('processSegmentSpan', spanJSON);
5859
}
5960

60-
// Allow hook subscribers to mutate the span JSON
61+
// This allows hook subscribers to mutate the span JSON
62+
// This also invokes the `processSpan` hook of all integrations
6163
client.emit('processSpan', spanJSON);
6264

6365
const { beforeSendSpan } = client.getOptions();

packages/core/src/types-hoist/integration.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Client } from '../client';
22
import type { Event, EventHint } from './event';
3+
import { StreamedSpanJSON } from './span';
34

45
/** Integration interface */
56
export interface Integration {
@@ -50,6 +51,20 @@ export interface Integration {
5051
* This receives the client that the integration was installed for as third argument.
5152
*/
5253
processEvent?(event: Event, hint: EventHint, client: Client): Event | null | PromiseLike<Event | null>;
54+
55+
/**
56+
* An optional hook that allows modifications to a span. This hook runs after the span is ended,
57+
* during `captureSpan` and before the span is passed to users' `beforeSendSpan` callback.
58+
* Use this hook to modify a span in-place.
59+
*/
60+
processSpan?(span: StreamedSpanJSON, client: Client): void;
61+
62+
/**
63+
* An optional hook that allows modifications to a segment span. This hook runs after the segment span is ended,
64+
* during `captureSpan` and before the segment span is passed to users' `beforeSendSpan` callback.
65+
* Use this hook to modify a segment span in-place.
66+
*/
67+
processSegmentSpan?(span: StreamedSpanJSON, client: Client): void;
5368
}
5469

5570
/**

0 commit comments

Comments
 (0)