Skip to content

Commit 0d30afc

Browse files
author
Luca Forstner
authored
Merge pull request #8878 from getsentry/prepare-release/7.65.0
2 parents b30936c + 31979c6 commit 0d30afc

File tree

77 files changed

+1413
-1878
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1413
-1878
lines changed

.github/workflows/flaky-test-detector.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jobs:
3535
uses: actions/setup-node@v3
3636
with:
3737
node-version-file: 'package.json'
38+
cache: 'yarn'
3839
- name: Install dependencies
3940
run: yarn install --ignore-engines --frozen-lockfile
4041

.github/workflows/issue-package-label.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ jobs:
6262
"@sentry.serverless": {
6363
"label": "Package: Serverless"
6464
},
65-
"@sentry.svelte": {
66-
"label": "Package: svelte"
67-
},
6865
"@sentry.sveltekit": {
6966
"label": "Package: SvelteKit"
7067
},
68+
"@sentry.svelte": {
69+
"label": "Package: svelte"
70+
},
7171
"@sentry.vue": {
7272
"label": "Package: vue"
7373
},

.size-limit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module.exports = [
7575
{
7676
name: '@sentry/react (incl. Tracing, Replay) - Webpack (gzipped)',
7777
path: 'packages/react/build/esm/index.js',
78-
import: '{ init, BrowserTYracing, Replay }',
78+
import: '{ init, BrowserTracing, Replay }',
7979
gzip: true,
8080
limit: '80 KB',
8181
},

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
## 7.65.0
8+
9+
- build: Remove build-specific polyfills (#8809)
10+
- build(deps): bump protobufjs from 6.11.3 to 6.11.4 (#8822)
11+
- deps(sveltekit): Bump `@sentry/vite-plugin` (#8877)
12+
- feat(core): Introduce `Sentry.startActiveSpan` and `Sentry.startSpan` (#8803)
13+
- fix: Memoize `AsyncLocalStorage` instance (#8831)
14+
- fix(nextjs): Check for validity of API route handler signature (#8811)
15+
- fix(nextjs): Fix `requestAsyncStorageShim` path resolution on windows (#8875)
16+
- fix(node): Log entire error object in `OnUncaughtException` (#8876)
17+
- fix(node): More relevant warning message when tracing extensions are missing (#8820)
18+
- fix(replay): Streamline session creation/refresh (#8813)
19+
- fix(sveltekit): Avoid invalidating data on route changes in `wrapServerLoadWithSentry` (#8801)
20+
- fix(tracing): Better guarding for performance observer (#8872)
21+
- ref(sveltekit): Remove custom client fetch instrumentation and use default instrumentation (#8802)
22+
- ref(tracing-internal): Deprecate `tracePropagationTargets` in `BrowserTracing` (#8874)
23+
724
## 7.64.0
825

926
- feat(core): Add setMeasurement export (#8791)

packages/angular-ivy/package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,16 @@
6161
"volta": {
6262
"extends": "../../package.json"
6363
},
64-
"sideEffects": false
64+
"sideEffects": false,
65+
"nx": {
66+
"targets": {
67+
"build:transpile": {
68+
"dependsOn": [
69+
"^build:transpile",
70+
"^build:transpile:uncached",
71+
"^build:types"
72+
]
73+
}
74+
}
75+
}
6576
}

packages/angular/package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,16 @@
6565
"volta": {
6666
"extends": "../../package.json"
6767
},
68-
"sideEffects": false
68+
"sideEffects": false,
69+
"nx": {
70+
"targets": {
71+
"build:transpile": {
72+
"dependsOn": [
73+
"^build:transpile",
74+
"^build:transpile:uncached",
75+
"^build:types"
76+
]
77+
}
78+
}
79+
}
6980
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window.Replay = new Sentry.Replay({
5+
flushMinDelay: 200,
6+
flushMaxDelay: 200,
7+
minReplayDuration: 0,
8+
});
9+
10+
Sentry.init({
11+
dsn: 'https://[email protected]/1337',
12+
sampleRate: 1,
13+
replaysSessionSampleRate: 0.0,
14+
replaysOnErrorSampleRate: 1.0,
15+
16+
integrations: [window.Replay],
17+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<button onclick="console.log('Test log 1')" id="button1">Click me</button>
8+
<button onclick="Sentry.captureException('test error')" id="buttonError">Click me</button>
9+
</body>
10+
</html>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../utils/fixtures';
4+
import {
5+
getReplaySnapshot,
6+
shouldSkipReplayTest,
7+
waitForReplayRequest,
8+
waitForReplayRunning,
9+
} from '../../../utils/replayHelpers';
10+
11+
sentryTest('continues buffer session in session mode after error & reload', async ({ getLocalTestPath, page }) => {
12+
if (shouldSkipReplayTest()) {
13+
sentryTest.skip();
14+
}
15+
16+
const reqPromise1 = waitForReplayRequest(page, 0);
17+
18+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
19+
return route.fulfill({
20+
status: 200,
21+
contentType: 'application/json',
22+
body: JSON.stringify({ id: 'test-id' }),
23+
});
24+
});
25+
26+
const url = await getLocalTestPath({ testDir: __dirname });
27+
28+
await page.goto(url);
29+
30+
// buffer session captures an error & switches to session mode
31+
await page.click('#buttonError');
32+
await new Promise(resolve => setTimeout(resolve, 300));
33+
await reqPromise1;
34+
35+
await waitForReplayRunning(page);
36+
const replay1 = await getReplaySnapshot(page);
37+
38+
expect(replay1.recordingMode).toEqual('session');
39+
expect(replay1.session?.sampled).toEqual('buffer');
40+
expect(replay1.session?.segmentId).toBeGreaterThan(0);
41+
42+
// Reload to ensure the session is correctly recovered from sessionStorage
43+
await page.reload();
44+
45+
await waitForReplayRunning(page);
46+
const replay2 = await getReplaySnapshot(page);
47+
48+
expect(replay2.recordingMode).toEqual('session');
49+
expect(replay2.session?.sampled).toEqual('buffer');
50+
expect(replay2.session?.segmentId).toBeGreaterThan(0);
51+
});

packages/browser-integration-tests/suites/replay/customEvents/test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ sentryTest(
8888

8989
const reqPromise0 = waitForReplayRequest(page, 0);
9090
const reqPromise1 = waitForReplayRequest(page, 1);
91+
const reqPromise2 = waitForReplayRequest(page, 2);
92+
const reqPromise3 = waitForReplayRequest(page, 3);
9193

9294
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
9395
return route.fulfill({
@@ -103,8 +105,6 @@ sentryTest(
103105
await reqPromise0;
104106

105107
await page.click('#error');
106-
await page.click('#img');
107-
await page.click('.sentry-unmask');
108108
await forceFlushReplay();
109109
const req1 = await reqPromise1;
110110
const content1 = getReplayRecordingContent(req1);
@@ -131,7 +131,11 @@ sentryTest(
131131
]),
132132
);
133133

134-
expect(content1.breadcrumbs).toEqual(
134+
await page.click('#img');
135+
await forceFlushReplay();
136+
const req2 = await reqPromise2;
137+
const content2 = getReplayRecordingContent(req2);
138+
expect(content2.breadcrumbs).toEqual(
135139
expect.arrayContaining([
136140
{
137141
...expectedClickBreadcrumb,
@@ -151,7 +155,11 @@ sentryTest(
151155
]),
152156
);
153157

154-
expect(content1.breadcrumbs).toEqual(
158+
await page.click('.sentry-unmask');
159+
await forceFlushReplay();
160+
const req3 = await reqPromise3;
161+
const content3 = getReplayRecordingContent(req3);
162+
expect(content3.breadcrumbs).toEqual(
155163
expect.arrayContaining([
156164
{
157165
...expectedClickBreadcrumb,

packages/core/src/hub.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,19 @@ export class Hub implements HubInterface {
374374
const result = this._callExtensionMethod<Transaction>('startTransaction', context, customSamplingContext);
375375

376376
if (__DEBUG_BUILD__ && !result) {
377-
// eslint-disable-next-line no-console
378-
console.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
377+
const client = this.getClient();
378+
if (!client) {
379+
// eslint-disable-next-line no-console
380+
console.warn(
381+
"Tracing extension 'startTransaction' is missing. You should 'init' the SDK before calling 'startTransaction'",
382+
);
383+
} else {
384+
// eslint-disable-next-line no-console
385+
console.warn(`Tracing extension 'startTransaction' has not been added. Call 'addTracingExtensions' before calling 'init':
379386
Sentry.addTracingExtensions();
380387
Sentry.init({...});
381388
`);
389+
}
382390
}
383391

384392
return result;

packages/core/src/tracing/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ export { extractTraceparentData, getActiveTransaction } from './utils';
66
// eslint-disable-next-line deprecation/deprecation
77
export { SpanStatus } from './spanstatus';
88
export type { SpanStatusType } from './span';
9-
export { trace } from './trace';
9+
export { trace, getActiveSpan, startActiveSpan, startSpan } from './trace';
1010
export { getDynamicSamplingContextFromClient } from './dynamicSamplingContext';
1111
export { setMeasurement } from './measurement';

packages/core/src/tracing/trace.ts

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ export function trace<T>(
3434

3535
const parentSpan = scope.getSpan();
3636

37-
function getActiveSpan(): Span | undefined {
37+
function startActiveSpan(): Span | undefined {
3838
if (!hasTracingEnabled()) {
3939
return undefined;
4040
}
4141
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
4242
}
4343

44-
const activeSpan = getActiveSpan();
44+
const activeSpan = startActiveSpan();
4545
scope.setSpan(activeSpan);
4646

4747
function finishAndSetSpan(): void {
@@ -76,3 +76,100 @@ export function trace<T>(
7676

7777
return maybePromiseResult;
7878
}
79+
80+
/**
81+
* Wraps a function with a transaction/span and finishes the span after the function is done.
82+
* The created span is the active span and will be used as parent by other spans created inside the function
83+
* and can be accessed via `Sentry.getSpan()`, as long as the function is executed while the scope is active.
84+
*
85+
* If you want to create a span that is not set as active, use {@link startSpan}.
86+
*
87+
* Note that if you have not enabled tracing extensions via `addTracingExtensions`
88+
* or you didn't set `tracesSampleRate`, this function will not generate spans
89+
* and the `span` returned from the callback will be undefined.
90+
*/
91+
export function startActiveSpan<T>(context: TransactionContext, callback: (span: Span | undefined) => T): T {
92+
const ctx = { ...context };
93+
// If a name is set and a description is not, set the description to the name.
94+
if (ctx.name !== undefined && ctx.description === undefined) {
95+
ctx.description = ctx.name;
96+
}
97+
98+
const hub = getCurrentHub();
99+
const scope = hub.getScope();
100+
101+
const parentSpan = scope.getSpan();
102+
103+
function startActiveSpan(): Span | undefined {
104+
if (!hasTracingEnabled()) {
105+
return undefined;
106+
}
107+
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
108+
}
109+
110+
const activeSpan = startActiveSpan();
111+
scope.setSpan(activeSpan);
112+
113+
function finishAndSetSpan(): void {
114+
activeSpan && activeSpan.finish();
115+
hub.getScope().setSpan(parentSpan);
116+
}
117+
118+
let maybePromiseResult: T;
119+
try {
120+
maybePromiseResult = callback(activeSpan);
121+
} catch (e) {
122+
activeSpan && activeSpan.setStatus('internal_error');
123+
finishAndSetSpan();
124+
throw e;
125+
}
126+
127+
if (isThenable(maybePromiseResult)) {
128+
Promise.resolve(maybePromiseResult).then(
129+
() => {
130+
finishAndSetSpan();
131+
},
132+
() => {
133+
activeSpan && activeSpan.setStatus('internal_error');
134+
finishAndSetSpan();
135+
},
136+
);
137+
} else {
138+
finishAndSetSpan();
139+
}
140+
141+
return maybePromiseResult;
142+
}
143+
144+
/**
145+
* Creates a span. This span is not set as active, so will not get automatic instrumentation spans
146+
* as children or be able to be accessed via `Sentry.getSpan()`.
147+
*
148+
* If you want to create a span that is set as active, use {@link startActiveSpan}.
149+
*
150+
* Note that if you have not enabled tracing extensions via `addTracingExtensions`
151+
* or you didn't set `tracesSampleRate` or `tracesSampler`, this function will not generate spans
152+
* and the `span` returned from the callback will be undefined.
153+
*/
154+
export function startSpan(context: TransactionContext): Span | undefined {
155+
if (!hasTracingEnabled()) {
156+
return undefined;
157+
}
158+
159+
const ctx = { ...context };
160+
// If a name is set and a description is not, set the description to the name.
161+
if (ctx.name !== undefined && ctx.description === undefined) {
162+
ctx.description = ctx.name;
163+
}
164+
165+
const hub = getCurrentHub();
166+
const parentSpan = getActiveSpan();
167+
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
168+
}
169+
170+
/**
171+
* Returns the currently active span.
172+
*/
173+
export function getActiveSpan(): Span | undefined {
174+
return getCurrentHub().getScope().getSpan();
175+
}

0 commit comments

Comments
 (0)