chia-agent
Version:
chia rpc/websocket client library
346 lines (345 loc) • 11.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Logger = exports.jsonLogFormatter = exports.simpleLogFormatter = exports.defaultLogFormatter = exports.DEFAULT_LOGGER_NAME = void 0;
exports.isValidLogLevel = isValidLogLevel;
exports.normalizeLogLevel = normalizeLogLevel;
exports.setDefaultLogLevel = setDefaultLogLevel;
exports.getDefaultLogLevel = getDefaultLogLevel;
exports.setDefaultWriter = setDefaultWriter;
exports.setDefaultFormatter = setDefaultFormatter;
exports.getDefaultFormatter = getDefaultFormatter;
exports.getLogger = getLogger;
exports.clearLoggerCache = clearLoggerCache;
exports.createConsoleWriter = createConsoleWriter;
exports.createNullWriter = createNullWriter;
exports.stringify = stringify;
exports.getLogLevel = getLogLevel;
exports.setLogLevel = setLogLevel;
const LOG_LEVELS = [
"error",
"warning",
"info",
"debug",
"trace",
"none",
];
function isValidLogLevel(level) {
return LOG_LEVELS.includes(level.toLowerCase());
}
function normalizeLogLevel(level) {
const normalized = level.toLowerCase();
if (isValidLogLevel(normalized)) {
return normalized;
}
return null;
}
const logPriority = {
none: 9999,
error: 5,
warning: 4,
info: 3,
debug: 2,
trace: 1,
};
class ConsoleWriter {
write(message, level) {
switch (level) {
case "error":
console.error(message);
break;
case "warning":
console.warn(message);
break;
case "info":
console.info(message);
break;
case "debug":
console.debug(message);
break;
case "trace":
console.trace(message);
break;
default:
console.log(message);
}
}
}
class NullWriter {
write(_message, _level) {
// Suppress all output
}
}
// Global defaults
let defaultLogLevel = "error";
if (process.env.LOG_LEVEL) {
const normalizedLevel = normalizeLogLevel(process.env.LOG_LEVEL);
if (normalizedLevel) {
defaultLogLevel = normalizedLevel;
}
else {
console.warn(`Invalid LOG_LEVEL environment variable: ${process.env.LOG_LEVEL}. Using default: error`);
}
}
let defaultWriter = process.env.LOG_SUPPRESS === "true" ? new NullWriter() : new ConsoleWriter();
// Logger instance cache
const loggerCache = new Map();
// Configuration functions for global defaults
function setDefaultLogLevel(level) {
// Normalize for JavaScript users who might pass uppercase
const normalized = normalizeLogLevel(level);
if (!normalized) {
throw new Error(`Invalid log level: ${level}. Valid levels are: ${LOG_LEVELS.join(", ")}`);
}
defaultLogLevel = normalized;
}
function getDefaultLogLevel() {
return defaultLogLevel;
}
function setDefaultWriter(writer) {
defaultWriter = writer;
}
function setDefaultFormatter(formatter) {
defaultFormatter = formatter;
}
function getDefaultFormatter() {
return defaultFormatter;
}
exports.DEFAULT_LOGGER_NAME = "default";
// Default formatter
const defaultLogFormatter = (context) => {
const timestamp = context.timestamp.toISOString();
const levelStr = context.level.toUpperCase();
const nameStr = context.loggerName === exports.DEFAULT_LOGGER_NAME
? ""
: ` [${context.loggerName}]`;
return `${timestamp} [${levelStr}]${nameStr} - ${context.message}`;
};
exports.defaultLogFormatter = defaultLogFormatter;
// Simple formatter without timestamp
const simpleLogFormatter = (context) => {
const levelStr = context.level.toUpperCase();
const nameStr = context.loggerName === exports.DEFAULT_LOGGER_NAME
? ""
: `[${context.loggerName}] `;
return `[${levelStr}] ${nameStr}${context.message}`;
};
exports.simpleLogFormatter = simpleLogFormatter;
// JSON formatter
const jsonLogFormatter = (context) => {
return JSON.stringify({
timestamp: context.timestamp.toISOString(),
level: context.level,
logger: context.loggerName,
message: context.message,
});
};
exports.jsonLogFormatter = jsonLogFormatter;
let defaultFormatter = exports.defaultLogFormatter;
// Factory function with caching
function getLogger(name = exports.DEFAULT_LOGGER_NAME) {
let logger = loggerCache.get(name);
if (!logger) {
logger = new Logger(name, defaultLogLevel, defaultWriter, defaultFormatter);
loggerCache.set(name, logger);
}
return logger;
}
// Clear logger cache (useful for testing)
function clearLoggerCache() {
loggerCache.clear();
}
// Helper to create writers
function createConsoleWriter() {
return new ConsoleWriter();
}
function createNullWriter() {
return new NullWriter();
}
function stringify(obj, indent) {
if (typeof obj === "string") {
return obj;
}
else if (typeof obj === "number") {
return `${obj}`;
}
else if (typeof obj === "boolean") {
return `${obj}`;
}
else if (typeof obj === "bigint") {
return `${obj}n`;
}
else if (typeof obj === "symbol") {
return obj.toString();
}
else if (typeof obj === "undefined") {
return "undefined";
}
else if (typeof obj === "function") {
return "[Function]";
}
else if (obj === null) {
return "null";
}
try {
// Deep clone the object while removing circular references
const seen = new WeakSet();
const deepCloneWithoutCircular = (value, path = []) => {
// Handle primitives and null
if (value === null || typeof value !== "object") {
if (typeof value === "bigint") {
return `${value}n`;
}
else if (typeof value === "function") {
return "[Function]";
}
else if (typeof value === "symbol") {
return value.toString();
}
return value;
}
// Check for circular reference
if (seen.has(value)) {
return undefined; // This will cause the property to be omitted
}
seen.add(value);
try {
// Handle arrays
if (Array.isArray(value)) {
const clonedArray = [];
for (let i = 0; i < value.length; i++) {
const clonedValue = deepCloneWithoutCircular(value[i], [
...path,
String(i),
]);
if (clonedValue !== undefined) {
clonedArray.push(clonedValue);
}
}
return clonedArray;
}
// Handle objects
const cloned = {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
const clonedValue = deepCloneWithoutCircular(value[key], [
...path,
key,
]);
if (clonedValue !== undefined) {
cloned[key] = clonedValue;
}
}
}
return cloned;
}
finally {
// Remove from seen set when we're done processing this level
seen.delete(value);
}
};
const clonedObj = deepCloneWithoutCircular(obj);
return JSON.stringify(clonedObj, null, indent);
}
catch (error) {
const msg = error && typeof error === "object" && "message" in error
? error.message
: "Unknown error";
return `[Error stringifying object: ${msg}]`;
}
}
class Logger {
constructor(name, logLevel, writer, formatter) {
// Normalize for JavaScript users who might pass uppercase
const normalized = normalizeLogLevel(logLevel);
if (!normalized) {
throw new Error(`Invalid log level: ${logLevel}. Valid levels are: ${LOG_LEVELS.join(", ")}`);
}
this._name = name;
this._logLevel = normalized;
this._writer = writer;
this._formatter = formatter || defaultFormatter;
}
// Allow changing log level for this specific logger
setLogLevel(level) {
// Normalize for JavaScript users who might pass uppercase
const normalized = normalizeLogLevel(level);
if (!normalized) {
throw new Error(`Invalid log level: ${level}. Valid levels are: ${LOG_LEVELS.join(", ")}`);
}
this._logLevel = normalized;
}
getLogLevel() {
return this._logLevel;
}
// Allow changing writer for this specific logger
setWriter(writer) {
this._writer = writer;
}
getWriter() {
return this._writer;
}
getName() {
return this._name;
}
setFormatter(formatter) {
this._formatter = formatter;
}
getFormatter() {
return this._formatter;
}
_shouldWrite(logLevel) {
return logPriority[this._logLevel] <= logPriority[logLevel];
}
_formatMessage(level, body) {
const context = {
timestamp: new Date(),
level,
loggerName: this._name,
message: body,
};
return this._formatter(context);
}
trace(msg) {
if (this._shouldWrite("trace")) {
const message = typeof msg === "function" ? msg() : msg;
this._writer.write(this._formatMessage("trace", stringify(message)), "trace");
}
}
debug(msg) {
if (this._shouldWrite("debug")) {
const message = typeof msg === "function" ? msg() : msg;
this._writer.write(this._formatMessage("debug", stringify(message)), "debug");
}
}
info(msg) {
if (this._shouldWrite("info")) {
const message = typeof msg === "function" ? msg() : msg;
this._writer.write(this._formatMessage("info", stringify(message)), "info");
}
}
warning(msg) {
if (this._shouldWrite("warning")) {
const message = typeof msg === "function" ? msg() : msg;
this._writer.write(this._formatMessage("warning", stringify(message)), "warning");
}
}
error(msg) {
if (this._shouldWrite("error")) {
const message = typeof msg === "function" ? msg() : msg;
this._writer.write(this._formatMessage("error", stringify(message)), "error");
}
}
}
exports.Logger = Logger;
// For backward compatibility
function getLogLevel() {
return getDefaultLogLevel();
}
function setLogLevel(level) {
// Normalize for JavaScript users who might pass uppercase
const normalized = normalizeLogLevel(level);
if (!normalized) {
throw new Error(`Invalid log level: ${level}. Valid levels are: ${LOG_LEVELS.join(", ")}`);
}
setDefaultLogLevel(normalized);
}