ultrawidify/src/ext/lib/logging/LogAggregator.ts

263 lines
7.7 KiB
TypeScript
Raw Normal View History

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: {
'PageInfo': {enabled: false},
'PlayerData': {enabled: false},
'VideoData': {enabled: false},
'Resizer': {enabled: false},
'Scaler': {enabled: false},
'Stretcher': {enabled: false},
'Zoom': {enabled: false},
'Aard': {enabled: false},
'KeyboardHandler': {enabled: false},
'MouseHandler': {enabled: false},
'Settings': {enabled: false},
'UWContent': {enabled: false},
'UWServer': {enabled: false},
'CommsServer': {enabled: false},
'CommsClient': {enabled: false},
'App.vue': {enabled: false},
'Popup': {enabled: false},
'SettingsPage': {enabled: false},
2025-05-01 00:55:22 +02:00
},
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[];
static async resetConfig() {
await this.saveConfig(BLANK_LOGGER_CONFIG);
}
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
}
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
);
}
}
/**
* Logs message to console.
* This function does NOT check before logging, as canLog check should now be performed
* BEFORE ComponentLogger starts processing log calls.
* @param logLevel
* @param message
* @param data
*/
log(logLevel: string, message: string, ...data: any[]) {
2025-05-01 00:55:22 +02:00
if (this.config.logToFile) {
console.warn('log to file not implemented!')
2025-05-01 00:55:22 +02:00
}
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
}