2019-09-03 00:48:18 +02:00
import currentBrowser from '../conf/BrowserDetect' ;
2020-01-30 23:18:30 +01:00
import { decycle } from 'json-cyclic' ;
2019-09-03 00:48:18 +02:00
2019-07-16 20:59:12 +02:00
class Logger {
2020-02-02 00:57:54 +01:00
constructor ( options ) {
2020-01-29 23:38:52 +01:00
this . onLogEndCallbacks = [ ] ;
this . history = [ ] ;
this . globalHistory = { } ;
this . isContentScript = false ;
this . isBackgroundScript = true ;
2020-02-02 00:57:54 +01:00
this . vuexStore = options ? . vuexStore ;
2019-07-16 20:59:12 +02:00
}
2020-01-16 01:00:47 +01:00
static saveConfig ( conf ) {
2020-01-29 23:38:52 +01:00
if ( process . env . CHANNEL === 'dev' ) {
console . info ( 'Saving logger conf:' , conf )
}
2020-01-29 01:36:23 +01:00
2020-01-16 01:00:47 +01:00
if ( currentBrowser . firefox || currentBrowser . edge ) {
return browser . storage . local . set ( { 'uwLogger' : JSON . stringify ( conf ) } ) ;
} else if ( currentBrowser . chrome ) {
return chrome . storage . local . set ( { 'uwLogger' : JSON . stringify ( conf ) } ) ;
}
}
static syncConfig ( callback ) {
const br = currentBrowser . firefox ? browser : chrome ;
br . storage . onChanged . addListener ( ( changes , area ) => {
2020-01-29 01:36:09 +01:00
if ( changes . uwLogger ) {
const newLoggerConf = JSON . parse ( changes . uwLogger . newValue )
2020-01-29 23:38:52 +01:00
if ( process . env . CHANNEL === 'dev' ) {
console . info ( 'Logger settings reloaded. New conf:' , conf ) ;
}
2020-01-29 01:36:09 +01:00
callback ( newLoggerConf ) ;
}
2020-01-16 01:00:47 +01:00
} ) ;
}
static async getConfig ( ) {
let ret ;
if ( currentBrowser . firefox ) {
ret = await browser . storage . local . get ( 'uwLogger' ) ;
} else if ( currentBrowser . chrome ) {
ret = await new Promise ( ( resolve , reject ) => {
chrome . storage . local . get ( 'uwLogger' , ( res ) => resolve ( res ) ) ;
} ) ;
} else if ( currentBrowser . edge ) {
ret = await new Promise ( ( resolve , reject ) => {
browser . storage . local . get ( 'uwLogger' , ( res ) => resolve ( res ) ) ;
} ) ;
}
2020-01-29 01:36:23 +01:00
if ( process . env . CHANNEL === 'dev' ) {
2020-01-16 01:00:47 +01:00
try {
2020-01-29 01:36:23 +01:00
console . info ( "[Logger::getSaved] Got settings:" , JSON . parse ( ret . uwLogger ) ) ;
2020-01-16 01:00:47 +01:00
} catch ( e ) {
2020-01-29 01:36:23 +01:00
console . info ( "[Logger::getSaved] No settings." )
2020-01-16 01:00:47 +01:00
}
}
try {
return JSON . parse ( ret . uwLogger ) ;
} catch ( e ) {
2020-01-27 23:20:38 +01:00
return { consoleOptions : { } , fileOptions : { } } ;
2020-01-16 01:00:47 +01:00
}
}
2020-01-27 22:32:38 +01:00
async init ( conf ) {
2020-01-29 23:38:52 +01:00
// this is the only property that always gets passed via conf
// and doesn't get ignored even if the rest of the conf gets
// loaded from browser storage
if ( conf . isContentScript ) {
this . isContentScript = true ;
this . isBackgroundScript = false ;
2020-01-30 23:18:30 +01:00
} else if ( conf . isBackgroundScript ) {
this . isContentScript = false ;
this . isBackgroundScript = true ;
2020-01-29 23:38:52 +01:00
}
2020-01-29 01:36:23 +01:00
if ( conf && process . env . CHANNEL === 'dev' && ! conf . useConfFromStorage ) {
2020-01-27 22:32:38 +01:00
this . conf = conf ;
} else {
this . conf = await Logger . getConfig ( ) ;
}
if ( this . conf . consoleOptions === undefined ) {
this . conf . consoleOptions = { } ;
}
2020-01-27 23:20:38 +01:00
if ( this . conf . fileOptions === undefined ) {
2020-01-27 22:32:38 +01:00
this . conf . fileOptions = { } ;
}
2020-01-29 23:38:52 +01:00
2020-01-27 22:32:38 +01:00
this . startTime = performance . now ( ) ;
this . temp _disable = false ;
2020-01-29 01:36:23 +01:00
this . stopTime = this . conf . timeout ? performance . now ( ) + ( this . conf . timeout * 1000 ) : undefined ;
2020-01-27 22:32:38 +01:00
2019-09-03 00:48:18 +02:00
const br = currentBrowser . firefox ? browser : chrome ;
2020-01-27 23:20:38 +01:00
2019-09-03 00:48:18 +02:00
br . storage . onChanged . addListener ( ( changes , area ) => {
2020-01-29 23:38:52 +01:00
if ( process . env . CHANNEL === 'dev' ) {
2020-02-09 01:14:05 +01:00
if ( ! changes . uwLogger ) {
console . info ( '[Logger::<storage/on change> No new logger settings!' ) ;
}
2019-09-03 00:48:18 +02:00
if ( changes [ 'uwLogger' ] && changes [ 'uwLogger' ] . newValue ) {
2020-02-09 01:14:05 +01:00
console . log ( "[Logger::<storage/on change>] Logger have been changed outside of here. Updating active settings. Changes:" , changes , "storage area:" , area ) ;
console . info ( "[Logger::<storage/on change>] new logger settings object (parsed):" , JSON . parse ( changes . uwLogger . newValue ) ) ;
2019-09-03 00:48:18 +02:00
}
}
2020-02-09 01:14:05 +01:00
if ( ! changes [ 'uwLogger' ] ) {
return ;
}
if ( changes [ 'uwLogger' ] ? . newValue ) {
2020-02-06 22:29:59 +01:00
const newConf = JSON . parse ( changes . uwLogger . newValue ) ;
if ( this . isContentScript && this . conf . allowLogging && ! newConf . allowLogging ) {
this . saveToVuex ( ) ;
}
this . conf = newConf ;
2019-09-03 00:48:18 +02:00
}
} ) ;
}
2019-07-16 20:59:12 +02:00
clear ( ) {
this . log = [ ] ;
this . startTime = performance . now ( ) ;
2020-01-06 21:37:40 +01:00
this . stopTime = this . conf . timeout ? performance . now ( ) + ( this . conf . timeout * 1000 ) : undefined ;
2019-07-16 20:59:12 +02:00
}
setConf ( conf ) {
2019-09-03 00:48:18 +02:00
this . conf = conf ; // effective immediately
// also persist settings:
2020-01-16 01:00:47 +01:00
Logger . saveConfig ( conf ) ;
2019-07-16 20:59:12 +02:00
}
2019-09-03 00:48:18 +02:00
async getSaved ( ) {
2020-01-16 01:00:47 +01:00
return Logger . getSaved ( ) ;
2019-09-03 00:48:18 +02:00
}
2019-07-16 20:59:12 +02:00
// allow syncing of start times between bg and page scripts.
// may result in negative times in the log file, but that doesn't
// really matter
getStartTime ( ) {
return this . startTime ;
}
setStartTime ( startTime ) {
if ( startTime ) {
this . startTime = startTime ;
} else {
this . startTime = performance . now ( ) ;
}
}
2020-01-06 21:37:40 +01:00
// getLogFileString() {
// let logfileStr = '';
// let logTs = ''; // number of seconds since extension started on a given page¸
// for (let i = 0; i < this.history.length; i++) {
// logTs = ((this.history[i].ts - Math.floor(this.performance.now)) / 3).toFixed(3);
// logfileStr = `${logfileStr}[@${logTs}] -- ${this.history[i].message}\n`
// }
// return logfileStr;
// }
getFileLogJSONString ( ) {
return {
site : window && window . location ,
log : JSON . toString ( this . history ) ,
2019-07-16 20:59:12 +02:00
}
}
2019-07-18 21:25:58 +02:00
pause ( ) {
this . temp _disable = true ;
}
resume ( ) {
this . temp _disable = false ;
}
2020-01-29 23:38:52 +01:00
onLogEnd ( callback ) {
this . onLogEndCallbacks . push ( callback ) ;
}
// this should be used mostly in background page instance of logger, btw
2020-01-30 01:07:00 +01:00
//
2020-01-29 23:38:52 +01:00
addToGlobalHistory ( key , log ) {
this . globalHistory [ key ] = log ;
2020-01-30 23:18:30 +01:00
this . log ( 'info' , 'debug' , 'Added log for' , key , 'to global history. Current history:' , this . globalHistory ) ;
2020-01-29 23:38:52 +01:00
}
finish ( ) {
if ( ! this . isBackgroundScript ) {
2020-01-30 23:18:30 +01:00
this . conf . allowLogging = false ;
2020-02-02 00:57:54 +01:00
// const logJson = JSON.stringify(decycle(this.history));
// this.log('force', 'debugr', 'Calling all log end callbacks. There\'s this many of them:', 1);
// for(const f of this.onLogEndCallbacks) {
// f(logJson);
// }
// } else {
// this.exportLogToFile();
2020-01-29 23:38:52 +01:00
}
2020-02-02 00:57:54 +01:00
this . saveToVuex ( ) ;
2020-01-29 23:38:52 +01:00
}
2020-01-28 01:27:30 +01:00
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 ;
2020-01-28 23:11:59 +01:00
stackInfo [ 'mousemove' ] = false ;
2020-01-30 23:18:30 +01:00
stackInfo [ 'exitLogs' ] = false ;
2020-01-28 01:27:30 +01:00
// 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 ) {
2020-01-30 23:18:30 +01:00
if ( line === 'doPeriodicPlayerElementChangeCheck' ) {
2020-01-28 01:27:30 +01:00
stackInfo [ 'periodicPlayerCheck' ] = true ;
break ;
2020-01-30 23:18:30 +01:00
} else if ( line === 'doPeriodicFallbackChangeDetectionCheck' ) {
2020-01-28 01:27:30 +01:00
stackInfo [ 'periodicVideoStyleChangeCheck' ] = true ;
break ;
2020-01-30 23:18:30 +01:00
} else if ( line === 'frameCheck' ) {
2020-01-28 01:27:30 +01:00
stackInfo [ 'aard' ] = true ;
break ;
2020-01-30 23:18:30 +01:00
} else if ( line === 'execAction' ) {
2020-01-28 01:27:30 +01:00
stackInfo [ 'keyboard' ] = true ;
break ;
2020-01-30 23:18:30 +01:00
} else if ( line === 'processReceivedMessage' ) {
2020-01-28 01:27:30 +01:00
stackInfo [ 'popup' ] = true ;
break ;
2020-01-30 23:18:30 +01:00
} else if ( line === 'handleMouseMove' ) {
2020-01-28 23:11:59 +01:00
stackInfo [ 'mousemove' ] = true ;
2020-01-30 23:18:30 +01:00
break ;
2020-01-28 01:27:30 +01:00
}
}
2020-01-30 23:18:30 +01:00
// exitLog overrides any other exclusions, so we look for it separately.
2020-02-03 22:13:03 +01:00
// we also remove some of the unnecessary messages to reduce log file size
for ( let i = 0 ; i < stackInfo . stack . trace . length ; i ++ ) {
2020-01-30 23:18:30 +01:00
if ( stackInfo . stack . trace [ i ] === 'finish' ) {
stackInfo [ 'exitLogs' ] = true ;
break ;
}
2020-02-03 22:13:03 +01:00
// if we hit one of these, we remove the rest of the array and call it a
// day. Chances are there's nothing of value past this point.
if ( stackInfo . stack . trace [ i ] . indexOf ( 'promise callback' ) !== - 1
|| stackInfo . stack . trace [ i ] . indexOf ( 'asyncGeneratorStep' ) !== - 1
|| stackInfo . stack . trace [ i ] . indexOf ( '_asyncToGenerator' ) !== - 1
|| stackInfo . stack . trace [ i ] . startsWith ( '_next' ) ) {
stackInfo . stack . trace . splice ( i ) ;
break ;
}
2020-01-30 23:18:30 +01:00
}
2020-01-28 01:27:30 +01:00
return stackInfo ;
}
2020-01-28 23:11:59 +01:00
// test for blacklisted origin
isBlacklistedOrigin ( stackInfo ) {
if ( stackInfo . periodicPlayerCheck ) {
return ! this . conf . allowBlacklistedOrigins ? . periodicPlayerCheck ;
2020-01-28 01:27:30 +01:00
}
2020-01-28 23:11:59 +01:00
if ( stackInfo . periodicVideoStyleChangeCheck ) {
return ! this . conf . allowBlacklistedOrigins ? . periodicVideoStyleChangeCheck ;
}
if ( stackInfo . mousemove ) {
return ! this . conf . allowBlacklistedOrigins ? . handleMouseMove ;
2020-01-28 01:27:30 +01:00
}
2020-01-28 23:11:59 +01:00
return false ;
}
2020-01-29 01:36:23 +01:00
isTimeUp ( ) {
2020-01-30 23:18:30 +01:00
// we don't do timeouts on background script
if ( this . isBackgroundScript ) {
return false ;
}
2020-01-29 01:36:23 +01:00
if ( this . stopTime && performance . now ( ) > this . stopTime ) {
if ( this . conf . allowLogging ) {
this . log ( 'force' , 'debug' , '-----[ alloted time has run out. logging will stop ]-----' ) ;
2020-01-29 23:38:52 +01:00
this . finish ( ) ;
2020-01-29 01:36:23 +01:00
}
return true ;
}
return false ;
}
2020-02-04 00:56:48 +01:00
isLoggingToFile ( ) {
return this . conf . allowLogging && this . conf . fileOptions ? . enabled ;
}
2020-01-28 23:11:59 +01:00
// NOTE: THIS FUNCTION IS NEVER USED INTERNALLY!
canLog ( component ) {
2020-01-30 23:18:30 +01:00
const stackInfo = this . parseStack ( ) ;
if ( ! this . conf . allowLogging && ! stackInfo . exitLogs ) {
2020-01-28 23:11:59 +01:00
return false ;
2020-01-28 01:27:30 +01:00
}
2020-01-28 23:11:59 +01:00
if ( this . isBlacklistedOrigin ( stackInfo ) ) {
return false ;
}
2020-01-28 01:27:30 +01:00
// if either of these two is true, we allow logging to happen (forbidden origins were checked above)
2020-01-30 23:18:30 +01:00
return ( this . canLogFile ( component ) || this . canLogConsole ( component ) || stackInfo . exitLogs ) ;
2019-07-18 21:25:58 +02:00
}
canLogFile ( component ) {
if ( ! this . conf . fileOptions . enabled || this . temp _disable ) {
return false ;
}
2019-11-04 22:14:41 +01:00
if ( Array . isArray ( component ) && component . length ) {
for ( const c of component ) {
if ( this . conf . fileOptions [ c ] ) {
return this . conf . fileOptions [ c ] ;
2019-07-18 21:25:58 +02:00
}
}
2019-11-04 22:14:41 +01:00
} else {
return this . conf . fileOptions [ component ] ;
}
2019-07-18 21:25:58 +02:00
}
2020-01-30 23:18:30 +01:00
canLogConsole ( component , stackInfo ) {
2020-01-29 01:36:23 +01:00
if ( ! this . conf . consoleOptions ? . enabled || this . temp _disable ) {
2020-01-06 21:37:40 +01:00
return false ;
}
2019-08-25 22:00:57 +02:00
if ( Array . isArray ( component ) && component . length ) {
2019-11-04 22:14:41 +01:00
for ( const c of component ) {
if ( this . conf . consoleOptions [ c ] ) {
return this . conf . consoleOptions [ c ] ;
2019-07-16 22:46:16 +02:00
}
}
2019-11-04 22:14:41 +01:00
} else {
2020-01-15 21:09:12 +01:00
return this . conf . consoleOptions [ component ] !== undefined ? this . conf . consoleOptions [ component ] : this . conf . logAll ;
2019-11-04 22:14:41 +01:00
}
2020-01-15 21:09:12 +01:00
return this . conf . logAll ;
2019-07-16 22:46:16 +02:00
}
2020-01-29 01:36:23 +01:00
logToFile ( message , stackInfo ) {
let ts = performance . now ( ) ;
if ( ts <= this . history [ this . history . length - 1 ] ) {
ts = this . history [ this . history . length - 1 ] + 0.00001 ;
}
this . history . push ( {
ts : ts ,
2020-01-30 23:18:30 +01:00
message : message ,
2020-01-29 01:36:23 +01:00
stack : stackInfo ,
2020-01-30 23:18:30 +01:00
} ) ;
2020-01-29 01:36:23 +01:00
}
logToConsole ( message , stackInfo ) {
2020-01-30 23:18:30 +01:00
try {
console . log ( ... message , { stack : stackInfo } ) ;
} catch ( e ) {
console . error ( "Message too big to log. Error:" , e , "stackinfo:" , stackInfo ) ;
}
2020-01-29 01:36:23 +01:00
}
2019-07-16 22:46:16 +02:00
2019-07-16 20:59:12 +02:00
// level is unused as of now, but this may change in the future
2020-01-28 23:11:59 +01:00
// levels: 'info', 'warn', 'error'.
// if level is `true` (bool), logging happens regardless of any other
// settings
2019-07-16 20:59:12 +02:00
log ( level , component , ... message ) {
2020-01-30 23:18:30 +01:00
const stackInfo = this . parseStack ( ) ;
if ( ! this . conf || ( ! this . conf . allowLogging && ! stackInfo . exitLogs ) ) {
2019-07-16 22:46:16 +02:00
return ;
}
2020-01-30 01:07:00 +01:00
2020-01-29 01:36:23 +01:00
// skip all checks if we force log
if ( level === 'force' ) {
2020-02-06 23:53:31 +01:00
if ( this . conf . fileOptions ? . enabled ) {
2020-01-29 01:36:23 +01:00
this . logToFile ( message , stackInfo ) ;
}
2020-02-06 23:53:31 +01:00
if ( this . conf . consoleOptions ? . enabled ) {
2020-01-29 01:36:23 +01:00
this . logToConsole ( message , stackInfo ) ;
}
return ; // don't check further — recursion-land ahead!
}
2020-01-30 23:18:30 +01:00
if ( this . isTimeUp ( ) && ! stackInfo . exitLogs ) {
2020-01-29 01:36:23 +01:00
return ;
}
2020-01-28 23:11:59 +01:00
// don't log stuff from blacklisted origin (unless logger conf says otherwise)
if ( this . isBlacklistedOrigin ( stackInfo ) ) {
return ;
}
2020-01-28 01:27:30 +01:00
2020-02-06 23:53:31 +01:00
if ( this . conf . fileOptions ? . enabled ) {
2020-01-30 23:18:30 +01:00
if ( this . canLogFile ( component ) || stackInfo . exitLogs ) {
2020-01-29 01:36:23 +01:00
this . logToFile ( message , stackInfo ) ;
2019-07-16 20:59:12 +02:00
}
}
2020-02-06 23:53:31 +01:00
if ( this . conf . consoleOptions ? . enabled ) {
2020-01-30 23:18:30 +01:00
if ( this . canLogConsole ( component ) || stackInfo . exitLogs ) {
2020-01-29 01:36:23 +01:00
this . logToConsole ( message , stackInfo ) ;
2019-07-16 20:59:12 +02:00
}
}
}
2020-01-29 01:36:23 +01:00
2019-07-16 20:59:12 +02:00
// leaves a noticeable mark in the file log at the time it got triggered, as well as
// at the intervals of 1s and .5s before the trigger moment
cahen ( ) {
if ( this . conf . logToFile ) {
let ts = performance . now ( ) ;
let secondMark = ts - 1000 ;
let halfSecondMark = ts - 500 ;
let i = this . history . length ( ) ;
// correct ts _after_ secondMark and halfSecondMark were determined
if ( ts <= this . history [ this . history . length - 1 ] ) {
ts = this . history [ this . history . length - 1 ] + 0.00001 ;
}
this . history . push ( {
ts : ts ,
message : "-------------------------------------- CAHEN --------------------------------------"
} ) ;
// find the spot for the half second mark. In this case, we don't really particularly care whether timestamps
// are duped due to cahen warnings
while ( this . history [ i -- ] . ts > halfSecondMark ) { }
this . history . push ( {
ts : halfSecondMark ,
message : "-------------------------------------- 0.5s to CAHEN --------------------------------------"
} ) ;
// repeat for one second warning
while ( this . history [ i -- ] . ts > secondMark ) { }
this . history . push ( {
ts : secondMark ,
message : "-------------------------------------- 1s to CAHEN --------------------------------------"
} ) ;
}
}
2020-01-21 00:41:06 +01:00
addLogFromPage ( host , tabId , frameId , pageHistory ) {
if ( ! this . globalHistory [ host ] ) {
this . globalHistory [ host ] = { } ;
}
if ( ! this . globalHistory [ tabId || 'tab' ] ) {
this . globalHistory [ host ] [ tabId || 'tab' ] = { } ;
}
if ( ! this . globalHistory [ frameId || 'top' ] ) {
this . globalHistory [ host ] [ tabId || 'tab' ] [ frameId || 'top' ] = pageHistory ;
} else {
this . globalHistory [ host ] [ tabId || 'tab' ] [ frameId || 'top' ] . push ( ... pageHistory ) ;
}
}
2020-02-02 00:57:54 +01:00
saveToVuex ( ) {
console . info ( '[info] will attempt to save to vuex store.' ) ;
if ( ! this . conf ? . fileOptions ? . enabled || this . isBackgroundScript ) {
console . info ( '[info] Logging to file is either disabled or we\'re not on the content script. Not saving.' ) ;
return ;
}
if ( ! this . vuexStore ) {
console . error ( "[info] No vue store. Log will not be exported." ) ;
return ;
}
console . info ( '[info] vuex store present. Parsing logs.' ) ;
2020-02-03 22:13:03 +01:00
let exportObject ;
try {
exportObject = {
pageLogs : decycle ( this . history ) ,
backgroundLogs : decycle ( this . globalHistory ) ,
loggerFileOptions : this . conf . fileOptions ,
}
} catch ( e ) {
console . error ( "[fail] error parsing logs!" , e )
return ;
}
console . info ( '[info] Logs were parsed successfuly. Putting stuff to vuex ...' ) ;
try {
this . vuexStore . dispatch ( 'uw-set-log' , exportObject ) ;
} catch ( e ) {
console . log ( "[fail] error saving to vuex" , e ) ;
return ;
2020-02-02 00:57:54 +01:00
}
2020-02-03 22:13:03 +01:00
console . info ( '[info] Export object saved to vuex store.' )
2020-02-02 00:57:54 +01:00
}
2019-07-16 20:59:12 +02:00
}
export default Logger ;