@ubiquity-os/ubiquity-os-logger
Version:
Ubiquity logging module with Supabase support.
360 lines (355 loc) • 9.79 kB
JavaScript
// src/constants.ts
var COLORS = {
reset: "\x1B[0m",
bright: "\x1B[1m",
dim: "\x1B[2m",
underscore: "\x1B[4m",
blink: "\x1B[5m",
reverse: "\x1B[7m",
hidden: "\x1B[8m",
fgBlack: "\x1B[30m",
fgRed: "\x1B[31m",
fgGreen: "\x1B[32m",
fgYellow: "\x1B[33m",
fgBlue: "\x1B[34m",
fgMagenta: "\x1B[35m",
fgCyan: "\x1B[36m",
fgWhite: "\x1B[37m",
bgBlack: "\x1B[40m",
bgRed: "\x1B[41m",
bgGreen: "\x1B[42m",
bgYellow: "\x1B[43m",
bgBlue: "\x1B[44m",
bgMagenta: "\x1B[45m",
bgCyan: "\x1B[46m",
bgWhite: "\x1B[47m"
};
var LOG_LEVEL = {
FATAL: "fatal",
ERROR: "error",
WARN: "warn",
INFO: "info",
VERBOSE: "verbose",
DEBUG: "debug"
};
// src/pretty-logs.ts
var PrettyLogs = class {
constructor() {
this.ok = this.ok.bind(this);
this.info = this.info.bind(this);
this.error = this.error.bind(this);
this.fatal = this.fatal.bind(this);
this.warn = this.warn.bind(this);
this.debug = this.debug.bind(this);
this.verbose = this.verbose.bind(this);
}
fatal(message, metadata) {
this._logWithStack(LOG_LEVEL.FATAL, message, metadata);
}
error(message, metadata) {
this._logWithStack(LOG_LEVEL.ERROR, message, metadata);
}
warn(message, metadata) {
this._logWithStack(LOG_LEVEL.WARN, message, metadata);
}
ok(message, metadata) {
this._logWithStack("ok", message, metadata);
}
info(message, metadata) {
this._logWithStack(LOG_LEVEL.INFO, message, metadata);
}
debug(message, metadata) {
this._logWithStack(LOG_LEVEL.DEBUG, message, metadata);
}
verbose(message, metadata) {
this._logWithStack(LOG_LEVEL.VERBOSE, message, metadata);
}
_logWithStack(type, message, metaData) {
this._log(type, message);
if (typeof metaData === "string") {
this._log(type, metaData);
return;
}
if (metaData) {
const metadata = metaData;
let stack = metadata?.error?.stack || metadata?.stack;
if (!stack) {
const stackTrace = new Error().stack?.split("\n");
if (stackTrace) {
stackTrace.splice(0, 4);
stack = stackTrace.filter((line) => line.includes(".ts:")).join("\n");
}
}
const newMetadata = { ...metadata };
delete newMetadata.message;
delete newMetadata.name;
delete newMetadata.stack;
if (!this._isEmpty(newMetadata)) {
this._log(type, newMetadata);
}
if (typeof stack == "string") {
const prettyStack = this._formatStackTrace(stack, 1);
const colorizedStack = this._colorizeText(prettyStack, COLORS.dim);
this._log(type, colorizedStack);
} else if (stack) {
const prettyStack = this._formatStackTrace(stack.join("\n"), 1);
const colorizedStack = this._colorizeText(prettyStack, COLORS.dim);
this._log(type, colorizedStack);
} else {
throw new Error("Stack is null");
}
}
}
_colorizeText(text, color) {
if (!color) {
throw new Error(`Invalid color: ${color}`);
}
return color.concat(text).concat(COLORS.reset);
}
_formatStackTrace(stack, linesToRemove = 0, prefix = "") {
const lines = stack.split("\n");
for (let i = 0; i < linesToRemove; i++) {
lines.shift();
}
return lines.map((line) => `${prefix}${line.replace(/\s*at\s*/, " \u21B3 ")}`).join("\n");
}
_isEmpty(obj) {
return !Reflect.ownKeys(obj).some((key) => typeof obj[String(key)] !== "function");
}
_log(type, message) {
const defaultSymbols = {
fatal: "\xD7",
ok: "\u2713",
warn: "\u26A0",
error: "\u26A0",
info: "\u203A",
debug: "\u203A\u203A",
verbose: "\u{1F4AC}"
};
const symbol = defaultSymbols[type];
const messageFormatted = typeof message === "string" ? message : JSON.stringify(message, null, 2);
const lines = messageFormatted.split("\n");
const logString = lines.map((line, index) => {
const prefix = index === 0 ? ` ${symbol}` : ` ${" ".repeat(symbol.length)}`;
return `${prefix} ${line}`;
}).join("\n");
const fullLogString = logString;
const colorMap = {
fatal: ["error", COLORS.fgRed],
ok: ["log", COLORS.fgGreen],
warn: ["warn", COLORS.fgYellow],
error: ["warn", COLORS.fgYellow],
info: ["info", COLORS.dim],
debug: ["debug", COLORS.fgMagenta],
verbose: ["debug", COLORS.dim]
};
const _console = console[colorMap[type][0]];
if (typeof _console === "function" && fullLogString.length > 12) {
_console(this._colorizeText(fullLogString, colorMap[type][1]));
} else if (fullLogString.length <= 12) {
return;
} else {
throw new Error(fullLogString);
}
}
};
// src/types/log-types.ts
var LogReturn = class {
logMessage;
metadata;
constructor(logMessage, metadata) {
this.logMessage = logMessage;
this.metadata = metadata;
}
};
// src/logs.ts
var Logs = class _Logs {
_maxLevel = -1;
static console;
_log({ level, consoleLog, logMessage, metadata, type }) {
if (this._getNumericLevel(level) <= this._maxLevel) {
consoleLog(logMessage, metadata);
}
return new LogReturn(
{
raw: logMessage,
diff: this._diffColorCommentMessage(type, logMessage),
type,
level
},
metadata
);
}
_addDiagnosticInformation(metadata) {
if (!metadata) {
metadata = {};
} else if (typeof metadata !== "object") {
metadata = { message: metadata };
}
const stackLines = new Error().stack?.split("\n") || [];
if (stackLines.length > 3) {
const callerLine = stackLines[3];
const match = callerLine.match(/at (\S+)/);
if (match) {
metadata.caller = match[1];
}
}
return metadata;
}
ok(log, metadata) {
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.INFO,
consoleLog: _Logs.console.ok,
logMessage: log,
metadata,
type: "ok"
});
}
info(log, metadata) {
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.INFO,
consoleLog: _Logs.console.info,
logMessage: log,
metadata,
type: "info"
});
}
warn(log, metadata) {
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.WARN,
consoleLog: _Logs.console.warn,
logMessage: log,
metadata,
type: "warn"
});
}
error(log, metadata) {
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.ERROR,
consoleLog: _Logs.console.error,
logMessage: log,
metadata,
type: "error"
});
}
debug(log, metadata) {
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.DEBUG,
consoleLog: _Logs.console.debug,
logMessage: log,
metadata,
type: "debug"
});
}
fatal(log, metadata) {
if (!metadata) {
metadata = _Logs.convertErrorsIntoObjects(new Error(log));
const stack = metadata.stack;
stack.splice(1, 1);
metadata.stack = stack;
}
if (metadata instanceof Error) {
metadata = _Logs.convertErrorsIntoObjects(metadata);
const stack = metadata.stack;
stack.splice(1, 1);
metadata.stack = stack;
}
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.FATAL,
consoleLog: _Logs.console.fatal,
logMessage: log,
metadata,
type: "fatal"
});
}
verbose(log, metadata) {
metadata = this._addDiagnosticInformation(metadata);
return this._log({
level: LOG_LEVEL.VERBOSE,
consoleLog: _Logs.console.verbose,
logMessage: log,
metadata,
type: "verbose"
});
}
constructor(logLevel) {
this._maxLevel = this._getNumericLevel(logLevel);
_Logs.console = new PrettyLogs();
}
_diffColorCommentMessage(type, message) {
const diffPrefix = {
fatal: "> [!CAUTION]",
error: "> [!CAUTION]",
warn: "> [!WARNING]",
ok: "> [!TIP]",
info: "> [!NOTE]",
debug: "> [!IMPORTANT]",
verbose: "> [!NOTE]"
};
const selected = diffPrefix[type];
if (selected) {
message = message.trim().split("\n").map((line) => `> ${line}`).join("\n");
}
return [selected, message].join("\n");
}
_getNumericLevel(level) {
switch (level) {
case LOG_LEVEL.FATAL:
return 0;
case LOG_LEVEL.ERROR:
return 1;
case LOG_LEVEL.WARN:
return 2;
case LOG_LEVEL.INFO:
return 3;
case LOG_LEVEL.VERBOSE:
return 4;
case LOG_LEVEL.DEBUG:
return 5;
default:
return -1;
}
}
static convertErrorsIntoObjects(obj) {
if (obj instanceof Error) {
return {
message: obj.message,
name: obj.name,
stack: obj.stack ? obj.stack.split("\n") : null
};
} else if (typeof obj === "object" && obj !== null) {
const keys = Object.keys(obj);
keys.forEach((key) => {
obj[key] = this.convertErrorsIntoObjects(obj[key]);
});
}
return obj;
}
};
// src/utils.ts
var ansiEscapeCodes = /\x1b\[\d+m|\s/g;
function cleanLogs(spy) {
const strs = spy.mock.calls.map((call) => call.map((str) => str?.toString()).join(" "));
return strs.flat().map((str) => cleanLogString(str));
}
function cleanLogString(logString) {
return logString.replaceAll(ansiEscapeCodes, "").replaceAll(/\n/g, "").replaceAll(/\r/g, "").replaceAll(/\t/g, "").trim();
}
function cleanSpyLogs(spy) {
return cleanLogs(spy);
}
export {
COLORS,
LOG_LEVEL,
LogReturn,
Logs,
PrettyLogs,
cleanLogString,
cleanSpyLogs
};