291 lines
9.1 KiB
TypeScript
291 lines
9.1 KiB
TypeScript
import { LogAggregator, LogSourceOptions } from './LogAggregator';
|
|
|
|
export enum LogLevel {
|
|
Debug = 'debug',
|
|
Info = 'info',
|
|
Log = 'log',
|
|
Warn = 'warn',
|
|
Error = 'error',
|
|
}
|
|
|
|
export type ComponentLoggerOptions = {
|
|
styles?: {
|
|
[x in LogLevel]?: string;
|
|
}
|
|
}
|
|
|
|
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 {
|
|
private logAggregator: LogAggregator;
|
|
private component: string;
|
|
private componentOptions?: ComponentLoggerOptions;
|
|
|
|
constructor(logAggregator: LogAggregator, component: string, componentOptions?: ComponentLoggerOptions) {
|
|
this.logAggregator = logAggregator;
|
|
this.component = component;
|
|
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) {
|
|
|
|
if (!this.logAggregator.canLog(this.component)) {
|
|
return;
|
|
}
|
|
|
|
let functionSource = typeof sourceFunction === 'string' ? sourceFunction : sourceFunction?.src;
|
|
|
|
let consoleMessageString = `[${this.component}${functionSource ? `::${functionSource}` : ''}]`;
|
|
const consoleMessageData = []
|
|
|
|
const styleObj = this.parseStyle(this.componentOptions?.styles?.[logLevel] ?? this.componentOptions?.styles?.[LogLevel.Log]);
|
|
|
|
let styleArgsCount = 0;
|
|
|
|
for (const m of message) {
|
|
if (styleArgsCount --> 0) {
|
|
consoleMessageData.push(m);
|
|
continue;
|
|
}
|
|
|
|
if (typeof m === 'string') {
|
|
consoleMessageString = `${consoleMessageString} ${m}`;
|
|
|
|
styleArgsCount = m.split('%c').length - 1;
|
|
} else if (typeof m === 'number') {
|
|
consoleMessageString = `${consoleMessageString} %c${m}%c`;
|
|
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
|
|
consoleMessageString = `${consoleMessageString} %o`;
|
|
consoleMessageData.push(m);
|
|
} else {
|
|
if (m === null) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
const style = this.componentOptions?.styles?.[logLevel] ?? this.componentOptions?.styles?.[LogLevel.Log];
|
|
if (style) {
|
|
consoleMessageString = `%c${consoleMessageString}`;
|
|
consoleMessageData.unshift(style);
|
|
}
|
|
|
|
this.logAggregator.log(logLevel, consoleMessageString, ...consoleMessageData);
|
|
}
|
|
|
|
debug(sourceFunction: string | LogSourceOptions, ...message: any[]) {
|
|
this.handleLog(LogLevel.Debug, sourceFunction, ...message);
|
|
}
|
|
info(sourceFunction: string | LogSourceOptions, ...message: any[]) {
|
|
this.handleLog(LogLevel.Info, sourceFunction, ...message);
|
|
}
|
|
log(sourceFunction: string | LogSourceOptions, ...message: any[]) {
|
|
this.handleLog(LogLevel.Log, sourceFunction, ...message);
|
|
}
|
|
warn(sourceFunction: string | LogSourceOptions, ...message: any[]) {
|
|
this.handleLog(LogLevel.Warn, sourceFunction, ...message);
|
|
}
|
|
error(sourceFunction: string | LogSourceOptions, ...message: any[]) {
|
|
this.handleLog(LogLevel.Error, sourceFunction, ...message);
|
|
}
|
|
|
|
}
|