1
0
peddlers-of-ketran/server/console-line.ts

74 lines
2.9 KiB
TypeScript
Executable File

/* monkey-patch console methods to prefix messages with file:line for easier logs */
(() => {
const cwd = process.cwd();
const cwdRe = new RegExp("^[^/]*" + cwd.replace("/", "\\/") + "/([^:]*:[0-9]*).*$");
const methods = ["log", "warn", "error", "info", "debug"] as const;
function getCallerFileLine(): string {
try {
// Create an Error to capture stack
const err = new Error();
if (!err.stack) return "unknown:0 -";
const lines = err.stack.split("\n").slice(1);
// Find the first stack line that is not this file
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!line) continue;
if (line.indexOf("console-line") !== -1) continue; // skip this helper
// Try to extract file:line from the line. Use a stricter capture so we
// don't accidentally include leading whitespace or the 'at' token.
const m = line.match(/\(?(\S+:\d+:\d+)\)?$/);
if (m && m[1]) {
return m[1].trim() + " -";
}
}
// Fallback: try to extract file:line:col from the third stack line even
// if it contains leading whitespace and the 'at' prefix. If that fails,
// fall back to the cwd-based replace and trim whitespace.
const fallback = err.stack.split("\n")[3] || "";
const m2 = fallback.match(/\(?(\S+:\d+:\d+)\)?$/);
if (m2 && m2[1]) return m2[1].trim() + " -";
const replaced = fallback.replace(cwdRe, "$1 -").trim();
return replaced || "unknown:0 -";
} catch (e) {
return "unknown:0 -";
}
}
methods.forEach((method) => {
const orig = (console as any)[method] || console.log;
(console as any)[method] = function (...args: any[]) {
try {
const prefix = getCallerFileLine();
// Separate Error objects from other args so we can print their stacks
// line-by-line with the same prefix. This keeps stack traces intact
// while ensuring every printed line shows the caller prefix.
const errorArgs = args.filter((a: any) => a instanceof Error) as Error[];
const otherArgs = args.filter((a: any) => !(a instanceof Error));
// Print non-error args in a single call (preserving original formatting)
const processedOther = otherArgs.map((a: any) => (a instanceof Error ? a.stack || a.toString() : a));
if (processedOther.length > 0) {
orig.apply(this, [prefix, ...processedOther]);
}
// For each Error, print each line of its stack as a separate prefixed log
// entry so lines that begin with ' at' are not orphaned.
errorArgs.forEach((err) => {
const stack = err.stack || err.toString();
stack.split("\n").forEach((line) => {
orig.apply(this, [prefix, line]);
});
});
} catch (e) {
try {
orig.apply(this, args);
} catch (e2) {
/* swallow */
}
}
};
});
})();