Work around chrome's console.log limitations.

This commit is contained in:
Tamius Han 2025-12-22 01:00:00 +01:00
parent dad1df0244
commit 126d891e5e
2 changed files with 259 additions and 12 deletions

View File

@ -14,6 +14,14 @@ export type ComponentLoggerOptions = {
} }
} }
const OBJECT_SEPARATOR_STYLE = {color: '#fff'};
const OBJECT_KEY_STYLE = {color: '#ebe4ff69'};
const OBJECT_STRING_STYLE = {color: '#80b6e5ff'};
const OBJECT_NUMBER_STYLE = {color: 'rgba(127, 104, 204, 1)'};
const OBJECT_BOOL_TRUE = {color: 'rgba(148, 206, 187, 1)'};
const OBJECT_BOOL_FALSE = {color: 'rgba(214, 82, 49, 1)'};
const OBJECT_UNDEFINED_STYLE = {color: '#ffffff3a'};
export class ComponentLogger { export class ComponentLogger {
private logAggregator: LogAggregator; private logAggregator: LogAggregator;
private component: string; private component: string;
@ -25,24 +33,232 @@ export class ComponentLogger {
this.componentOptions = componentOptions; this.componentOptions = componentOptions;
} }
private parseStyle(s?: string) {
if (!s) {
return;
}
const segments = s.split(';').map(x =>x.trim());
const out = {};
for (const sg of segments) {
const [key, value] = sg.split(':', 1);
out[key] = value;
}
return out;
}
private mix(base?: any, style?: any) {
if (!base && !style) {
return undefined;
}
const combined = {...base, ...style};
let out = [];
for (const key in combined) {
out.push(`${key}: ${combined[key]}`);
}
return out.join('; ');
}
private generateObjectPreview(obj: any, baseStyle?: any) {
const maxKeys = 5;
const colorArgs = [this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)];
const objKeys = [];
let keyCount = 0;
for (const key of Object.keys(obj)) {
if (keyCount >= maxKeys) {
objKeys.push('...');
break;
}
if (typeof obj[key] === 'object') {
if (obj[key] === null) {
objKeys.push(` %c${key}%c: %c${obj[key]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_KEY_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE),
this.mix(baseStyle, OBJECT_NUMBER_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
} else {
if (Array.isArray(obj[key])) {
objKeys.push(`%c${key}%c: [...]`);
} else {
objKeys.push(`%c${key}%c: {...}`);
}
colorArgs.push(
this.mix(baseStyle, OBJECT_KEY_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
}
} else {
if (typeof obj[key] === 'number' ) {
objKeys.push(`%c${key}%c: %c${obj[key]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_KEY_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE),
this.mix(baseStyle, OBJECT_NUMBER_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
} else if (typeof obj[key] === 'boolean') {
objKeys.push(`%c${key}%c: %c${obj[key]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_KEY_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE),
this.mix(baseStyle, obj[key] ? OBJECT_BOOL_TRUE : OBJECT_BOOL_FALSE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
} else if (typeof obj[key] === 'undefined') {
objKeys.push(`%c${key}%c: %o`);
colorArgs.push(
this.mix(baseStyle, OBJECT_KEY_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE),
obj[key]
);
} else {
objKeys.push(`%c${key}%c: %c${obj[key]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_KEY_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE),
this.mix(baseStyle, OBJECT_STRING_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
}
}
keyCount++;
}
// unset colors
colorArgs.push(baseStyle ? this.mix(baseStyle, undefined) : '');
return {
str: `%c{ ${objKeys.join(', ')} }%c`,
colors: colorArgs
}
}
private generateArrayPreview(obj: any[], baseStyle?: any) {
const maxKeys = 5;
const limit = Math.min(obj.length, maxKeys);
const arrValues = [];
const colorArgs = [this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)];
for (let i = 0; i < limit; i++) {
if (typeof obj[i] === 'object') {
if (obj[i] === null) {
arrValues.push(`%c${obj[i]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_NUMBER_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
} if (Array.isArray(obj[i])) {
arrValues.push(` [...]`);
} else {
arrValues.push(` {...}`);
}
} else {
if (typeof obj[i] === 'number' ) {
arrValues.push(`%c${obj[i]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_NUMBER_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
} else if (typeof obj[i] === 'boolean') {
arrValues.push(`%c${obj[i]}%c`);
colorArgs.push(
this.mix(baseStyle, obj[i] ? OBJECT_BOOL_TRUE : OBJECT_BOOL_FALSE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
} else if (typeof obj[i] === 'undefined') {
arrValues.push(`%o`);
colorArgs.push(
obj[i],
);
} else {
arrValues.push(`%c${obj[i]}%c`);
colorArgs.push(
this.mix(baseStyle, OBJECT_STRING_STYLE),
this.mix(baseStyle, OBJECT_SEPARATOR_STYLE)
);
}
}
}
if (obj.length > limit) {
arrValues.push('...');
}
// unset colors
colorArgs.push(baseStyle ? this.mix(baseStyle, undefined) : '');
return {
str: `%c[ ${arrValues.join(', ')} ]%c`,
colors: colorArgs
}
}
private handleLog(logLevel: LogLevel, sourceFunction: string | LogSourceOptions, ...message: any) { private handleLog(logLevel: LogLevel, sourceFunction: string | LogSourceOptions, ...message: any) {
if (!this.logAggregator.canLog(this.component)) {
return;
}
let functionSource = typeof sourceFunction === 'string' ? sourceFunction : sourceFunction?.src; let functionSource = typeof sourceFunction === 'string' ? sourceFunction : sourceFunction?.src;
let consoleMessageString = `[${this.component}${functionSource ? `::${functionSource}` : ''}]`; let consoleMessageString = `[${this.component}${functionSource ? `::${functionSource}` : ''}]`;
const consoleMessageData = [] const consoleMessageData = []
const styleObj = this.parseStyle(this.componentOptions?.styles?.[logLevel] ?? this.componentOptions?.styles?.[LogLevel.Log]);
let styleArgsCount = 0;
for (const m of message) { for (const m of message) {
if (styleArgsCount --> 0) {
consoleMessageData.push(m);
continue;
}
if (typeof m === 'string') { if (typeof m === 'string') {
consoleMessageString = `${consoleMessageString} ${m}`; consoleMessageString = `${consoleMessageString} ${m}`;
styleArgsCount = m.split('%c').length - 1;
} else if (typeof m === 'number') { } else if (typeof m === 'number') {
consoleMessageString = `${consoleMessageString} %f`; consoleMessageString = `${consoleMessageString} %c${m}%c`;
consoleMessageData.push(m); consoleMessageData.push(
this.mix(styleObj, OBJECT_NUMBER_STYLE),
''
);
} else if (typeof m === 'undefined') {
consoleMessageString = `${consoleMessageString} %c%o%c`;
consoleMessageData.push(
this.mix(styleObj, OBJECT_UNDEFINED_STYLE),
m,
''
);
} else if (typeof HTMLElement !== 'undefined' && m instanceof HTMLElement) { // HTMLElement does not exist in background script, but this class may } else if (typeof HTMLElement !== 'undefined' && m instanceof HTMLElement) { // HTMLElement does not exist in background script, but this class may
consoleMessageString = `${consoleMessageString} %o`; consoleMessageString = `${consoleMessageString} %o`;
consoleMessageData.push(m); consoleMessageData.push(m);
} else { } else {
consoleMessageString = `${consoleMessageString} %O`; if (m === null) {
consoleMessageData.push(m); consoleMessageString = `${consoleMessageString} %c${m}%c`;
consoleMessageData.push(
this.mix(styleObj, OBJECT_NUMBER_STYLE),
m,
''
);
} else if (Array.isArray(m)) {
const {str, colors} = this.generateArrayPreview(m, styleObj);
consoleMessageString = `${consoleMessageString} ${str}%O`;
consoleMessageData.push(...colors, m);
} else {
const {str, colors} = this.generateObjectPreview(m, styleObj);
consoleMessageString = `${consoleMessageString} ${str}%O`;
consoleMessageData.push(...colors, m);
}
} }
} }
@ -52,7 +268,7 @@ export class ComponentLogger {
consoleMessageData.unshift(style); consoleMessageData.unshift(style);
} }
this.logAggregator.log(this.component, logLevel, typeof sourceFunction === 'object' ? sourceFunction : undefined, consoleMessageString, ...consoleMessageData); this.logAggregator.log(logLevel, consoleMessageString, ...consoleMessageData);
} }
debug(sourceFunction: string | LogSourceOptions, ...message: any[]) { debug(sourceFunction: string | LogSourceOptions, ...message: any[]) {

View File

@ -4,6 +4,29 @@ export const BLANK_LOGGER_CONFIG: LogConfig = {
logToConsole: false, logToConsole: false,
logToFile: false, logToFile: false,
component: { 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},
}, },
origins: { origins: {
videoRescan: { disabled: true}, videoRescan: { disabled: true},
@ -41,6 +64,10 @@ export class LogAggregator {
history: any[]; history: any[];
static async resetConfig() {
await this.saveConfig(BLANK_LOGGER_CONFIG);
}
static async getConfig() { static async getConfig() {
let ret: any = await chrome.storage.local.get(STORAGE_LOG_SETTINGS_KEY); let ret: any = await chrome.storage.local.get(STORAGE_LOG_SETTINGS_KEY);
@ -94,7 +121,7 @@ export class LogAggregator {
this.init(); this.init();
} }
private canLog(component: string, sourceOptions?: LogSourceOptions): boolean { canLog(component: string, sourceOptions?: LogSourceOptions): boolean {
// component is not in config, so we add a blank entry // component is not in config, so we add a blank entry
if (this.config && !this.config.component[component]) { if (this.config && !this.config.component[component]) {
this.config.component[component] = {enabled: false}; this.config.component[component] = {enabled: false};
@ -215,13 +242,17 @@ export class LogAggregator {
} }
} }
log(component: string, logLevel: string, sourceOptions: LogSourceOptions, message: string, ...data: any[]) { /**
if (! this.canLog(component, sourceOptions)) { * Logs message to console.
return; * 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[]) {
if (this.config.logToFile) { if (this.config.logToFile) {
console.warn('log to file not implemented!')
} }
if (this.config.logToConsole){ if (this.config.logToConsole){