* Don't require auth for .ping * Finally introducing loggers * Remove file list caching from disk_space_primitives I don't think this is required anymore with the sync model * Simplification of space primitives and more tests * More reliable online status * Add .zed settings * Remove selfUpdate argument from `writeFile` no longer used - Add `sync` config schema and document sync options - Move sync config handling to client boot logic - Move BootConfig type to ui_types.ts - Remove sync env vars from server - Update documentation for sync and architecture * Improve sync logic and proxy routing for full sync state * Add BoxProxy for late-bound client and improve Lua boot - Add isMobile syscall for editor * Add concurrency to sync process and log indexing duration * Better service worker lifecycle management
106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
// Logger that monkey patches console methods with prefixes and can capture logs for server transmission
|
|
|
|
export interface LogEntry {
|
|
level: "log" | "info" | "warn" | "error" | "debug";
|
|
timestamp: number;
|
|
message: string;
|
|
args: any[];
|
|
}
|
|
|
|
export class Logger {
|
|
private originalConsole: {
|
|
log: typeof console.log;
|
|
info: typeof console.info;
|
|
warn: typeof console.warn;
|
|
error: typeof console.error;
|
|
debug: typeof console.debug;
|
|
};
|
|
private logCapture: LogEntry[] = [];
|
|
|
|
constructor(
|
|
private prefix: string = "",
|
|
private maxCaptureSize: number = 1000,
|
|
) {
|
|
this.prefix = prefix;
|
|
|
|
// Store original console methods
|
|
this.originalConsole = {
|
|
log: console.log.bind(console),
|
|
info: console.info.bind(console),
|
|
warn: console.warn.bind(console),
|
|
error: console.error.bind(console),
|
|
debug: console.debug.bind(console),
|
|
};
|
|
|
|
this.patchConsole();
|
|
}
|
|
|
|
private patchConsole(): void {
|
|
const createPatchedMethod = (level: keyof typeof this.originalConsole) => {
|
|
return (...args: any[]) => {
|
|
const prefixedArgs = this.prefix ? [this.prefix, ...args] : args;
|
|
|
|
// Call original console method
|
|
this.originalConsole[level](...prefixedArgs);
|
|
|
|
// Capture log if capturing is enabled
|
|
this.captureLog(level, args);
|
|
};
|
|
};
|
|
|
|
console.log = createPatchedMethod("log");
|
|
console.info = createPatchedMethod("info");
|
|
console.warn = createPatchedMethod("warn");
|
|
console.error = createPatchedMethod("error");
|
|
console.debug = createPatchedMethod("debug");
|
|
}
|
|
|
|
private captureLog(level: LogEntry["level"], args: any[]): void {
|
|
const entry: LogEntry = {
|
|
level,
|
|
timestamp: Date.now(),
|
|
message: args.map((arg) => {
|
|
if (typeof arg === "string") {
|
|
return arg;
|
|
}
|
|
try {
|
|
return JSON.stringify(arg);
|
|
} catch {
|
|
// Handle circular references or other JSON.stringify failures
|
|
return String(arg);
|
|
}
|
|
}).join(" "),
|
|
args: args.map((arg) => {
|
|
// Serialize complex objects for safe transmission
|
|
try {
|
|
return typeof arg === "object"
|
|
? JSON.parse(JSON.stringify(arg))
|
|
: arg;
|
|
} catch {
|
|
// This may fail due to recursive structures, in that case just fall back to a best-effort string
|
|
return String(arg);
|
|
}
|
|
}),
|
|
};
|
|
|
|
this.logCapture.push(entry);
|
|
|
|
// Maintain max capture size by removing oldest entries
|
|
if (this.logCapture.length > this.maxCaptureSize) {
|
|
this.logCapture.shift();
|
|
}
|
|
}
|
|
|
|
getCapturedLogs(): LogEntry[] {
|
|
return [...this.logCapture];
|
|
}
|
|
}
|
|
|
|
// Global logger instance
|
|
let globalLogger: Logger | undefined = undefined;
|
|
|
|
export function initLogger(prefix: string = ""): Logger {
|
|
globalLogger = new Logger(prefix);
|
|
return globalLogger;
|
|
}
|