Skip to content

Commit 264675d

Browse files
authored
Merge pull request #12272 from getsentry/prepare-release/8.6.00
meta: Add Changelog entry for 8.6.0
2 parents d3f3313 + 787a29b commit 264675d

File tree

113 files changed

+1961
-890
lines changed

Some content is hidden

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

113 files changed

+1961
-890
lines changed

.github/workflows/build.yml

+1
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,7 @@ jobs:
10091009
'nextjs-app-dir',
10101010
'nextjs-14',
10111011
'nextjs-15',
1012+
'react-19',
10121013
'react-create-hash-router',
10131014
'react-router-6-use-routes',
10141015
'react-router-5',

CHANGELOG.md

+54
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,60 @@
44

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

7+
## 8.6.0
8+
9+
### Important Changes
10+
11+
- **feat(metrics): Add `timings` method to metrics (#12226)**
12+
13+
This introduces a new method, `metrics.timing()`, which can be used in two ways:
14+
15+
1. With a numeric value, to simplify creating a distribution metric. This will default to `second` as unit:
16+
17+
```js
18+
Sentry.metrics.timing('myMetric', 100);
19+
```
20+
21+
2. With a callback, which will wrap the duration of the callback. This can accept a sync or async callback. It will
22+
create an inactive span around the callback and at the end emit a metric with the duration of the span in seconds:
23+
24+
```js
25+
const returnValue = Sentry.metrics.timing('myMetric', measureThisFunction);
26+
```
27+
28+
- **feat(react): Add `Sentry.reactErrorHandler` (#12147)**
29+
30+
This PR introduces `Sentry.reactErrorHandler`, which you can use in React 19 as follows:
31+
32+
```js
33+
import * as Sentry from '@sentry/react';
34+
import { hydrateRoot } from 'react-dom/client';
35+
36+
ReactDOM.hydrateRoot(
37+
document.getElementById('root'),
38+
<React.StrictMode>
39+
<App />
40+
</React.StrictMode>,
41+
{
42+
onUncaughtError: Sentry.reactErrorHandler(),
43+
onCaughtError: Sentry.reactErrorHandler((error, errorInfo) => {
44+
// optional callback if users want custom config.
45+
}),
46+
},
47+
);
48+
```
49+
50+
For more details, take a look at [the PR](https://github.com/getsentry/sentry-javascript/pull/12147). Our
51+
documentation will be updated soon!
52+
53+
### Other Changes
54+
55+
- feat(sveltekit): Add request data to server-side events (#12254)
56+
- fix(core): Pass in cron monitor config correctly (#12248)
57+
- fix(nextjs): Don't capture suspense errors in server components (#12261)
58+
- fix(tracing): Ensure sent spans are limited to 1000 (#12252)
59+
- ref(core): Use versioned carrier on global object (#12206)
60+
761
## 8.5.0
862

963
### Important Changes
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,139 @@
1-
!function(n,e,r,t,i,o,a,c,s){for(var u=s,f=0;f<document.scripts.length;f++)if(document.scripts[f].src.indexOf(o)>-1){u&&"no"===document.scripts[f].getAttribute("data-lazy")&&(u=!1);break}var p=[];function l(n){return"e"in n}function d(n){return"p"in n}function _(n){return"f"in n}var v=[];function y(n){u&&(l(n)||d(n)||_(n)&&n.f.indexOf("capture")>-1||_(n)&&n.f.indexOf("showReportDialog")>-1)&&m(),v.push(n)}function g(){y({e:[].slice.call(arguments)})}function h(n){y({p:n})}function E(){try{n.SENTRY_SDK_SOURCE="loader";var e=n[i],o=e.init;e.init=function(i){n.removeEventListener(r,g),n.removeEventListener(t,h);var a=c;for(var s in i)Object.prototype.hasOwnProperty.call(i,s)&&(a[s]=i[s]);!function(n,e){var r=n.integrations||[];if(!Array.isArray(r))return;var t=r.map((function(n){return n.name}));n.tracesSampleRate&&-1===t.indexOf("BrowserTracing")&&(e.BrowserTracing?r.push(new e.BrowserTracing):e.browserTracingIntegration&&r.push(e.browserTracingIntegration()));(n.replaysSessionSampleRate||n.replaysOnErrorSampleRate)&&-1===t.indexOf("Replay")&&(e.Replay?r.push(new e.Replay):e.replayIntegration&&r.push(e.replayIntegration()));n.integrations=r}(a,e),o(a)},setTimeout((function(){return function(e){try{"function"==typeof n.sentryOnLoad&&(n.sentryOnLoad(),n.sentryOnLoad=void 0);for(var r=0;r<p.length;r++)"function"==typeof p[r]&&p[r]();p.splice(0);for(r=0;r<v.length;r++){_(o=v[r])&&"init"===o.f&&e.init.apply(e,o.a)}L()||e.init();var t=n.onerror,i=n.onunhandledrejection;for(r=0;r<v.length;r++){var o;if(_(o=v[r])){if("init"===o.f)continue;e[o.f].apply(e,o.a)}else l(o)&&t?t.apply(n,o.e):d(o)&&i&&i.apply(n,[o.p])}}catch(n){console.error(n)}}(e)}))}catch(n){console.error(n)}}var O=!1;function m(){if(!O){O=!0;var n=e.scripts[0],r=e.createElement("script");r.src=a,r.crossOrigin="anonymous",r.addEventListener("load",E,{once:!0,passive:!0}),n.parentNode.insertBefore(r,n)}}function L(){var e=n.__SENTRY__;return!(void 0===e||!e.hub||!e.hub.getClient())}n[i]=n[i]||{},n[i].onLoad=function(n){L()?n():p.push(n)},n[i].forceLoad=function(){setTimeout((function(){m()}))},["init","addBreadcrumb","captureMessage","captureException","captureEvent","configureScope","withScope","showReportDialog"].forEach((function(e){n[i][e]=function(){y({f:e,a:arguments})}})),n.addEventListener(r,g),n.addEventListener(t,h),u||setTimeout((function(){m()}))}
2-
(
1+
!(function (n, e, r, t, i, o, a, c, s) {
2+
for (var u = s, f = 0; f < document.scripts.length; f++)
3+
if (document.scripts[f].src.indexOf(o) > -1) {
4+
u && 'no' === document.scripts[f].getAttribute('data-lazy') && (u = !1);
5+
break;
6+
}
7+
var p = [];
8+
function l(n) {
9+
return 'e' in n;
10+
}
11+
function d(n) {
12+
return 'p' in n;
13+
}
14+
function _(n) {
15+
return 'f' in n;
16+
}
17+
var v = [];
18+
function y(n) {
19+
u &&
20+
(l(n) || d(n) || (_(n) && n.f.indexOf('capture') > -1) || (_(n) && n.f.indexOf('showReportDialog') > -1)) &&
21+
m(),
22+
v.push(n);
23+
}
24+
function g() {
25+
y({ e: [].slice.call(arguments) });
26+
}
27+
function h(n) {
28+
y({ p: n });
29+
}
30+
function E() {
31+
try {
32+
n.SENTRY_SDK_SOURCE = 'loader';
33+
var e = n[i],
34+
o = e.init;
35+
(e.init = function (i) {
36+
n.removeEventListener(r, g), n.removeEventListener(t, h);
37+
var a = c;
38+
for (var s in i) Object.prototype.hasOwnProperty.call(i, s) && (a[s] = i[s]);
39+
!(function (n, e) {
40+
var r = n.integrations || [];
41+
if (!Array.isArray(r)) return;
42+
var t = r.map(function (n) {
43+
return n.name;
44+
});
45+
n.tracesSampleRate &&
46+
-1 === t.indexOf('BrowserTracing') &&
47+
(e.BrowserTracing
48+
? r.push(new e.BrowserTracing())
49+
: e.browserTracingIntegration && r.push(e.browserTracingIntegration()));
50+
(n.replaysSessionSampleRate || n.replaysOnErrorSampleRate) &&
51+
-1 === t.indexOf('Replay') &&
52+
(e.Replay ? r.push(new e.Replay()) : e.replayIntegration && r.push(e.replayIntegration()));
53+
n.integrations = r;
54+
})(a, e),
55+
o(a);
56+
}),
57+
setTimeout(function () {
58+
return (function (e) {
59+
try {
60+
'function' == typeof n.sentryOnLoad && (n.sentryOnLoad(), (n.sentryOnLoad = void 0));
61+
for (var r = 0; r < p.length; r++) 'function' == typeof p[r] && p[r]();
62+
p.splice(0);
63+
for (r = 0; r < v.length; r++) {
64+
_((o = v[r])) && 'init' === o.f && e.init.apply(e, o.a);
65+
}
66+
L() || e.init();
67+
var t = n.onerror,
68+
i = n.onunhandledrejection;
69+
for (r = 0; r < v.length; r++) {
70+
var o;
71+
if (_((o = v[r]))) {
72+
if ('init' === o.f) continue;
73+
e[o.f].apply(e, o.a);
74+
} else l(o) && t ? t.apply(n, o.e) : d(o) && i && i.apply(n, [o.p]);
75+
}
76+
} catch (n) {
77+
console.error(n);
78+
}
79+
})(e);
80+
});
81+
} catch (n) {
82+
console.error(n);
83+
}
84+
}
85+
var O = !1;
86+
function m() {
87+
if (!O) {
88+
O = !0;
89+
var n = e.scripts[0],
90+
r = e.createElement('script');
91+
(r.src = a),
92+
(r.crossOrigin = 'anonymous'),
93+
r.addEventListener('load', E, { once: !0, passive: !0 }),
94+
n.parentNode.insertBefore(r, n);
95+
}
96+
}
97+
function L() {
98+
var e = n.__SENTRY__;
99+
100+
// TODO: This is a temporary hack to make the loader script compatible with the versioned
101+
// carrier. This needs still needs to be added to the actual loader script before we
102+
// release the loader for v8!
103+
var v = e && e.version && e[e.version];
104+
105+
return !(void 0 === e || !e.hub || !e.hub.getClient()) || !!v;
106+
}
107+
(n[i] = n[i] || {}),
108+
(n[i].onLoad = function (n) {
109+
L() ? n() : p.push(n);
110+
}),
111+
(n[i].forceLoad = function () {
112+
setTimeout(function () {
113+
m();
114+
});
115+
}),
116+
[
117+
'init',
118+
'addBreadcrumb',
119+
'captureMessage',
120+
'captureException',
121+
'captureEvent',
122+
'configureScope',
123+
'withScope',
124+
'showReportDialog',
125+
].forEach(function (e) {
126+
n[i][e] = function () {
127+
y({ f: e, a: arguments });
128+
};
129+
}),
130+
n.addEventListener(r, g),
131+
n.addEventListener(t, h),
132+
u ||
133+
setTimeout(function () {
134+
m();
135+
});
136+
})(
3137
window,
4138
document,
5139
'error',
@@ -8,5 +142,5 @@
8142
'loader.js',
9143
__LOADER_BUNDLE__,
10144
__LOADER_OPTIONS__,
11-
__LOADER_LAZY__
145+
__LOADER_LAZY__,
12146
);

dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@ window.sentryIsLoaded = () => {
1616
const __sentry = window.__SENTRY__;
1717

1818
// If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked
19-
return !!(!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient());
19+
return !!(
20+
!(typeof __sentry === 'undefined') &&
21+
__sentry.version &&
22+
!!__sentry[__sentry.version]
23+
);
2024
};

dev-packages/browser-integration-tests/suites/metrics/metricsEvent/init.js

+17
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,20 @@ Sentry.metrics.gauge('gauge', 5);
1515
Sentry.metrics.gauge('gauge', '15');
1616
Sentry.metrics.set('set', 'nope');
1717
Sentry.metrics.set('set', 'another');
18+
19+
Sentry.metrics.timing('timing', 99, 'hour');
20+
Sentry.metrics.timing('timingSync', () => {
21+
sleepSync(200);
22+
});
23+
Sentry.metrics.timing('timingAsync', async () => {
24+
await new Promise(resolve => setTimeout(resolve, 200));
25+
});
26+
27+
function sleepSync(milliseconds) {
28+
var start = new Date().getTime();
29+
for (var i = 0; i < 1e7; i++) {
30+
if (new Date().getTime() - start > milliseconds) {
31+
break;
32+
}
33+
}
34+
}

dev-packages/browser-integration-tests/suites/metrics/metricsEvent/test.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@ sentryTest('collects metrics', async ({ getLocalTestUrl, page }) => {
1717
const statsdBuffer = await getFirstSentryEnvelopeRequest<Uint8Array>(page, url, properEnvelopeRequestParser);
1818
const statsdString = new TextDecoder().decode(statsdBuffer);
1919
// Replace all the Txxxxxx to remove the timestamps
20-
const normalisedStatsdString = statsdString.replace(/T\d+\n?/g, 'T000000');
20+
const normalisedStatsdString = statsdString.replace(/T\d+\n?/g, 'T000000').trim();
2121

22-
expect(normalisedStatsdString).toEqual(
23-
'increment@none:6|c|T000000distribution@none:42:45|d|T000000gauge@none:15:5:15:20:2|g|T000000set@none:3387254:3443787523|s|T000000',
24-
);
22+
const parts = normalisedStatsdString.split('T000000');
23+
24+
expect(parts).toEqual([
25+
'increment@none:6|c|',
26+
'distribution@none:42:45|d|',
27+
'gauge@none:15:5:15:20:2|g|',
28+
'set@none:3387254:3443787523|s|',
29+
'timing@hour:99|d|',
30+
expect.stringMatching(/timingSync@second:0.(\d+)\|d\|/),
31+
expect.stringMatching(/timingAsync@second:0.(\d+)\|d\|/),
32+
'', // trailing element
33+
]);
2534
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
tracesSampleRate: 1.0,
8+
release: '1.0.0',
9+
autoSessionTracking: false,
10+
});
11+
12+
window.timingSync = () => {
13+
// Ensure we always have a wrapping span
14+
return Sentry.startSpan({ name: 'manual span' }, () => {
15+
return Sentry.metrics.timing('timingSync', () => {
16+
sleepSync(200);
17+
return 'sync done';
18+
});
19+
});
20+
};
21+
22+
window.timingAsync = () => {
23+
// Ensure we always have a wrapping span
24+
return Sentry.startSpan({ name: 'manual span' }, () => {
25+
return Sentry.metrics.timing('timingAsync', async () => {
26+
await new Promise(resolve => setTimeout(resolve, 200));
27+
return 'async done';
28+
});
29+
});
30+
};
31+
32+
function sleepSync(milliseconds) {
33+
var start = new Date().getTime();
34+
for (var i = 0; i < 1e7; i++) {
35+
if (new Date().getTime() - start > milliseconds) {
36+
break;
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)