Add readme and apply feedback

This commit is contained in:
Rémi de Juvigny 2020-12-14 17:50:19 +01:00
parent c43e9b21bb
commit 5484afe817
5 changed files with 153 additions and 111 deletions

View File

@ -1,3 +1,70 @@
# Strapi plugin sentry
# Strapi plugin Sentry
A quick description of sentry.
The official plugin to track Strapi errors with Sentry.
## Features
- Initialize a Sentry instance when your Strapi app starts
- Send errors encountered in your application's end API to Sentry
- Attach useful metadata to Sentry events, to help you with debugging
- Expose a global Sentry service
## Configuration
| property | type (default) | description |
| -------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dsn` | string (null) | Your Sentry data source name ([see Sentry docs](https://docs.sentry.io/product/sentry-basics/dsn-explainer/)). Omitting it will disable the plugin. |
| `sendMetadata` | boolean (true) | Whether the plugin should attach additional information (like OS, browser, etc.) to the events sent to Sentry. |
**Example**
`./config/plugins.js`
```js
module.exports = ({ env }) => ({
// ...
sentry: {
dsn: env('SENTRY_DSN'),
sendMetadata: true,
},
// ...
});
```
## Global Sentry service
You can access a Sentry service throughout your app.
```js
const sentryService = strapi.plugins.sentry.services.sentry;
```
This service exposes the following methods:
### `sendError(error, configureScope)`
Use it to manually send errors to Sentry. The `configureScope` is optional, it allows you to customize the error event. Read more about Sentry's scope system [on their docs](https://docs.sentry.io/platforms/node/enriching-events/scopes/#configuring-the-scope).
**Example**
```js
try {
// Your code here
} catch (error) {
strapi.plugins.sentry.services.sentry.sendError(error, (scope, sentryInstance) => {
// Customize the scope here
scope.setTag('my_custom_tag', 'Tag value');
});
throw error;
}
```
### `sendError(error, configureScope)`
Use it if you need direct access to the Sentry instance, which should already already be initialized. It's useful if `sendError` doesn't suit your needs.
**Example**
```js
const sentryInstance = strapi.plugins.sentry.services.sentry.getInstance();
```

View File

@ -5,24 +5,19 @@ module.exports = async () => {
const { sentry } = strapi.plugins.sentry.services;
sentry.init();
// Only send errors to Sentry if a valid DSN was entered and the plugin isn't disabled
const shouldSendSentryEvents = sentry.isReady;
// Create a middleware to intercept API errors
strapi.app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
if (shouldSendSentryEvents) {
sentry.sendError(error, (scope, sentryInstance) => {
scope.addEventProcessor(event => {
// Parse Koa context to add error metadata
return sentryInstance.Handlers.parseRequest(event, ctx.request);
});
// Manually add Strapi version
scope.setTag('strapi_version', strapi.config.info.strapi);
sentry.sendError(error, (scope, sentryInstance) => {
scope.addEventProcessor(event => {
// Parse Koa context to add error metadata
return sentryInstance.Handlers.parseRequest(event, ctx.request);
});
}
// Manually add Strapi version
scope.setTag('strapi_version', strapi.config.info.strapi);
});
throw error;
}
});

View File

@ -1,12 +1,3 @@
{
"routes": [
{
"method": "GET",
"path": "/",
"handler": "sentry.index",
"config": {
"policies": []
}
}
]
"routes": []
}

View File

@ -6,19 +6,4 @@
* @description: A set of functions called "actions" of the `sentry` plugin.
*/
module.exports = {
/**
* Default action.
*
* @return {Object}
*/
index: async ctx => {
// Add your own logic here.
// Send 200 `ok`
ctx.send({
message: 'ok',
});
},
};
module.exports = {};

View File

@ -3,81 +3,85 @@
const Sentry = require('@sentry/node');
const defaultSettings = require('../config/settings.json');
module.exports = {
isReady: false,
_instance: null,
settings: {},
const createSentryService = () => {
let isReady = false;
let instance = null;
let settings = {};
/**
* Initialize Sentry service
*/
init() {
// Make sure there isn't a Sentry instance already running
if (this._instance != null) {
strapi.log.warn('Sentry has already been initialized');
return;
}
// Retrieve user settings and merge them with the default ones
this.settings = {
...defaultSettings,
...strapi.plugins.sentry.config,
};
try {
// Don't init Sentry if no DSN was provided
if (this.settings.dsn) {
Sentry.init({
dsn: this.settings.dsn,
environment: strapi.config.environment,
});
// Store the successfully initialized Sentry instance
this._instance = Sentry;
this.isReady = true;
} else {
strapi.log.info('strapi-plugin-sentry is disabled because no Sentry DSN was provided');
return {
/**
* Initialize Sentry service
*/
init() {
// Make sure there isn't a Sentry instance already running
if (instance != null) {
strapi.log.warn('Sentry has already been initialized');
return;
}
} catch (error) {
strapi.log.warn('Could not set up Sentry, make sure you entered a valid DSN');
}
},
/**
* Expose Sentry instance through a getter
* @returns {Sentry}
*/
getInstance() {
return this._instance;
},
// Retrieve user settings and merge them with the default ones
settings = {
...defaultSettings,
...strapi.plugins.sentry.config,
};
/**
* Callback to [configure an instance of Sentry's scope]{@link https://docs.sentry.io/platforms/node/enriching-events/scopes/#configuring-the-scope}
* @callback configureScope
* @param {Sentry.scope} scope
* @param {Sentry=} instance An initialized Sentry instance
*/
/**
* Higher level method to send exception events to Sentry
* @param {Error} error An error object
* @param {configureScope=} configureScope
*/
sendError(error, configureScope) {
// Make sure Sentry is ready
if (!this.isReady) {
strapi.log.warn("Sentry wasn't properly initialized, cannot send event");
return;
}
this._instance.withScope(scope => {
// Configure the Sentry scope using the provided callback
if (this.settings.sendMetadata) {
configureScope(scope, this._instance);
try {
// Don't init Sentry if no DSN was provided
if (settings.dsn) {
Sentry.init({
dsn: settings.dsn,
environment: strapi.config.environment,
});
// Store the successfully initialized Sentry instance
instance = Sentry;
isReady = true;
} else {
strapi.log.info('strapi-plugin-sentry is disabled because no Sentry DSN was provided');
}
} catch (error) {
strapi.log.warn('Could not set up Sentry, make sure you entered a valid DSN');
}
// Actually send the Error to Sentry
this._instance.captureException(error);
});
},
strapi.log.info('An error was sent to Sentry');
},
/**
* Expose Sentry instance through a getter
* @returns {Sentry}
*/
getInstance() {
return instance;
},
/**
* Callback to [configure an instance of Sentry's scope]{@link https://docs.sentry.io/platforms/node/enriching-events/scopes/#configuring-the-scope}
* @callback configureScope
* @param {Sentry.scope} scope
* @param {Sentry=} instance An initialized Sentry instance
*/
/**
* Higher level method to send exception events to Sentry
* @param {Error} error An error object
* @param {configureScope=} configureScope
*/
sendError(error, configureScope) {
// Make sure Sentry is ready
if (!isReady) {
strapi.log.warn("Sentry wasn't properly initialized, cannot send event");
return;
}
instance.withScope(scope => {
// Configure the Sentry scope using the provided callback
if (settings.sendMetadata) {
configureScope(scope, instance);
}
// Actually send the Error to Sentry
instance.captureException(error);
});
strapi.log.info('An error was sent to Sentry');
},
};
};
module.exports = createSentryService();