mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 20:32:20 +03:00
- Consolidated 4-tab Logs page: Request Logs, Proxy Logs, Audit Logs, Console - Terminal-style Console Log Viewer with level filter, search, auto-scroll - Console interceptor captures all console.log/warn/error to JSON log file - Integrated in Next.js instrumentation.ts for both dev and prod - Log rotation by size + retention-based cleanup - Fixed pino logger file transport (pino/file targets only) - Moved initAuditLog() to instrumentation.ts for proper initialization - Bumped version to 1.0.3 - Updated CHANGELOG, all 8 README translations, and .env.example
128 lines
3.2 KiB
TypeScript
128 lines
3.2 KiB
TypeScript
/**
|
|
* Console Log Interceptor — captures console output to a log file.
|
|
*
|
|
* Monkey-patches console.log, console.info, console.warn, console.error,
|
|
* and console.debug to also append JSON log entries to a file. This allows
|
|
* the Console Log Viewer to display application logs in real-time.
|
|
*
|
|
* Call initConsoleInterceptor() once at server startup (before any logging).
|
|
*
|
|
* @module lib/consoleInterceptor
|
|
*/
|
|
|
|
import { appendFileSync, existsSync, mkdirSync } from "fs";
|
|
import { dirname, resolve } from "path";
|
|
|
|
const logToFile = process.env.LOG_TO_FILE !== "false";
|
|
const logFilePath = resolve(process.env.LOG_FILE_PATH || "logs/application/app.log");
|
|
|
|
let initialized = false;
|
|
|
|
/**
|
|
* Map console method names to log levels.
|
|
*/
|
|
const LEVEL_MAP: Record<string, string> = {
|
|
debug: "debug",
|
|
log: "info",
|
|
info: "info",
|
|
warn: "warn",
|
|
error: "error",
|
|
};
|
|
|
|
/**
|
|
* Ensure the log directory exists.
|
|
*/
|
|
function ensureDir() {
|
|
const dir = dirname(logFilePath);
|
|
if (!existsSync(dir)) {
|
|
mkdirSync(dir, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to extract component name from message patterns like [COMPONENT] or [component].
|
|
*/
|
|
function extractComponent(msg: string): string {
|
|
const match = msg.match(/^\[([^\]]+)\]/);
|
|
return match ? match[1] : "app";
|
|
}
|
|
|
|
/**
|
|
* Convert arguments to a string message, handling objects and errors.
|
|
*/
|
|
function argsToMessage(args: unknown[]): string {
|
|
return args
|
|
.map((arg) => {
|
|
if (arg instanceof Error) return `${arg.message}\n${arg.stack || ""}`;
|
|
if (typeof arg === "object" && arg !== null) {
|
|
try {
|
|
return JSON.stringify(arg);
|
|
} catch {
|
|
return String(arg);
|
|
}
|
|
}
|
|
return String(arg);
|
|
})
|
|
.join(" ");
|
|
}
|
|
|
|
/**
|
|
* Append a JSON log entry to the log file.
|
|
*/
|
|
function writeEntry(level: string, args: unknown[]) {
|
|
try {
|
|
const message = argsToMessage(args);
|
|
const entry = {
|
|
timestamp: new Date().toISOString(),
|
|
level,
|
|
component: extractComponent(message),
|
|
message,
|
|
};
|
|
appendFileSync(logFilePath, JSON.stringify(entry) + "\n");
|
|
} catch {
|
|
// Silently fail — never break the app over log writing
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the console interceptor.
|
|
* Patches console.log, console.info, console.warn, console.error, console.debug
|
|
* to also write to the log file.
|
|
*
|
|
* Safe to call multiple times — only initializes once.
|
|
*/
|
|
export function initConsoleInterceptor(): void {
|
|
if (!logToFile || initialized) return;
|
|
|
|
try {
|
|
ensureDir();
|
|
} catch {
|
|
// Can't create log dir — skip interception
|
|
return;
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
// Save original methods
|
|
const originalMethods = {
|
|
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),
|
|
};
|
|
|
|
// Patch each console method
|
|
for (const [method, level] of Object.entries(LEVEL_MAP)) {
|
|
const original = originalMethods[method as keyof typeof originalMethods];
|
|
if (!original) continue;
|
|
|
|
(console as unknown as Record<string, unknown>)[method] = (...args: unknown[]) => {
|
|
// Call original console method
|
|
original(...args);
|
|
// Also write to file
|
|
writeEntry(level, args);
|
|
};
|
|
}
|
|
}
|