Skip to content

Commit b8691d3

Browse files
authored
v5 migration docs (getsentry#1959)
Fix `@sentry/integrations` package
1 parent 7bbd283 commit b8691d3

File tree

16 files changed

+242
-200
lines changed

16 files changed

+242
-200
lines changed

.craft.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ targets:
99
- name: github
1010
includeNames: /^sentry-.*$/
1111
- name: gcs
12-
includeNames: /^bundle\..*$/
12+
includeNames: /*\.js.*$/
1313
bucket: sentry-js-sdk
1414
paths:
1515
- path: /{{version}}/

MIGRATION.md

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Upgrading from 4.x to 5.x
2+
3+
In this version upgrade, there are a few breaking changes. This guide should help you update your code accordingly.
4+
5+
## Integrations
6+
7+
We moved optional integrations into their own package, called `@sentry/integrations`. Also, we made a few default
8+
integrations now optional. This is probably the biggest breaking change regarding the upgrade.
9+
10+
Integrations that are now opt-in and were default before:
11+
12+
- Dedupe (responsible for sending the same error only once)
13+
- ExtraErrorData (responsible for doing fancy magic, trying to extract data out of the error object using any
14+
non-standard keys)
15+
16+
Integrations that were pluggable/optional before, that also live in this package:
17+
18+
- Angular (browser)
19+
- Debug (browser/node)
20+
- Ember (browser)
21+
- ReportingObserver (browser)
22+
- RewriteFrames (browser/node)
23+
- Transaction (browser/node)
24+
- Vue (browser)
25+
26+
### How to use `@sentry/integrations`?
27+
28+
Lets start with the approach if you install `@sentry/browser` / `@sentry/electron` with `npm` or `yarn`.
29+
30+
Given you have a `Vue` application running, in order to use the `Vue` integration you need to do the following:
31+
32+
With `4.x`:
33+
34+
```js
35+
import * as Sentry from '@sentry/browser';
36+
37+
Sentry.init({
38+
dsn: '___PUBLIC_DSN___',
39+
integrations: [
40+
new Sentry.Integrations.Vue({
41+
Vue,
42+
attachProps: true,
43+
}),
44+
],
45+
});
46+
```
47+
48+
With `5.x` you need to install `@sentry/integrations` and change the import.
49+
50+
```js
51+
import * as Sentry from '@sentry/browser';
52+
import * as Integrations from '@sentry/integrations';
53+
54+
Sentry.init({
55+
dsn: '___PUBLIC_DSN___',
56+
integrations: [
57+
new Integrations.Vue({
58+
Vue,
59+
attachProps: true,
60+
}),
61+
],
62+
});
63+
```
64+
65+
In case you are using the CDN version or the Loader, we provide a standalone file for every integration, you can use it
66+
like this:
67+
68+
```html
69+
<!-- Note that we now also provide a es6 build only -->
70+
<!-- <script src="https://browser.sentry-cdn.com/5.0.0/bundle.es6.min.js" crossorigin="anonymous"></script> -->
71+
<script src="https://browser.sentry-cdn.com/5.0.0/bundle.min.js" crossorigin="anonymous"></script>
72+
73+
<!-- If you include the integration it will be available under Sentry.Integrations.Vue -->
74+
<script src="https://browser.sentry-cdn.com/5.0.0/vue.min.js" crossorigin="anonymous"></script>
75+
76+
<script>
77+
Sentry.init({
78+
dsn: '___PUBLIC_DSN___',
79+
integrations: [
80+
new Sentry.Integrations.Vue({
81+
Vue,
82+
attachProps: true,
83+
}),
84+
],
85+
});
86+
</script>
87+
```
88+
89+
## New Scope functions
90+
91+
We realized how annoying it is to set a whole object using `setExtra`, that's why there are now a few new methods on the
92+
`Scope`.
93+
94+
```typescript
95+
setTags(tags: { [key: string]: string }): this;
96+
setExtras(extras: { [key: string]: any }): this;
97+
clearBreadcrumbs(): this;
98+
```
99+
100+
So you can do this now:
101+
102+
```js
103+
// New in 5.x setExtras
104+
Sentry.withScope(scope => {
105+
scope.setExtras(errorInfo);
106+
Sentry.captureException(error);
107+
});
108+
109+
// vs. 4.x
110+
Sentry.withScope(scope => {
111+
Object.keys(errorInfo).forEach(key => {
112+
scope.setExtra(key, errorInfo[key]);
113+
});
114+
Sentry.captureException(error);
115+
});
116+
```
117+
118+
## Less Async API
119+
120+
We removed a lot of the internal async code since in certain situations it generated a lot of memory pressure. This
121+
really only affects you if you where either using the `BrowserClient` or `NodeClient` directly.
122+
123+
So all the `capture*` functions now instead of returning `Promise<Response>` return `string | undefined`. `string` in
124+
this case is the `event_id`, in case the event will not be sent because of filtering it will return `undefined`.
125+
126+
## `close` vs. `flush`
127+
128+
In `4.x` we had both `close` and `flush` on the `Client` draining the internal queue of events, helpful when you were
129+
using `@sentry/node` on a serverless infrastructure.
130+
131+
Now `close` and `flush` work similar, with the difference that if you call `close` in addition to returing a `Promise`
132+
that you can await it also **disables** the client so it will not send any future events.

Makefile

-23
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,10 @@
1-
bump:
2-
yarn lerna version --exact --no-git-tag-version --no-push
3-
.PHONY: bump
4-
51
prepare-release:
62
yarn clean
73
yarn build
84
yarn lint
95
yarn test
106
.PHONY: prepare-release
117

12-
publish-npm:
13-
cd packages/browser; npm publish
14-
cd packages/core; npm publish
15-
cd packages/hub; npm publish
16-
cd packages/integrations; npm publish
17-
cd packages/minimal; npm publish
18-
cd packages/node; npm publish
19-
# cd packages/types; npm publish
20-
# cd packages/typescript; npm publish
21-
cd packages/utils; npm publish
22-
.PHONY: publish-npm
23-
24-
publish-cdn:
25-
node scripts/browser-upload-cdn.js
26-
.PHONY: publish-cdn
27-
288
build-docs:
299
rm -rf ./docs
3010
yarn typedoc --options ./typedoc.js
@@ -39,6 +19,3 @@ publish-docs: build-docs
3919
git push origin gh-pages
4020
git checkout master
4121
.PHONY: publish-docs
42-
43-
release: bump prepare-release publish-npm publish-cdn
44-
.PHONY: release

packages/browser/examples/app.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ class HappyIntegration {
55
}
66

77
setupOnce() {
8-
Sentry.addGlobalEventProcessor(async event => {
9-
const self = getCurrentHub().getIntegration(HappyIntegration);
8+
Sentry.addGlobalEventProcessor(event => {
9+
const self = Sentry.getCurrentHub().getIntegration(HappyIntegration);
1010
// Run the integration ONLY when it was installed on the current Hub
1111
if (self) {
1212
if (event.message === 'Happy Message') {
@@ -19,7 +19,7 @@ class HappyIntegration {
1919
}
2020

2121
class HappyTransport extends Sentry.Transports.BaseTransport {
22-
captureEvent(event) {
22+
sendEvent(event) {
2323
console.log(
2424
`This is the place where you'd implement your own sending logic. It'd get url: ${this.url} and an event itself:`,
2525
event,
@@ -36,11 +36,11 @@ Sentry.init({
3636
dsn: 'https://363a337c11a64611be4845ad6e24f3ac@sentry.io/297378',
3737
// An array of strings or regexps that'll be used to ignore specific errors based on their type/message
3838
ignoreErrors: [/PickleRick_\d\d/, 'RangeError'],
39-
// // An array of strings or regexps that'll be used to ignore specific errors based on their origin url
39+
// An array of strings or regexps that'll be used to ignore specific errors based on their origin url
4040
blacklistUrls: ['external-lib.js'],
41-
// // An array of strings or regexps that'll be used to allow specific errors based on their origin url
41+
// An array of strings or regexps that'll be used to allow specific errors based on their origin url
4242
whitelistUrls: ['http://localhost:5000', 'https://browser.sentry-cdn'],
43-
// // Debug mode with valuable initialization/lifecycle informations.
43+
// Debug mode with valuable initialization/lifecycle informations.
4444
debug: true,
4545
// Whether SDK should be enabled or not.
4646
enabled: true,

packages/browser/src/index.ts

+11
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,22 @@ export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDi
3333
export { SDK_NAME, SDK_VERSION } from './version';
3434

3535
import { Integrations as CoreIntegrations } from '@sentry/core';
36+
import { getGlobalObject } from '@sentry/utils/misc';
3637

3738
import * as BrowserIntegrations from './integrations';
3839
import * as Transports from './transports';
3940

41+
let windowIntegrations = {};
42+
43+
// tslint:disable: no-unsafe-any
44+
const _window = getGlobalObject<Window>() as any;
45+
if (_window.Sentry && _window.Sentry.Integrations) {
46+
windowIntegrations = _window.Sentry.Integrations;
47+
}
48+
// tslint:enable: no-unsafe-any
49+
4050
const INTEGRATIONS = {
51+
...windowIntegrations,
4152
...CoreIntegrations,
4253
...BrowserIntegrations,
4354
};

packages/browser/src/loader.js

+18-17
Original file line numberDiff line numberDiff line change
@@ -150,26 +150,27 @@
150150
}
151151
}
152152

153-
// We don't want to _window.Sentry = _window.Sentry || { ... } since we want to make sure
154-
// that the first Sentry "instance" is our with onLoad
155-
_window[_namespace] = {
156-
onLoad: function (callback) {
157-
onLoadCallbacks.push(callback);
158-
if (lazy && !forceLoad) {
159-
return;
160-
}
161-
injectSdk(onLoadCallbacks);
162-
},
163-
forceLoad: function() {
164-
forceLoad = true;
165-
if (lazy) {
166-
setTimeout(function() {
167-
injectSdk(onLoadCallbacks);
168-
});
169-
}
153+
// We make sure we do not overwrite window.Sentry since there could be already integrations in there
154+
_window[_namespace] = _window[_namespace] || {};
155+
156+
_window[_namespace].onLoad = function (callback) {
157+
onLoadCallbacks.push(callback);
158+
if (lazy && !forceLoad) {
159+
return;
170160
}
161+
injectSdk(onLoadCallbacks);
171162
};
172163

164+
_window[_namespace].forceLoad = function() {
165+
forceLoad = true;
166+
if (lazy) {
167+
setTimeout(function() {
168+
injectSdk(onLoadCallbacks);
169+
});
170+
}
171+
};
172+
173+
173174
[
174175
'init',
175176
'addBreadcrumb',

packages/core/src/baseclient.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Scope } from '@sentry/hub';
2-
import { Client, Event, EventHint, Integration, IntegrationClass, Options, Severity } from '@sentry/types';
2+
import { Client, Event, EventHint, Integration, IntegrationClass, Options, SdkInfo, Severity } from '@sentry/types';
33
import { isPrimitive, isThenable } from '@sentry/utils/is';
44
import { logger } from '@sentry/utils/logger';
55
import { uuid4 } from '@sentry/utils/misc';
@@ -278,6 +278,8 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
278278
prepared.event_id = uuid4();
279279
}
280280

281+
this._addIntegrations(prepared.sdk);
282+
281283
// We prepare the result here with a resolved Event.
282284
let result = SyncPromise.resolve<Event | null>(prepared);
283285

@@ -291,6 +293,17 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
291293
return result;
292294
}
293295

296+
/**
297+
* This function adds all used integrations to the SDK info in the event.
298+
* @param sdkInfo The sdkInfo of the event that will be filled with all integrations.
299+
*/
300+
protected _addIntegrations(sdkInfo?: SdkInfo): void {
301+
const integrationsArray = Object.keys(this._integrations);
302+
if (sdkInfo && integrationsArray.length > 0) {
303+
sdkInfo.integrations = integrationsArray;
304+
}
305+
}
306+
294307
/**
295308
* Processes an event (either error or message) and sends it to Sentry.
296309
*

packages/core/src/integration.ts

+9-21
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ export function getIntegrationsToSetup(options: Options): Integration[] {
2121
// Leave only unique default integrations, that were not overridden with provided user integrations
2222
defaultIntegrations.forEach(defaultIntegration => {
2323
if (
24-
userIntegrationsNames.indexOf(getIntegrationName(defaultIntegration)) === -1 &&
25-
pickedIntegrationsNames.indexOf(getIntegrationName(defaultIntegration)) === -1
24+
userIntegrationsNames.indexOf(defaultIntegration.name) === -1 &&
25+
pickedIntegrationsNames.indexOf(defaultIntegration.name) === -1
2626
) {
2727
integrations.push(defaultIntegration);
28-
pickedIntegrationsNames.push(getIntegrationName(defaultIntegration));
28+
pickedIntegrationsNames.push(defaultIntegration.name);
2929
}
3030
});
3131

3232
// Don't add same user integration twice
3333
userIntegrations.forEach(userIntegration => {
34-
if (pickedIntegrationsNames.indexOf(getIntegrationName(userIntegration)) === -1) {
34+
if (pickedIntegrationsNames.indexOf(userIntegration.name) === -1) {
3535
integrations.push(userIntegration);
36-
pickedIntegrationsNames.push(getIntegrationName(userIntegration));
36+
pickedIntegrationsNames.push(userIntegration.name);
3737
}
3838
});
3939
} else if (typeof userIntegrations === 'function') {
@@ -48,12 +48,12 @@ export function getIntegrationsToSetup(options: Options): Integration[] {
4848

4949
/** Setup given integration */
5050
export function setupIntegration(integration: Integration): void {
51-
if (installedIntegrations.indexOf(getIntegrationName(integration)) !== -1) {
51+
if (installedIntegrations.indexOf(integration.name) !== -1) {
5252
return;
5353
}
5454
integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
55-
installedIntegrations.push(getIntegrationName(integration));
56-
logger.log(`Integration installed: ${getIntegrationName(integration)}`);
55+
installedIntegrations.push(integration.name);
56+
logger.log(`Integration installed: ${integration.name}`);
5757
}
5858

5959
/**
@@ -65,20 +65,8 @@ export function setupIntegration(integration: Integration): void {
6565
export function setupIntegrations<O extends Options>(options: O): IntegrationIndex {
6666
const integrations: IntegrationIndex = {};
6767
getIntegrationsToSetup(options).forEach(integration => {
68-
integrations[getIntegrationName(integration)] = integration;
68+
integrations[integration.name] = integration;
6969
setupIntegration(integration);
7070
});
7171
return integrations;
7272
}
73-
74-
/**
75-
* Returns the integration static id.
76-
* @param integration Integration to retrieve id
77-
*/
78-
function getIntegrationName(integration: Integration): string {
79-
/**
80-
* @depracted
81-
*/
82-
// tslint:disable-next-line:no-unsafe-any
83-
return (integration as any).constructor.id || integration.name;
84-
}

0 commit comments

Comments
 (0)