2025-03-30 00:27:50 +01:00
|
|
|
import { log } from 'console';
|
|
|
|
|
|
2025-05-01 00:55:22 +02:00
|
|
|
export const BLANK_LOGGER_CONFIG: LogConfig = {
|
|
|
|
|
logToConsole: false,
|
|
|
|
|
logToFile: false,
|
|
|
|
|
component: {
|
|
|
|
|
},
|
|
|
|
|
origins: {
|
|
|
|
|
videoRescan: { disabled: true},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LogSourceOptions {
|
|
|
|
|
src?: string;
|
|
|
|
|
origin?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LogComponentConfig {
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LogConfig {
|
|
|
|
|
logToConsole?: boolean;
|
|
|
|
|
logToFile?: boolean;
|
|
|
|
|
stopAfter?: number;
|
|
|
|
|
component?: {[x: string]: LogComponentConfig};
|
|
|
|
|
origins?: {
|
|
|
|
|
videoRescan?: {
|
|
|
|
|
disabled: boolean;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-30 00:27:50 +01:00
|
|
|
|
2025-05-04 21:00:15 +02:00
|
|
|
const STORAGE_LOG_SETTINGS_KEY = 'uw-log-config';
|
2025-03-30 00:27:50 +01:00
|
|
|
|
|
|
|
|
export class LogAggregator {
|
2025-05-01 00:55:22 +02:00
|
|
|
private segment: string;
|
|
|
|
|
private config: LogConfig;
|
|
|
|
|
|
|
|
|
|
private startTime: number;
|
2025-03-30 00:27:50 +01:00
|
|
|
|
|
|
|
|
history: any[];
|
|
|
|
|
|
2025-05-01 00:55:22 +02:00
|
|
|
static async getConfig() {
|
2025-12-01 01:03:12 +01:00
|
|
|
let ret: any = await chrome.storage.local.get(STORAGE_LOG_SETTINGS_KEY);
|
2025-05-01 00:55:22 +02:00
|
|
|
|
|
|
|
|
if (process.env.CHANNEL === 'dev') {
|
|
|
|
|
try {
|
2025-05-04 21:00:15 +02:00
|
|
|
console.info("[LogAggregator::getSaved] Got settings:", JSON.parse(ret[STORAGE_LOG_SETTINGS_KEY]));
|
2025-05-01 00:55:22 +02:00
|
|
|
} catch (e) {
|
2025-05-04 21:00:15 +02:00
|
|
|
console.info("[LogAggregator::getSaved] No settings.", ret)
|
2025-05-01 00:55:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-30 00:27:50 +01:00
|
|
|
|
2025-05-01 00:55:22 +02:00
|
|
|
try {
|
2025-05-04 21:00:15 +02:00
|
|
|
return JSON.parse(ret[STORAGE_LOG_SETTINGS_KEY]);
|
2025-05-01 00:55:22 +02:00
|
|
|
} catch (e) {
|
|
|
|
|
return JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG));
|
|
|
|
|
}
|
2025-03-30 00:27:50 +01:00
|
|
|
}
|
|
|
|
|
|
2025-05-04 21:00:15 +02:00
|
|
|
static async saveConfig(conf: LogConfig) {
|
|
|
|
|
try {
|
|
|
|
|
const confCp = JSON.parse(JSON.stringify(conf));
|
|
|
|
|
if (process.env.CHANNEL === 'dev') {
|
|
|
|
|
console.info('Saving logger conf:', confCp)
|
|
|
|
|
}
|
2025-05-01 00:55:22 +02:00
|
|
|
|
2025-05-04 21:00:15 +02:00
|
|
|
await chrome.storage.local.set({[STORAGE_LOG_SETTINGS_KEY]: JSON.stringify(confCp)} );
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('[LogAggregator::saveConfig] Error while trying to save logger config:', e);
|
|
|
|
|
}
|
2025-05-01 00:55:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static syncConfig(callback: (x) => void) {
|
2025-12-01 01:03:12 +01:00
|
|
|
chrome.storage.onChanged.addListener( (changes: any, area) => {
|
2025-05-01 00:55:22 +02:00
|
|
|
if (changes.uwLogger) {
|
|
|
|
|
const newLoggerConf = JSON.parse(changes.uwLogger.newValue)
|
|
|
|
|
if (process.env.CHANNEL === 'dev') {
|
|
|
|
|
console.info('Logger settings reloaded. New conf:', newLoggerConf);
|
|
|
|
|
}
|
|
|
|
|
callback(newLoggerConf);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constructor(segment: string) {
|
|
|
|
|
this.segment = segment;
|
|
|
|
|
|
|
|
|
|
chrome.storage.onChanged.addListener((changes, area) => {
|
|
|
|
|
this.storageChangeListener(changes, area)
|
|
|
|
|
});
|
2025-05-04 21:00:15 +02:00
|
|
|
|
|
|
|
|
this.init();
|
2025-05-01 00:55:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private canLog(component: string, sourceOptions?: LogSourceOptions): boolean {
|
2025-05-04 21:00:15 +02:00
|
|
|
// component is not in config, so we add a blank entry
|
|
|
|
|
if (this.config && !this.config.component[component]) {
|
|
|
|
|
this.config.component[component] = {enabled: false};
|
|
|
|
|
LogAggregator.saveConfig(this.config);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-05-01 00:55:22 +02:00
|
|
|
if (this.config?.component?.[component]?.enabled) {
|
|
|
|
|
if (sourceOptions?.origin && this.config?.origins?.[sourceOptions.origin]?.disabled) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-05-04 21:00:15 +02:00
|
|
|
|
2025-05-01 00:55:22 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private storageChangeListener(changes, area) {
|
2025-05-04 21:00:15 +02:00
|
|
|
if (!changes[STORAGE_LOG_SETTINGS_KEY]) {
|
2025-05-01 00:55:22 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2025-05-04 21:00:15 +02:00
|
|
|
this.config = JSON.parse(changes[STORAGE_LOG_SETTINGS_KEY].newValue);
|
2025-05-01 00:55:22 +02:00
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('[uwLogger] Error while trying to parse new conf for logger:', e, '\nWe received the following changes:', changes, 'for area:', area);
|
|
|
|
|
}
|
2025-03-30 00:27:50 +01:00
|
|
|
|
2025-05-01 00:55:22 +02:00
|
|
|
// This code can only execute if user tried to enable or disable logging
|
|
|
|
|
// through the popup. In cases like this, we do not gate the console.log
|
|
|
|
|
// behind a check, since we _always_ want to have this feedback in response
|
|
|
|
|
// to an action.
|
|
|
|
|
console.info(
|
|
|
|
|
'[uwLogger] logger config changed! New configuration:',
|
|
|
|
|
this.config, '\nraw changes:', changes, 'area?', area,
|
|
|
|
|
'\n————————————————————————————————————————————————————————————————————————\n\n\n\n\n\n\n\n\n\n\n\n-----\nLogging with new settings starts now.'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.init(this.config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private parseStack() {
|
|
|
|
|
const trace = (new Error()).stack;
|
|
|
|
|
|
|
|
|
|
const stackInfo: any = {};
|
|
|
|
|
// 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;
|
|
|
|
|
stackInfo['mousemove'] = false;
|
|
|
|
|
stackInfo['exitLogs'] = false;
|
|
|
|
|
|
|
|
|
|
// here we check which source triggered the action. There can be more
|
|
|
|
|
// than one source, too, so we don't break when we find the first one
|
|
|
|
|
for (const line of stackInfo.stack.trace) {
|
|
|
|
|
if (line === 'doPeriodicPlayerElementChangeCheck') {
|
|
|
|
|
stackInfo['periodicPlayerCheck'] = true;
|
|
|
|
|
} else if (line === 'doPeriodicFallbackChangeDetectionCheck') {
|
|
|
|
|
stackInfo['periodicVideoStyleChangeCheck'] = true;
|
|
|
|
|
} else if (line === 'frameCheck') {
|
|
|
|
|
stackInfo['aard'] = true;
|
|
|
|
|
} else if (line === 'execAction') {
|
|
|
|
|
stackInfo['keyboard'] = true;
|
|
|
|
|
} else if (line === 'processReceivedMessage') {
|
|
|
|
|
stackInfo['popup'] = true;
|
|
|
|
|
} else if (line === 'handleMouseMove') {
|
|
|
|
|
stackInfo['mousemove'] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// exitLog overrides any other exclusions, so we look for it separately.
|
|
|
|
|
// we also remove some of the unnecessary messages to reduce log file size
|
|
|
|
|
for(let i = 0; i < stackInfo.stack.trace.length; i++) {
|
|
|
|
|
if (stackInfo.stack.trace[i] === 'finish') {
|
|
|
|
|
stackInfo['exitLogs'] = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stackInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: implement this
|
|
|
|
|
private stopLogging() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-04 21:00:15 +02:00
|
|
|
async init(config?: LogConfig) {
|
|
|
|
|
if (!config) {
|
|
|
|
|
config = await LogAggregator.getConfig();
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-01 00:55:22 +02:00
|
|
|
this.config = config;
|
|
|
|
|
this.startTime = performance.now();
|
|
|
|
|
|
|
|
|
|
if (this.config?.stopAfter) {
|
|
|
|
|
setTimeout(
|
|
|
|
|
() => {
|
|
|
|
|
this.stopLogging();
|
|
|
|
|
},
|
|
|
|
|
this.config.stopAfter * 1000
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(component: string, logLevel: string, sourceOptions: LogSourceOptions, message: string, ...data: any[]) {
|
|
|
|
|
if (! this.canLog(component, sourceOptions)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.config.logToFile) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-04 21:00:15 +02:00
|
|
|
if (this.config.logToConsole){
|
2025-05-01 00:55:22 +02:00
|
|
|
console[logLevel](`[${this.segment}]>>${message}`, ...data, {stack: this.parseStack()});
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-30 00:27:50 +01:00
|
|
|
}
|