-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(browser): Include culture context with events #19148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd028cf
df8560c
f4339bb
3678b78
94966b7
7a39bb8
99f06d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,11 @@ sentryTest('should capture feedback with custom button', async ({ getLocalTestUr | |
| type: 'feedback', | ||
| breadcrumbs: expect.any(Array), | ||
| contexts: { | ||
| culture: { | ||
| locale: expect.any(String), | ||
| timezone: expect.any(String), | ||
| calendar: expect.any(String), | ||
| }, | ||
| feedback: { | ||
| contact_email: '[email protected]', | ||
| message: 'my example feedback', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,11 @@ sentryTest('should capture feedback', async ({ getLocalTestUrl, page }) => { | |
| type: 'feedback', | ||
| breadcrumbs: expect.any(Array), | ||
| contexts: { | ||
| culture: { | ||
| locale: expect.any(String), | ||
| timezone: expect.any(String), | ||
| calendar: expect.any(String), | ||
| }, | ||
| feedback: { | ||
| contact_email: '[email protected]', | ||
| message: 'my example feedback', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,6 +72,11 @@ sentryTest('should capture feedback', async ({ forceFlushReplay, getLocalTestUrl | |
| type: 'feedback', | ||
| breadcrumbs: expect.any(Array), | ||
| contexts: { | ||
| culture: { | ||
| locale: expect.any(String), | ||
| timezone: expect.any(String), | ||
| calendar: expect.any(String), | ||
| }, | ||
| feedback: { | ||
| contact_email: '[email protected]', | ||
| message: 'my example feedback', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,11 @@ sentryTest('should capture feedback', async ({ getLocalTestUrl, page }) => { | |
| type: 'feedback', | ||
| breadcrumbs: expect.any(Array), | ||
| contexts: { | ||
| culture: { | ||
| locale: expect.any(String), | ||
| timezone: expect.any(String), | ||
| calendar: expect.any(String), | ||
| }, | ||
| feedback: { | ||
| contact_email: '[email protected]', | ||
| message: 'my example feedback', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import * as Sentry from '@sentry/browser'; | ||
|
|
||
| window.Sentry = Sentry; | ||
|
|
||
| const integrations = Sentry.getDefaultIntegrations({}).filter( | ||
| defaultIntegration => defaultIntegration.name === 'CultureContext', | ||
| ); | ||
|
|
||
| const client = new Sentry.BrowserClient({ | ||
| dsn: 'https://[email protected]/1337', | ||
| transport: Sentry.makeFetchTransport, | ||
| stackParser: Sentry.defaultStackParser, | ||
| integrations: integrations, | ||
| }); | ||
|
|
||
| const scope = new Sentry.Scope(); | ||
| scope.setClient(client); | ||
| client.init(); | ||
|
|
||
| window._sentryScope = scope; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| window._sentryScope.captureException(new Error('test error')); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { expect } from '@playwright/test'; | ||
| import type { Event } from '@sentry/core'; | ||
| import { sentryTest } from '../../../utils/fixtures'; | ||
| import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; | ||
|
|
||
| sentryTest('cultureContextIntegration captures locale, timezone, and calendar', async ({ getLocalTestUrl, page }) => { | ||
| const url = await getLocalTestUrl({ testDir: __dirname }); | ||
|
|
||
| const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url); | ||
|
|
||
| expect(eventData.exception?.values).toHaveLength(1); | ||
|
|
||
| expect(eventData.contexts?.culture).toEqual({ | ||
| locale: expect.any(String), | ||
| timezone: expect.any(String), | ||
| calendar: expect.any(String), | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,60 @@ | ||||||||||||||||||
| import type { CultureContext, IntegrationFn } from '@sentry/core'; | ||||||||||||||||||
| import { defineIntegration } from '@sentry/core'; | ||||||||||||||||||
| import { WINDOW } from '../helpers'; | ||||||||||||||||||
|
|
||||||||||||||||||
| const INTEGRATION_NAME = 'CultureContext'; | ||||||||||||||||||
|
|
||||||||||||||||||
| const _cultureContextIntegration = (() => { | ||||||||||||||||||
| return { | ||||||||||||||||||
| name: INTEGRATION_NAME, | ||||||||||||||||||
| preprocessEvent(event) { | ||||||||||||||||||
| const culture = getCultureContext(); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (culture) { | ||||||||||||||||||
| event.contexts = { | ||||||||||||||||||
| ...event.contexts, | ||||||||||||||||||
| culture: { ...culture, ...event.contexts?.culture }, | ||||||||||||||||||
| }; | ||||||||||||||||||
| } | ||||||||||||||||||
| }, | ||||||||||||||||||
| }; | ||||||||||||||||||
| }) satisfies IntegrationFn; | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Captures culture context from the browser. | ||||||||||||||||||
| * | ||||||||||||||||||
| * Enabled by default. | ||||||||||||||||||
| * | ||||||||||||||||||
| * @example | ||||||||||||||||||
| * ```js | ||||||||||||||||||
| * import * as Sentry from '@sentry/browser'; | ||||||||||||||||||
| * | ||||||||||||||||||
| * Sentry.init({ | ||||||||||||||||||
| * integrations: [Sentry.cultureContextIntegration()], | ||||||||||||||||||
| * }); | ||||||||||||||||||
| * ``` | ||||||||||||||||||
| */ | ||||||||||||||||||
| export const cultureContextIntegration = defineIntegration(_cultureContextIntegration); | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Returns the culture context from the browser's Intl API. | ||||||||||||||||||
| */ | ||||||||||||||||||
| function getCultureContext(): CultureContext | undefined { | ||||||||||||||||||
| try { | ||||||||||||||||||
| const intl = (WINDOW as { Intl?: typeof Intl }).Intl; | ||||||||||||||||||
| if (!intl) { | ||||||||||||||||||
| return undefined; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| const options = intl.DateTimeFormat().resolvedOptions(); | ||||||||||||||||||
|
|
||||||||||||||||||
| return { | ||||||||||||||||||
| locale: options.locale, | ||||||||||||||||||
| timezone: options.timeZone, | ||||||||||||||||||
| calendar: options.calendar, | ||||||||||||||||||
| }; | ||||||||||||||||||
| } catch { | ||||||||||||||||||
| // Ignore errors | ||||||||||||||||||
|
Comment on lines
+54
to
+57
|
||||||||||||||||||
| calendar: options.calendar, | |
| }; | |
| } catch { | |
| // Ignore errors | |
| }; | |
| } catch { | |
| // Ignore errors | |
| // Ignore errors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
q: Is that the only sdk where we need to add this separately?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I checked the other SDKs and they don't override the browser's default integrations. Only the angular one does.