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
12 changes: 12 additions & 0 deletions lib/statescontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,18 @@ class StatesController extends EventEmitter {
catch { /* */ }
}

// Drop a stashed error once its cause is resolved (e.g. a send to the device succeeded
// again), so the next occurrence is logged as a fresh error instead of silently counting.
unstashError(key) {
try {
if (this.stashedErrors.errors.hasOwnProperty(key)) {
delete this.stashedErrors.errors[key];
this.stashedErrors.hasData = Object.keys(this.stashedErrors.errors).length > 0 || Object.keys(this.stashedErrors.unknownModels).length > 0;
}
}
catch { /* */ }
}

stashUnknownModel(model, msg) {
try {
if (!this.stashedErrors.unknownModels.hasOwnProperty(model)) {
Expand Down
7 changes: 6 additions & 1 deletion lib/zigbeecontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,9 @@ class ZigbeeController extends EventEmitter {
}
}
retry = 0;
// the send went through - a stashed send failure for this device is
// resolved, the next failure is logged as a fresh error again
this.adapter.stController?.unstashError(`send:${String(deviceId).replace('0x', '')}`);
if (readKeys) {
for (const readKey of readKeys) {
if (readKey.converter) {
Expand Down Expand Up @@ -1753,8 +1756,10 @@ class ZigbeeController extends EventEmitter {
}
else {
retry = 0;
// stash key: repeated send failures to the same device (e.g. while it
// is unreachable) are counted instead of logged, until a send succeeds
this.adapter.filterError(`Error ${error.code} on send command to '${this.devLabel(deviceId)}'.` +
` Error: ${error.message}`, `Send command to ${deviceId} failed with`, error);
` Error: ${error.message}`, `Send command to ${deviceId} failed with`, error, `send:${String(deviceId).replace('0x', '')}`);
}
}
} while (retry > 0);
Expand Down
23 changes: 18 additions & 5 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,32 @@
} */
}

filterError(errormessage, message, error) {
// Error-level output of filterError. With a stash key, repeated identical errors
// (e.g. every failed send to an unreachable device, one per ~5 seconds) are collected
// by the states controller instead of flooding the log: the first one is logged as an
// error, the repeats only update the counter shown in the errors view of the admin
// tab and in info.lasterror. The caller clears the key once the cause is resolved.
logFilteredError(stashKey, text) {
if (stashKey && this.stController) {
this.stController.stashErrors(stashKey, `${text} - repeats are counted, not logged (errors view in the admin tab)`, true);
} else {
this.log.error(text);
}
}

filterError(errormessage, message, error, stashKey) {
if (error != null && error.code == undefined) {
let em = error.stack.match(/failed \((.+?)\) at/);

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings starting with 'failed (' and with many repetitions of 'failed (a'.
em = em || error.stack.match(/failed \((.+?)\)/);

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings starting with 'failed (' and with many repetitions of 'failed (a'.
this.log.error(`${message} no error code (${(em ? em[1] : 'undefined')})`);
this.logFilteredError(stashKey, `${message} no error code (${(em ? em[1] : 'undefined')})`);
this.sendError(error, `${message} no error code`);
if (this.debugActive) this.log.debug(`Stack trace for ${em}: ${error.stack}`);
return;
}

const ecode = errorCodes[error.code];
if (ecode === undefined) {
this.log.error(errormessage);
this.logFilteredError(stashKey, errormessage);
this.sendError(error, errormessage);
return;
}
Expand All @@ -194,11 +207,11 @@
this.log.warn(`${message}: Code ${error.code} (${ecode.message})`);
break;
case E_ERROR:
this.log.error(`${message}: Code ${error.code} (${ecode.message})`);
this.logFilteredError(stashKey, `${message}: Code ${error.code} (${ecode.message})`);
this.sendError(error, `${message}: Code ${error.code} (${ecode.message})`);
break;
default:
this.log.error(`${message}: Code ${error.code} (malformed error)`);
this.logFilteredError(stashKey, `${message}: Code ${error.code} (malformed error)`);
this.sendError(error, `${message}: Code ${error.code} (malformed error)`);
break;
}
Expand Down
Loading