Initial commit
This commit is contained in:
322
core/debug-core.js
Normal file
322
core/debug-core.js
Normal file
@@ -0,0 +1,322 @@
|
||||
import { EventCenter } from "./event-manager.js";
|
||||
|
||||
const DEFAULT_MAX_LOGS = 200;
|
||||
|
||||
function now() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
function safeStringify(value) {
|
||||
try {
|
||||
if (typeof value === "string") return value;
|
||||
return JSON.stringify(value);
|
||||
} catch {
|
||||
try {
|
||||
return String(value);
|
||||
} catch {
|
||||
return "[unstringifiable]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function errorToStack(err) {
|
||||
try {
|
||||
if (!err) return null;
|
||||
if (typeof err === "string") return err;
|
||||
if (err && typeof err.stack === "string") return err.stack;
|
||||
return safeStringify(err);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class LoggerCore {
|
||||
constructor() {
|
||||
this._enabled = false;
|
||||
this._buffer = [];
|
||||
this._maxSize = DEFAULT_MAX_LOGS;
|
||||
this._seq = 0;
|
||||
this._originalConsole = null;
|
||||
this._originalOnError = null;
|
||||
this._originalOnUnhandledRejection = null;
|
||||
this._mounted = false;
|
||||
}
|
||||
|
||||
setMaxSize(n) {
|
||||
const v = Number.parseInt(n, 10);
|
||||
if (Number.isFinite(v) && v > 0) this._maxSize = v;
|
||||
if (this._buffer.length > this._maxSize) {
|
||||
this._buffer.splice(0, this._buffer.length - this._maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
return !!this._enabled;
|
||||
}
|
||||
|
||||
enable() {
|
||||
if (this._enabled) return;
|
||||
this._enabled = true;
|
||||
this._mountGlobalHooks();
|
||||
}
|
||||
|
||||
disable() {
|
||||
this._enabled = false;
|
||||
this.clear();
|
||||
this._unmountGlobalHooks();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._buffer.length = 0;
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this._buffer.slice();
|
||||
}
|
||||
|
||||
export() {
|
||||
return JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
exportedAt: now(),
|
||||
maxSize: this._maxSize,
|
||||
logs: this.getAll(),
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
_push(entry) {
|
||||
if (!this._enabled) return;
|
||||
this._buffer.push(entry);
|
||||
if (this._buffer.length > this._maxSize) {
|
||||
this._buffer.splice(0, this._buffer.length - this._maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
_log(level, moduleId, message, err) {
|
||||
if (!this._enabled) return;
|
||||
const id = ++this._seq;
|
||||
const timestamp = now();
|
||||
const stack = err ? errorToStack(err) : null;
|
||||
this._push({
|
||||
id,
|
||||
timestamp,
|
||||
level,
|
||||
module: moduleId || "unknown",
|
||||
message: typeof message === "string" ? message : safeStringify(message),
|
||||
stack,
|
||||
});
|
||||
}
|
||||
|
||||
info(moduleId, message) {
|
||||
this._log("info", moduleId, message, null);
|
||||
}
|
||||
|
||||
warn(moduleId, message) {
|
||||
this._log("warn", moduleId, message, null);
|
||||
}
|
||||
|
||||
error(moduleId, message, err) {
|
||||
this._log("error", moduleId, message, err || null);
|
||||
}
|
||||
|
||||
_mountGlobalHooks() {
|
||||
if (this._mounted) return;
|
||||
this._mounted = true;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
try {
|
||||
this._originalOnError = window.onerror;
|
||||
} catch {}
|
||||
try {
|
||||
this._originalOnUnhandledRejection = window.onunhandledrejection;
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
window.onerror = (message, source, lineno, colno, error) => {
|
||||
try {
|
||||
const loc = source ? `${source}:${lineno || 0}:${colno || 0}` : "";
|
||||
this.error("window", `${String(message || "error")} ${loc}`.trim(), error || null);
|
||||
} catch {}
|
||||
try {
|
||||
if (typeof this._originalOnError === "function") {
|
||||
return this._originalOnError(message, source, lineno, colno, error);
|
||||
}
|
||||
} catch {}
|
||||
return false;
|
||||
};
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
window.onunhandledrejection = (event) => {
|
||||
try {
|
||||
const reason = event?.reason;
|
||||
this.error("promise", "Unhandled promise rejection", reason || null);
|
||||
} catch {}
|
||||
try {
|
||||
if (typeof this._originalOnUnhandledRejection === "function") {
|
||||
return this._originalOnUnhandledRejection(event);
|
||||
}
|
||||
} catch {}
|
||||
return undefined;
|
||||
};
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (typeof console !== "undefined" && console) {
|
||||
this._originalConsole = this._originalConsole || {
|
||||
warn: console.warn?.bind(console),
|
||||
error: console.error?.bind(console),
|
||||
};
|
||||
|
||||
try {
|
||||
if (typeof this._originalConsole.warn === "function") {
|
||||
console.warn = (...args) => {
|
||||
try {
|
||||
const msg = args.map(a => (typeof a === "string" ? a : safeStringify(a))).join(" ");
|
||||
this.warn("console", msg);
|
||||
} catch {}
|
||||
return this._originalConsole.warn(...args);
|
||||
};
|
||||
}
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
if (typeof this._originalConsole.error === "function") {
|
||||
console.error = (...args) => {
|
||||
try {
|
||||
const msg = args.map(a => (typeof a === "string" ? a : safeStringify(a))).join(" ");
|
||||
this.error("console", msg, null);
|
||||
} catch {}
|
||||
return this._originalConsole.error(...args);
|
||||
};
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
_unmountGlobalHooks() {
|
||||
if (!this._mounted) return;
|
||||
this._mounted = false;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
try {
|
||||
if (this._originalOnError !== null && this._originalOnError !== undefined) {
|
||||
window.onerror = this._originalOnError;
|
||||
} else {
|
||||
window.onerror = null;
|
||||
}
|
||||
} catch {}
|
||||
try {
|
||||
if (this._originalOnUnhandledRejection !== null && this._originalOnUnhandledRejection !== undefined) {
|
||||
window.onunhandledrejection = this._originalOnUnhandledRejection;
|
||||
} else {
|
||||
window.onunhandledrejection = null;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (typeof console !== "undefined" && console && this._originalConsole) {
|
||||
try {
|
||||
if (this._originalConsole.warn) console.warn = this._originalConsole.warn;
|
||||
} catch {}
|
||||
try {
|
||||
if (this._originalConsole.error) console.error = this._originalConsole.error;
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const logger = new LoggerCore();
|
||||
|
||||
export const xbLog = {
|
||||
enable: () => logger.enable(),
|
||||
disable: () => logger.disable(),
|
||||
isEnabled: () => logger.isEnabled(),
|
||||
setMaxSize: (n) => logger.setMaxSize(n),
|
||||
info: (moduleId, message) => logger.info(moduleId, message),
|
||||
warn: (moduleId, message) => logger.warn(moduleId, message),
|
||||
error: (moduleId, message, err) => logger.error(moduleId, message, err),
|
||||
getAll: () => logger.getAll(),
|
||||
clear: () => logger.clear(),
|
||||
export: () => logger.export(),
|
||||
};
|
||||
|
||||
export const CacheRegistry = (() => {
|
||||
const _registry = new Map();
|
||||
|
||||
function register(moduleId, cacheInfo) {
|
||||
if (!moduleId || !cacheInfo || typeof cacheInfo !== "object") return;
|
||||
_registry.set(String(moduleId), cacheInfo);
|
||||
}
|
||||
|
||||
function unregister(moduleId) {
|
||||
if (!moduleId) return;
|
||||
_registry.delete(String(moduleId));
|
||||
}
|
||||
|
||||
function getStats() {
|
||||
const out = [];
|
||||
for (const [moduleId, info] of _registry.entries()) {
|
||||
let size = null;
|
||||
let bytes = null;
|
||||
let name = null;
|
||||
let hasDetail = false;
|
||||
try { name = info?.name || moduleId; } catch { name = moduleId; }
|
||||
try { size = typeof info?.getSize === "function" ? info.getSize() : null; } catch { size = null; }
|
||||
try { bytes = typeof info?.getBytes === "function" ? info.getBytes() : null; } catch { bytes = null; }
|
||||
try { hasDetail = typeof info?.getDetail === "function"; } catch { hasDetail = false; }
|
||||
out.push({ moduleId, name, size, bytes, hasDetail });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function getDetail(moduleId) {
|
||||
const info = _registry.get(String(moduleId));
|
||||
if (!info || typeof info.getDetail !== "function") return null;
|
||||
try {
|
||||
return info.getDetail();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function clear(moduleId) {
|
||||
const info = _registry.get(String(moduleId));
|
||||
if (!info || typeof info.clear !== "function") return false;
|
||||
try {
|
||||
info.clear();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
const results = {};
|
||||
for (const moduleId of _registry.keys()) {
|
||||
results[moduleId] = clear(moduleId);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
return { register, unregister, getStats, getDetail, clear, clearAll };
|
||||
})();
|
||||
|
||||
export function enableDebugMode() {
|
||||
xbLog.enable();
|
||||
try { EventCenter.enableDebug?.(); } catch {}
|
||||
}
|
||||
|
||||
export function disableDebugMode() {
|
||||
xbLog.disable();
|
||||
try { EventCenter.disableDebug?.(); } catch {}
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.xbLog = xbLog;
|
||||
window.xbCacheRegistry = CacheRegistry;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user