Skip to content

Commit

Permalink
v5 migration docs (#1959)
Browse files Browse the repository at this point in the history
Fix `@sentry/integrations` package
  • Loading branch information
HazAT committed Mar 28, 2019
1 parent 7bbd283 commit b8691d3
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 200 deletions.
2 changes: 1 addition & 1 deletion .craft.yml
Expand Up @@ -9,7 +9,7 @@ targets:
- name: github
includeNames: /^sentry-.*$/
- name: gcs
includeNames: /^bundle\..*$/
includeNames: /*\.js.*$/
bucket: sentry-js-sdk
paths:
- path: /{{version}}/
Expand Down
132 changes: 132 additions & 0 deletions MIGRATION.md
@@ -0,0 +1,132 @@
# Upgrading from 4.x to 5.x

In this version upgrade, there are a few breaking changes. This guide should help you update your code accordingly.

## Integrations

We moved optional integrations into their own package, called `@sentry/integrations`. Also, we made a few default
integrations now optional. This is probably the biggest breaking change regarding the upgrade.

Integrations that are now opt-in and were default before:

- Dedupe (responsible for sending the same error only once)
- ExtraErrorData (responsible for doing fancy magic, trying to extract data out of the error object using any
non-standard keys)

Integrations that were pluggable/optional before, that also live in this package:

- Angular (browser)
- Debug (browser/node)
- Ember (browser)
- ReportingObserver (browser)
- RewriteFrames (browser/node)
- Transaction (browser/node)
- Vue (browser)

### How to use `@sentry/integrations`?

Lets start with the approach if you install `@sentry/browser` / `@sentry/electron` with `npm` or `yarn`.

Given you have a `Vue` application running, in order to use the `Vue` integration you need to do the following:

With `4.x`:

```js
import * as Sentry from '@sentry/browser';

Sentry.init({
dsn: '___PUBLIC_DSN___',
integrations: [
new Sentry.Integrations.Vue({
Vue,
attachProps: true,
}),
],
});
```

With `5.x` you need to install `@sentry/integrations` and change the import.

```js
import * as Sentry from '@sentry/browser';
import * as Integrations from '@sentry/integrations';

Sentry.init({
dsn: '___PUBLIC_DSN___',
integrations: [
new Integrations.Vue({
Vue,
attachProps: true,
}),
],
});
```

In case you are using the CDN version or the Loader, we provide a standalone file for every integration, you can use it
like this:

```html
<!-- Note that we now also provide a es6 build only -->
<!-- <script src="https://browser.sentry-cdn.com/5.0.0/bundle.es6.min.js" crossorigin="anonymous"></script> -->
<script src="https://browser.sentry-cdn.com/5.0.0/bundle.min.js" crossorigin="anonymous"></script>

<!-- If you include the integration it will be available under Sentry.Integrations.Vue -->
<script src="https://browser.sentry-cdn.com/5.0.0/vue.min.js" crossorigin="anonymous"></script>

<script>
Sentry.init({
dsn: '___PUBLIC_DSN___',
integrations: [
new Sentry.Integrations.Vue({
Vue,
attachProps: true,
}),
],
});
</script>
```

## New Scope functions

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
`Scope`.

```typescript
setTags(tags: { [key: string]: string }): this;
setExtras(extras: { [key: string]: any }): this;
clearBreadcrumbs(): this;
```

So you can do this now:

```js
// New in 5.x setExtras
Sentry.withScope(scope => {
scope.setExtras(errorInfo);
Sentry.captureException(error);
});

// vs. 4.x
Sentry.withScope(scope => {
Object.keys(errorInfo).forEach(key => {
scope.setExtra(key, errorInfo[key]);
});
Sentry.captureException(error);
});
```

## Less Async API

We removed a lot of the internal async code since in certain situations it generated a lot of memory pressure. This
really only affects you if you where either using the `BrowserClient` or `NodeClient` directly.

So all the `capture*` functions now instead of returning `Promise<Response>` return `string | undefined`. `string` in
this case is the `event_id`, in case the event will not be sent because of filtering it will return `undefined`.

## `close` vs. `flush`

In `4.x` we had both `close` and `flush` on the `Client` draining the internal queue of events, helpful when you were
using `@sentry/node` on a serverless infrastructure.

Now `close` and `flush` work similar, with the difference that if you call `close` in addition to returing a `Promise`
that you can await it also **disables** the client so it will not send any future events.
23 changes: 0 additions & 23 deletions Makefile
@@ -1,30 +1,10 @@
bump:
yarn lerna version --exact --no-git-tag-version --no-push
.PHONY: bump

prepare-release:
yarn clean
yarn build
yarn lint
yarn test
.PHONY: prepare-release

publish-npm:
cd packages/browser; npm publish
cd packages/core; npm publish
cd packages/hub; npm publish
cd packages/integrations; npm publish
cd packages/minimal; npm publish
cd packages/node; npm publish
# cd packages/types; npm publish
# cd packages/typescript; npm publish
cd packages/utils; npm publish
.PHONY: publish-npm

publish-cdn:
node scripts/browser-upload-cdn.js
.PHONY: publish-cdn

build-docs:
rm -rf ./docs
yarn typedoc --options ./typedoc.js
Expand All @@ -39,6 +19,3 @@ publish-docs: build-docs
git push origin gh-pages
git checkout master
.PHONY: publish-docs

release: bump prepare-release publish-npm publish-cdn
.PHONY: release
12 changes: 6 additions & 6 deletions packages/browser/examples/app.js
Expand Up @@ -5,8 +5,8 @@ class HappyIntegration {
}

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

class HappyTransport extends Sentry.Transports.BaseTransport {
captureEvent(event) {
sendEvent(event) {
console.log(
`This is the place where you'd implement your own sending logic. It'd get url: ${this.url} and an event itself:`,
event,
Expand All @@ -36,11 +36,11 @@ Sentry.init({
dsn: 'https://363a337c11a64611be4845ad6e24f3ac@sentry.io/297378',
// An array of strings or regexps that'll be used to ignore specific errors based on their type/message
ignoreErrors: [/PickleRick_\d\d/, 'RangeError'],
// // An array of strings or regexps that'll be used to ignore specific errors based on their origin url
// An array of strings or regexps that'll be used to ignore specific errors based on their origin url
blacklistUrls: ['external-lib.js'],
// // An array of strings or regexps that'll be used to allow specific errors based on their origin url
// An array of strings or regexps that'll be used to allow specific errors based on their origin url
whitelistUrls: ['http://localhost:5000', 'https://browser.sentry-cdn'],
// // Debug mode with valuable initialization/lifecycle informations.
// Debug mode with valuable initialization/lifecycle informations.
debug: true,
// Whether SDK should be enabled or not.
enabled: true,
Expand Down
11 changes: 11 additions & 0 deletions packages/browser/src/index.ts
Expand Up @@ -33,11 +33,22 @@ export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDi
export { SDK_NAME, SDK_VERSION } from './version';

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

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

let windowIntegrations = {};

// tslint:disable: no-unsafe-any
const _window = getGlobalObject<Window>() as any;
if (_window.Sentry && _window.Sentry.Integrations) {
windowIntegrations = _window.Sentry.Integrations;
}
// tslint:enable: no-unsafe-any

const INTEGRATIONS = {
...windowIntegrations,
...CoreIntegrations,
...BrowserIntegrations,
};
Expand Down
35 changes: 18 additions & 17 deletions packages/browser/src/loader.js
Expand Up @@ -150,26 +150,27 @@
}
}

// We don't want to _window.Sentry = _window.Sentry || { ... } since we want to make sure
// that the first Sentry "instance" is our with onLoad
_window[_namespace] = {
onLoad: function (callback) {
onLoadCallbacks.push(callback);
if (lazy && !forceLoad) {
return;
}
injectSdk(onLoadCallbacks);
},
forceLoad: function() {
forceLoad = true;
if (lazy) {
setTimeout(function() {
injectSdk(onLoadCallbacks);
});
}
// We make sure we do not overwrite window.Sentry since there could be already integrations in there
_window[_namespace] = _window[_namespace] || {};

_window[_namespace].onLoad = function (callback) {
onLoadCallbacks.push(callback);
if (lazy && !forceLoad) {
return;
}
injectSdk(onLoadCallbacks);
};

_window[_namespace].forceLoad = function() {
forceLoad = true;
if (lazy) {
setTimeout(function() {
injectSdk(onLoadCallbacks);
});
}
};


[
'init',
'addBreadcrumb',
Expand Down
15 changes: 14 additions & 1 deletion packages/core/src/baseclient.ts
@@ -1,5 +1,5 @@
import { Scope } from '@sentry/hub';
import { Client, Event, EventHint, Integration, IntegrationClass, Options, Severity } from '@sentry/types';
import { Client, Event, EventHint, Integration, IntegrationClass, Options, SdkInfo, Severity } from '@sentry/types';
import { isPrimitive, isThenable } from '@sentry/utils/is';
import { logger } from '@sentry/utils/logger';
import { uuid4 } from '@sentry/utils/misc';
Expand Down Expand Up @@ -278,6 +278,8 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
prepared.event_id = uuid4();
}

this._addIntegrations(prepared.sdk);

// We prepare the result here with a resolved Event.
let result = SyncPromise.resolve<Event | null>(prepared);

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

/**
* This function adds all used integrations to the SDK info in the event.
* @param sdkInfo The sdkInfo of the event that will be filled with all integrations.
*/
protected _addIntegrations(sdkInfo?: SdkInfo): void {
const integrationsArray = Object.keys(this._integrations);
if (sdkInfo && integrationsArray.length > 0) {
sdkInfo.integrations = integrationsArray;
}
}

/**
* Processes an event (either error or message) and sends it to Sentry.
*
Expand Down
30 changes: 9 additions & 21 deletions packages/core/src/integration.ts
Expand Up @@ -21,19 +21,19 @@ export function getIntegrationsToSetup(options: Options): Integration[] {
// Leave only unique default integrations, that were not overridden with provided user integrations
defaultIntegrations.forEach(defaultIntegration => {
if (
userIntegrationsNames.indexOf(getIntegrationName(defaultIntegration)) === -1 &&
pickedIntegrationsNames.indexOf(getIntegrationName(defaultIntegration)) === -1
userIntegrationsNames.indexOf(defaultIntegration.name) === -1 &&
pickedIntegrationsNames.indexOf(defaultIntegration.name) === -1
) {
integrations.push(defaultIntegration);
pickedIntegrationsNames.push(getIntegrationName(defaultIntegration));
pickedIntegrationsNames.push(defaultIntegration.name);
}
});

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

/** Setup given integration */
export function setupIntegration(integration: Integration): void {
if (installedIntegrations.indexOf(getIntegrationName(integration)) !== -1) {
if (installedIntegrations.indexOf(integration.name) !== -1) {
return;
}
integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
installedIntegrations.push(getIntegrationName(integration));
logger.log(`Integration installed: ${getIntegrationName(integration)}`);
installedIntegrations.push(integration.name);
logger.log(`Integration installed: ${integration.name}`);
}

/**
Expand All @@ -65,20 +65,8 @@ export function setupIntegration(integration: Integration): void {
export function setupIntegrations<O extends Options>(options: O): IntegrationIndex {
const integrations: IntegrationIndex = {};
getIntegrationsToSetup(options).forEach(integration => {
integrations[getIntegrationName(integration)] = integration;
integrations[integration.name] = integration;
setupIntegration(integration);
});
return integrations;
}

/**
* Returns the integration static id.
* @param integration Integration to retrieve id
*/
function getIntegrationName(integration: Integration): string {
/**
* @depracted
*/
// tslint:disable-next-line:no-unsafe-any
return (integration as any).constructor.id || integration.name;
}

0 comments on commit b8691d3

Please sign in to comment.