feat(log_system): restructure console log output with color-coded table format and module name splitting

- Rename LOG_MODULE_NAME from 'model-linker' to 'MLinker' for shorter prefixes
- Add USE_COLORS config option to control styled console output
- Refine log level color palette: purple/debug, green/info, amber/warn, red/error
- Update timestamp format to HH:mm:ss.SSS for millisecond precision
- Refactor console log rendering to a color-coded table layout with background
  highlights on timestamps and logger name columns
- Add splitModuleName helper to separate root module from dotted sub-namespace
- Add stringifyArgs helper for safe serialization of non-string log arguments
  (strings, errors, objects via JSON.stringify, fallback to String)
- Update serializeLogEntry and console output to use the new leveled layout
  (levelName padded to fixed width)
This commit is contained in:
Dariusz L
2026-05-19 07:40:47 +02:00
parent bcd947e37d
commit b532d83ae7
2 changed files with 60 additions and 12 deletions
+4 -1
View File
@@ -9,7 +9,10 @@ description: Logging Configuration
export const LOG_LEVEL = 'DEBUG';
// Logger module name. Set to null to use the default project name.
export const LOG_MODULE_NAME = "model-linker";
export const LOG_MODULE_NAME = "MLinker";
// Default project/logger name used when LOG_MODULE_NAME is not set.
export const DEFAULT_LOGGER_NAME = 'az-logs';
// Enable styled console logs.
export const USE_COLORS = true;
+56 -11
View File
@@ -13,7 +13,7 @@ Logger - Central logging system for comfyui-model-linker
- Ability to export logs
*/
import { DEFAULT_LOGGER_NAME, LOG_MODULE_NAME } from './config.js';
import { DEFAULT_LOGGER_NAME, LOG_MODULE_NAME, USE_COLORS } from './config.js';
function padStart(str, targetLength, padString) {
targetLength = targetLength >> 0;
@@ -55,17 +55,17 @@ export const LogLevel = {
const DEFAULT_CONFIG = {
globalLevel: LogLevel.INFO,
moduleSettings: {},
useColors: true,
useColors: USE_COLORS,
saveToStorage: false,
maxStoredLogs: 1000,
timestampFormat: 'HH:mm:ss',
timestampFormat: 'HH:mm:ss.SSS',
storageKey: LOGGER_STORAGE_KEY
};
const COLORS = {
[LogLevel.DEBUG]: '#9e9e9e',
[LogLevel.INFO]: '#2196f3',
[LogLevel.WARN]: '#ff9800',
[LogLevel.ERROR]: '#f44336',
[LogLevel.DEBUG]: '#9B59B6',
[LogLevel.INFO]: '#2ECC71',
[LogLevel.WARN]: '#F39C12',
[LogLevel.ERROR]: '#C0392B',
};
const LEVEL_NAMES = {
[LogLevel.DEBUG]: 'DEBUG',
@@ -79,6 +79,9 @@ const CONSOLE_METHODS = {
[LogLevel.WARN]: 'warn',
[LogLevel.ERROR]: 'error',
};
const TIME_BG = '#263f4c';
const LEVEL_WIDTH = 5;
class Logger {
constructor() {
this.config = { ...DEFAULT_CONFIG };
@@ -184,17 +187,59 @@ class Logger {
*/
printToConsole(logData) {
const { timestamp, module, level, levelName, args } = logData;
const prefix = `[${timestamp}] [${module}] [${levelName}]`;
const consoleMethod = CONSOLE_METHODS[level] || 'log';
const consoleFn = typeof console[consoleMethod] === 'function'
? console[consoleMethod].bind(console)
: console.log.bind(console);
if (this.config.useColors && typeof consoleFn === 'function') {
const color = COLORS[level] || '#000000';
consoleFn(`%c${prefix}`, `color: ${color}; font-weight: bold;`, ...args);
const { root, detail } = this.splitModuleName(module);
const message = this.stringifyArgs(args);
const suffix = detail ? ` (${detail})` : '';
consoleFn(
`%c ${levelName.padEnd(LEVEL_WIDTH, ' ')} %c ${timestamp} %c %c ${root} %c %c${message}${suffix}`,
`background:${color};color:#fff;font-weight:bold;`,
`background:${TIME_BG};color:#fff;font-weight:bold;`,
`background:${color};color:${color};`,
`background:${TIME_BG};color:#fff;font-weight:bold;`,
`background:${color};color:${color};`,
`color:${color};font-weight:bold;`,
);
return;
}
consoleFn(prefix, ...args);
const { root, detail } = this.splitModuleName(module);
const suffix = detail ? ` (${detail})` : '';
consoleFn(`${levelName.padEnd(LEVEL_WIDTH, ' ')} ${timestamp} ${root} ${this.stringifyArgs(args)}${suffix}`);
}
splitModuleName(module) {
const value = String(module || LOGGER_NAME);
const parts = value.split('.');
const root = parts.shift() || LOGGER_NAME;
return {
root,
detail: parts.join('.')
};
}
stringifyArgs(args = []) {
return args.map((arg) => {
if (typeof arg === 'string') {
return arg;
}
if (arg instanceof Error) {
return arg.stack || arg.message;
}
if (typeof arg === 'object' && arg !== null) {
try {
return JSON.stringify(arg);
}
catch (e) {
return String(arg);
}
}
return String(arg);
}).join(' ');
}
serializeLogEntry(log) {
return {
@@ -331,7 +376,7 @@ class Logger {
extension = 'json';
}
else {
content = this.logs.map((log) => `[${log.timestamp}] [${log.module}] [${log.levelName}] ${log.args.join(' ')}`).join('\n');
content = this.logs.map((log) => `${log.levelName.padEnd(LEVEL_WIDTH, ' ')} ${log.timestamp} ${log.module} ${this.stringifyArgs(log.args)}`).join('\n');
mimeType = 'text/plain';
extension = 'txt';
}