Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: ioBroker/testing-action-check@v1
- uses: ioBroker/testing-action-check@v2
with:
node-version: '22.x'
node-version: '24.x'
# Uncomment the following line if your adapter cannot be installed using 'npm ci'
# install-command: 'npm install'
lint: true
Expand Down Expand Up @@ -69,7 +69,7 @@ jobs:
steps:
- uses: ioBroker/testing-action-deploy@v1
with:
node-version: '22.x'
node-version: '24.x'
build: true
# Uncomment the following line if your adapter cannot be installed using 'npm ci'
# install-command: 'npm install'
Expand Down
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ To obtain a Sentry account as a developer, you have a couple of options:
The basic process to use the ioBroker Sentry system is as follows:

1. Contact @Apollon77 by creating an issue in this project for each adapter you require access to. Make sure to include the link to the adapter repository.
1. We will conduct an enhanced adapter review, specifically focusing on error handling to ensure that adapters do not flood the Sentry system.
1. We will need your email address to invite you to the Sentry instance, and you will need a Two-Factor-Authentication app (e.g., Google Authenticator) to secure your account.
1. We will create the project on Sentry and assign it to you.
1. We will provide you with the necessary Sentry DSN for your configuration.
1. You must add the configuration to the io-package.json file and include a short information section in your README. Please add the following notice to the top of your README:
2. We will conduct an enhanced adapter review, specifically focusing on error handling to ensure that adapters do not flood the Sentry system.
3. We will need your email address to invite you to the Sentry instance, and you will need a Two-Factor-Authentication app (e.g., Google Authenticator) to secure your account.
4. We will create the project on Sentry and assign it to you.
5. We will provide you with the necessary Sentry DSN for your configuration.
6. You must add the configuration to the io-package.json file and include a short information section in your README. Please add the following notice to the top of your README:
`**This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and instructions on disabling error reporting, please refer to the [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Use of Sentry reporting starts with js-controller 3.0.`
1. We can discuss the details separately if you wish to transfer your own errors or other events.
1. With everything finally set up, you can test and release your adapter.
7. We can discuss the details separately if you wish to transfer your own errors or other events.
8. With everything finally set up, you can test and release your adapter.

Please follow these steps for smooth integration with the ioBroker Sentry system.

Expand Down Expand Up @@ -162,14 +162,19 @@ This should cause the adapter to crash and the exception to be shown in the sent
-->

## Changelog

### **WORK IN PROGRESS**
- (@Apollon77) Disable Sentry session reporting
- (@GermanBluefox) Updated packages to the latest versions

### 3.0.4 (2025-12-30)
- (@GermanBluefox) Updated packages to latest versions
- (@GermanBluefox) Updated packages to the latest versions

### 3.0.0 (2025-10-13)
- (@GermanBluefox) Updated packages to latest versions
- (@GermanBluefox) Updated packages to the latest versions

### 2.0.4 (2024-06-01)
- (foxriver76) work with plugin base v2
- (foxriver76) works with plugin base v2
- (foxriver76) ported to TypeScript to provide improved type support

**Breaking Changes**: Due to the port to Plugin Base v2, `init` now returns a promise instead of accepting a callback parameter, also the export has changed to a named export
Expand Down
2 changes: 1 addition & 1 deletion build/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { PluginBase } from '@iobroker/plugin-base';
export default class SentryPlugin extends PluginBase {
/** The Sentry instance */
Sentry: typeof import('@sentry/node');
/** If plugin is enabled after all checks */
/** If the plugin is enabled after all checks */
reallyEnabled: boolean;
/**
* Register and initialize Sentry
Expand Down
121 changes: 62 additions & 59 deletions build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class SentryPlugin extends plugin_base_1.PluginBase {
/** The Sentry instance */
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
Sentry;
/** If plugin is enabled after all checks */
/** If the plugin is enabled after all checks */
reallyEnabled = false;
/**
* Register and initialize Sentry
Expand Down Expand Up @@ -89,7 +89,7 @@ class SentryPlugin extends plugin_base_1.PluginBase {
}
async _registerSentry(pluginConfig, uuid) {
this.reallyEnabled = true;
// Require needed tooling
// Require necessary tooling
this.Sentry = await import('@sentry/node');
const SentryIntegrations = require('@sentry/integrations');
// By installing source map support, we get the original source
Expand All @@ -116,7 +116,12 @@ class SentryPlugin extends plugin_base_1.PluginBase {
this.Sentry.init({
release: `${this.parentPackage.name}@${this.parentPackage.version}`,
dsn: pluginConfig.dsn,
integrations: [new SentryIntegrations.Dedupe()],
integrations: [
new SentryIntegrations.Dedupe(),
SentryIntegrations.httpIntegration({
trackIncomingRequestsAsSessions: false, // default: true
}),
],
});
if (this.parentIoPackage?.common) {
this.Sentry.setTag('version', this.parentIoPackage.common.installedVersion || this.parentIoPackage.common.version);
Expand Down Expand Up @@ -151,69 +156,67 @@ class SentryPlugin extends plugin_base_1.PluginBase {
this.Sentry.setUser({ id: uuid });
}
const scope = this.Sentry.getCurrentScope?.();
if (scope) {
scope.addEventProcessor((event, hint) => {
if (!this.isActive) {
return;
scope?.addEventProcessor((event, hint) => {
if (!this.isActive) {
return null;
}
// Try to filter out some events
if (event.exception?.values?.[0]) {
const eventData = event.exception.values[0];
// if the error type is one from the blacklist, we ignore this error
if (eventData.type && sentryErrorBlacklist.includes(eventData.type)) {
return null;
}
// Try to filter out some events
if (event.exception?.values?.[0]) {
const eventData = event.exception.values[0];
// if the error type is one from the blacklist, we ignore this error
if (eventData.type && sentryErrorBlacklist.includes(eventData.type)) {
return null;
}
const originalException = hint.originalException;
// ignore EROFS, ENOSPC and such errors always
const errorText = originalException?.code
? originalException.code.toString()
: originalException?.message?.toString() || originalException;
if (typeof errorText === 'string' &&
(errorText.includes('EROFS') || // Read only FS
errorText.includes('ENOSPC') || // No disk space available
errorText.includes('ENOMEM') || // No memory (RAM) available
errorText.includes('EIO') || // I/O error
errorText.includes('ENXIO') || // I/O error
errorText.includes('EMFILE') || // too many open files
errorText.includes('ENFILE') || // file table overflow
errorText.includes('EBADF')) // Bad file descriptor
) {
const originalException = hint.originalException;
// ignore EROFS, ENOSPC and such errors always
const errorText = originalException?.code
? originalException.code.toString()
: originalException?.message?.toString() || originalException;
if (typeof errorText === 'string' &&
(errorText.includes('EROFS') || // Read only FS
errorText.includes('ENOSPC') || // No disk space available
errorText.includes('ENOMEM') || // No memory (RAM) available
errorText.includes('EIO') || // I/O error
errorText.includes('ENXIO') || // I/O error
errorText.includes('EMFILE') || // too many open files
errorText.includes('ENFILE') || // file table overflow
errorText.includes('EBADF')) // Bad file descriptor
) {
return null;
}
if (eventData.stacktrace?.frames &&
Array.isArray(eventData.stacktrace.frames) &&
eventData.stacktrace.frames.length) {
// if the last exception frame is from a Node.js internal method, we ignore this error
const fileName = eventData.stacktrace.frames[eventData.stacktrace.frames.length - 1].filename;
if (fileName && (fileName.startsWith('internal/') || fileName.startsWith('Module.'))) {
return null;
}
if (eventData.stacktrace?.frames &&
Array.isArray(eventData.stacktrace.frames) &&
eventData.stacktrace.frames.length) {
// if the last exception frame is from a Node.js internal method, we ignore this error
const fileName = eventData.stacktrace.frames[eventData.stacktrace.frames.length - 1].filename;
if (fileName && (fileName.startsWith('internal/') || fileName.startsWith('Module.'))) {
return null;
// Check if any entry is whitelisted from pathWhitelist
const whitelisted = eventData.stacktrace.frames.find(frame => {
if (frame.function?.startsWith('Module.')) {
return false;
}
if (frame.filename?.startsWith('internal/')) {
return false;
}
// Check if any entry is whitelisted from pathWhitelist
const whitelisted = eventData.stacktrace.frames.find(frame => {
if (frame.function?.startsWith('Module.')) {
return false;
}
if (frame.filename?.startsWith('internal/')) {
return false;
}
if (frame.filename &&
!sentryPathWhitelist.find(path => path?.length && frame.filename.includes(path))) {
return false;
}
if (frame.filename &&
sentryPathBlacklist.find(path => path?.length && frame.filename.includes(path))) {
return false;
}
return true;
});
if (!whitelisted) {
return null;
if (frame.filename &&
!sentryPathWhitelist.find(path => path?.length && frame.filename.includes(path))) {
return false;
}
if (frame.filename &&
sentryPathBlacklist.find(path => path?.length && frame.filename.includes(path))) {
return false;
}
return true;
});
if (!whitelisted) {
return null;
}
}
return event;
});
}
}
return event;
});
}
/**
* Return the Sentry object. This can be used to send own Messages or such
Expand Down
Loading