dash-core
Version:
A foundational toolkit of types, collections, services, and architectural patterns designed to accelerate application development.
197 lines (196 loc) • 7.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceLogger = exports.LogLevel = void 0;
const path_1 = __importDefault(require("path"));
const winston_1 = require("winston");
const RESET = '\x1b[0m'; // Reset to default color
const RED = '\x1b[31m';
const YELLOW = '\x1b[33m';
const CYAN = "\x1b[36m";
const GREEN = "\x1b[32m";
var LogLevel;
(function (LogLevel) {
LogLevel["DEBUG"] = "debug";
LogLevel["INFO"] = "info";
LogLevel["WARNING"] = "warn";
LogLevel["ERROR"] = "error";
})(LogLevel || (exports.LogLevel = LogLevel = {}));
const LogLevelMap = {
[LogLevel.ERROR]: 1,
[LogLevel.WARNING]: 2,
[LogLevel.INFO]: 3,
[LogLevel.DEBUG]: 4
};
const defaultFormat = winston_1.format.combine(winston_1.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston_1.format.errors({ stack: true }), winston_1.format.splat());
const fileFormat = winston_1.format.combine(defaultFormat, winston_1.format.printf((info) => {
const { timestamp, level, message, service } = info;
const splat = (info[Symbol.for('splat')] || []);
let baseLog = `${timestamp}\t${level}\t${service}\t${message}`;
if (splat && splat.length > 0)
baseLog += '\n' + deserializeSplat(splat);
return baseLog;
}));
const consoleFormat = winston_1.format.combine(defaultFormat, winston_1.format.printf((info) => {
const { timestamp, level, message, service } = info;
const splat = (info[Symbol.for('splat')] || []);
let baseLog = level === LogLevel.INFO || level === LogLevel.DEBUG
? `${CYAN}${timestamp}${RESET}\t${level}\t${GREEN}${service}${RESET}\t${message}`
: `${timestamp}\t${level}\t${service}\t${message}`;
if (splat && splat.length > 0)
baseLog += '\n' + deserializeSplat(splat);
if (level === LogLevel.ERROR)
return `${RED}${baseLog}${RESET}`;
if (level === LogLevel.WARNING)
return `${YELLOW}${baseLog}${RESET}`;
return baseLog;
}));
function deserializeSplat(splat) {
return splat
.map((data) => {
if (data instanceof Error) {
return data.stack;
}
if (typeof data === 'object') {
try {
return JSON.stringify(data, null, 2);
}
catch {
return String(data);
}
}
return String(data);
})
.join(' ');
}
function parseFilter(input) {
const map = new Map();
if (!input) {
return map;
}
input.split(",").forEach((raw) => {
const item = raw.trim();
if (item === "")
return;
const isIncluded = !item.startsWith("!");
const name = item.replace(/^!/, "").trim();
map.set(name, isIncluded);
});
return map;
}
function createWinstonLogger(options) {
const globalLogLevel = options.globalLogLevel;
const targetLogLevel = options.logLevel;
const logLevel = LogLevelMap[globalLogLevel] < LogLevelMap[targetLogLevel] ? globalLogLevel : targetLogLevel;
const projectDirectory = options.logPath || process.cwd();
const logDirectory = path_1.default.join(projectDirectory, 'logs');
const result = (0, winston_1.createLogger)({
level: logLevel,
format: fileFormat,
defaultMeta: { service: 'user-service' },
transports: [
new winston_1.transports.File({ filename: path_1.default.join(logDirectory, 'error.log'), level: LogLevel.ERROR }),
new winston_1.transports.File({ filename: path_1.default.join(logDirectory, 'combined.log') }),
],
});
if (options.verbose) {
result.add(new winston_1.transports.Console({
format: consoleFormat
}));
}
return result;
}
/** Encapsulates service logging with configurable options. */
class ServiceLogger {
logger;
options;
get isObservable() {
const item = this.options.globalFilter.get(this.options.serviceName);
if (item === undefined)
return true;
return item;
}
/**
* Creates a new instance of the ServiceLogger class with specified options.
* If no options are provided, defaults are used.
* @param {ServiceOptions} [options={}] - Configuration options for the logger.
* @param {string} options.serviceName - The name of the service (default is "nameless-service").
* @param {LogLevel} options.logLevel - The default log level (default is LogLevel.DEBUG).
* @param {boolean} options.verbose - Indicates whether verbose logging is enabled (default is true).
* @param {string} options.logPath - The directory where logs will be stored (default is the current working directory).
* @param {LogLevel} options.globalLogLevel - The global log level for the service (default is LogLevel.DEBUG).
* @param {string} options.globalFilter - A filter string for global logging configuration.
*/
constructor(options = {}) {
this.options = {
serviceName: options.serviceName || "nameless-service",
logLevel: options.logLevel || LogLevel.DEBUG,
verbose: options.verbose || true,
logPath: options.logPath || process.cwd(),
globalLogLevel: typeof options.globalLogLevel === 'string' ? options.globalLogLevel : options.globalLogLevel || LogLevel.DEBUG,
globalFilter: parseFilter(options.globalFilter)
};
this.logger = createWinstonLogger(this.options);
this.logger.defaultMeta = { service: this.options.serviceName };
}
/**
* Logs an informational message if the service is observable.
* @param {string} message - The message to log.
* @param {...unknown[]} meta - Additional metadata to log alongside the message.
*/
info(message, ...meta) {
if (!this.isObservable)
return;
this.logger.info(message, ...meta);
}
;
/**
* Logs a warning message.
* @param {string} message - The message to log.
* @param {...unknown[]} meta - Additional metadata to log alongside the message.
*/
warning(message, ...meta) {
this.logger.warn(message, ...meta);
}
/**
* Logs a debug message if the service is observable.
* @param {string} message - The message to log.
* @param {...unknown[]} meta - Additional metadata to log alongside the message.
*/
debug(message, ...meta) {
if (!this.isObservable)
return;
this.logger.debug(message, ...meta);
}
/**
* Logs an error message and returns an Error object.
* @param {string} message - The error message.
* @param {...unknown[]} meta - Additional metadata to log alongside the message.
* @returns {Error} The created error.
*/
error(message, ...meta) {
this.logger.error(message, ...meta);
return new Error(message);
}
/**
* Adds transport to the logger to write logs to a specified file.
* @param {string} relativePath - The relative path where the log file will be stored.
* @param {LogLevel} [logLevel] - The log level for this transport. If not provided, all logs will be written.
*/
addTransport(relativePath, logLevel) {
if (!this.options.verbose)
return;
let transport;
const fullLogPath = path_1.default.join(this.options.logPath, relativePath);
if (!logLevel) {
transport = new winston_1.transports.File({ filename: path_1.default.join(fullLogPath) });
}
else {
transport = new winston_1.transports.File({ filename: path_1.default.join(fullLogPath), level: logLevel });
}
this.logger.add(transport);
}
}
exports.ServiceLogger = ServiceLogger;