[Logger] Track origin of the call stack. Allow blacklisting of some periodic calls

This commit is contained in:
Tamius Han 2020-01-28 01:27:30 +01:00
parent 3e565ddbfc
commit 0cbd62352e
4 changed files with 116 additions and 18 deletions

View File

@ -144,8 +144,71 @@ class Logger {
this.temp_disable = false;
}
canLog(component) {
return this.conf.allowLogging && (this.canLogFile(component) || this.canLogConsole(component));
parseStack() {
const trace = (new Error()).stack;
const stackInfo = {};
// we turn our stack into array and remove the "file::line" part of the trace,
// since that is useless because minification/webpack
stackInfo['stack'] = {trace: trace.split('\n').map(a => a.split('@')[0])};
// here's possible sources that led to this log entry
stackInfo['periodicPlayerCheck'] = false;
stackInfo['periodicVideoStyleChangeCheck'] = false;
stackInfo['aard'] = false;
stackInfo['keyboard'] = false;
stackInfo['popup'] = false;
// here we check which source triggered the action. We know that only one of these
// functions will appear in the trace at most once (and if more than one of these
// appears — e.g. frameCheck triggered by user toggling autodetection in popup —
// the most recent one will be the correct one 99% of the time)
for (const line of stackInfo.stack.trace) {
if (line.startsWith('doPeriodicPlayerElementChangeCheck')) {
stackInfo['periodicPlayerCheck'] = true;
break;
} else if (line.startsWith('doPeriodicFallbackChangeDetectionCheck')) {
stackInfo['periodicVideoStyleChangeCheck'] = true;
break;
} else if (line.startsWith('frameCheck')) {
stackInfo['aard'] = true;
break;
} else if (line.startsWith('execAction')) {
stackInfo['keyboard'] = true;
break;
} else if (line.startsWith('processReceivedMessage')) {
stackInfo['popup'] = true;
break;
}
}
return stackInfo;
}
canLog(component, stackInfo) {
if (!this.conf.allowLogging) {
return false;
}
// if we call this function from outside of logger,
// stackInfo may not be provided. Here's a fallback.
if (stackInfo === undefined) {
stackInfo = this.parseStack();
}
// check if log belongs to a blacklisted origin. If yes, then only allow content to be logged if the
// origin is explicitly whitelisted in the conf object
if (stackInfo) {
if (stackInfo.periodicPlayerCheck) {
return this.conf.allowBlacklistedOrigins && this.conf.allowBlacklistedOrigins.periodicPlayerCheck;
}
if (stackInfo.periodicVideoStyleChangeCheck) {
return this.conf.allowBlacklistedOrigins && this.conf.allowBlacklistedOrigins.periodicVideoStyleChangeCheck;
}
}
// if either of these two is true, we allow logging to happen (forbidden origins were checked above)
return (this.canLogFile(component) || this.canLogConsole(component));
}
canLogFile(component) {
@ -193,9 +256,10 @@ class Logger {
if (!this.conf) {
return;
}
const error = new Error();
const stackInfo = this.parseStack();
if (this.conf.fileOptions.enabled) {
if (this.canLogFile(component)) {
if (this.canLogFile(component, stackInfo)) {
let ts = performance.now();
if (ts <= this.history[this.history.length - 1]) {
ts = this.history[this.history.length - 1] + 0.00001;
@ -204,13 +268,13 @@ class Logger {
this.history.push({
ts: ts,
message: JSON.stringify(message),
stack: error.stack,
stack: stackInfo,
})
}
}
if (this.conf.consoleOptions.enabled) {
if (this.canLogConsole(component)) {
console.log(...message, {stack: error});
if (this.canLogConsole(component, stackInfo)) {
console.log(...message, {stack: stackInfo});
}
}
}

View File

@ -127,18 +127,21 @@ class PlayerData {
while (!this.halted) {
await this.sleep(1000);
try {
if (this.periodicallyRefreshPlayerElement) {
this.forceRefreshPlayerElement();
}
if (this.checkPlayerSizeChange()) {
this.videoData.resizer.restore();
}
this.doPeriodicPlayerElementChangeCheck();
} catch (e) {
console.error('[playerdata::legacycd] this message is pretty high on the list of messages you shouldnt see', e);
}
}
}
doPeriodicPlayerElementChangeCheck() {
if (this.periodicallyRefreshPlayerElement) {
if (this.forceDetectPlayerElementChange()) {
this.videoData.resizer.restore();
}
}
}
stopChangeDetection(){
this.observer.disconnect();
}
@ -232,6 +235,18 @@ class PlayerData {
return;
}
// log the entire hierarchy from <video> to root
if (this.logger.canLog('playerDetect')) {
const logObj = [];
logObj.push(`window size: ${window.innerWidth} x ${window.innerHeight}`);
let e = element;
while (e) {
logObj.push({offsetSize: {width: e.offsetWidth, height: e.offsetHeight}, clientSize: {width: e.clientWidth, height: e.clientHeight}, element: e});
e = e.parentNode;
}
this.logger.log('info', 'playerDetect', "\n\n[PlayerDetect::getPlayer()] element hierarchy (video->root)", logObj);
}
if (this.settings.active.sites[host]
&& this.settings.active.sites[host].DOM
&& this.settings.active.sites[host].DOM.player
@ -275,6 +290,9 @@ class PlayerData {
element = element.parentNode;
}
// log player candidates
this.logger.log('info', 'playerDetect', 'player detect via query selector: element queue and final element:', {queue: elementQ, bestCandidate: elementQ.length ? elementQ.sort( (a,b) => b.score - a.score)[0].element : 'n/a'});
if (elementQ.length) {
// return element with biggest score
// if video player has not been found, proceed to automatic detection
@ -332,9 +350,13 @@ class PlayerData {
element = element.parentNode;
}
// log player candidates
this.logger.log('info', 'playerDetect', 'player detect, auto/fallback: element queue and final element:', {queue: elementQ, bestCandidate: elementQ.length ? elementQ.sort( (a,b) => b.score - a.score)[0].element : 'n/a'});
if (elementQ.length) {
// return element with biggest score
const playerElement = elementQ.sort( (a,b) => b.score - a.score)[0].element;
this.updatePlayerDimensions(playerElement);
return playerElement;
}

View File

@ -71,10 +71,14 @@ class VideoData {
async fallbackChangeDetection() {
while (!this.destroyed && !this.invalid) {
await this.sleep(500);
this.validateVideoOffsets();
this.doPeriodicFallbackChangeDetectionCheck();
}
}
doPeriodicFallbackChangeDetectionCheck() {
this.validateVideoOffsets();
}
async sleep(timeout) {
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
}

View File

@ -55,9 +55,9 @@ class UW {
'settings': true,
'keyboard': true,
'mousemove': false,
'actionHandler': false,
'comms': false,
'playerDetect': false,
'actionHandler': true,
'comms': true,
'playerDetect': true,
'resizer': true,
'scaler': true,
'stretcher': true,
@ -65,8 +65,16 @@ class UW {
// 'playerRescan': true,
'arDetect': true,
'arDetect_verbose': true,
},
allowBlacklistedOrigins: {
// logs that were called from functions named here will be excluded from both console logging
// as well as logging to file. Exclusions happen because these functions — being periodic in
// nature as well as doing a lot of work — usually clog console too fast without providing
// any valuable info.
'periodicPlayerCheck': true,
'periodicVideoStyleChangeCheck': true,
}
};
};
this.logger = new Logger();
await this.logger.init(loggingOptions);
// await this.logger.init(); // not needed if logging options are provided at creation