weelog
Version:
Next-generation JavaScript logging library with performance tracking, memory monitoring, analytics, and advanced debugging features.
362 lines (358 loc) • 11 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**
* WeeLog - Tiny Logging Library for JavaScript
* Zero dependencies, browser and Node.js compatible
*/
class Logger {
constructor(options = {}) {
this.levels = {
debug: 0,
info: 1,
warn: 2,
error: 3
};
this.colors = {
debug: '#6b7280',
info: '#2563eb',
warn: '#f59e0b',
error: '#ef4444'
};
this.level = options.level || 'info';
this.enabled = options.enabled !== false;
this.useTimestamp = options.useTimestamp || false;
this.enablePerformanceTracking = options.enablePerformanceTracking || false;
this.enableMemoryTracking = options.enableMemoryTracking || false;
this.maxLogHistory = options.maxLogHistory || 1000;
this.enableLogAnalytics = options.enableLogAnalytics || false;
this.interceptors = [];
this.logHistory = [];
this.sessionId = this.generateSessionId();
this.performanceMarks = new Map();
this.analytics = {
totalLogs: 0,
logsByLevel: { debug: 0, info: 0, warn: 0, error: 0 },
averageLogRate: 0,
errorRate: 0,
topContexts: []
};
}
/**
* Set the minimum log level
*/
setLevel(level) {
this.level = level;
return this;
}
/**
* Enable or disable logging
*/
enable(enabled) {
this.enabled = enabled;
return this;
}
/**
* Create a logger with a specific context
*/
withContext(context) {
const newLogger = Object.create(this);
newLogger.context = context;
return newLogger;
}
/**
* Add a log interceptor callback
*/
onLog(callback) {
this.interceptors.push(callback);
return this;
}
/**
* Generate a unique session ID
*/
generateSessionId() {
return 'session_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
}
/**
* Get memory usage information
*/
getMemoryInfo() {
if (!this.enableMemoryTracking)
return undefined;
// Browser environment
if (typeof window !== 'undefined' && performance.memory) {
const memory = performance.memory;
return {
used: memory.usedJSHeapSize,
total: memory.totalJSHeapSize,
percentage: Math.round((memory.usedJSHeapSize / memory.totalJSHeapSize) * 100)
};
}
// Node.js environment
if (typeof process !== 'undefined' && process.memoryUsage) {
const memory = process.memoryUsage();
return {
used: memory.heapUsed,
total: memory.heapTotal,
percentage: Math.round((memory.heapUsed / memory.heapTotal) * 100)
};
}
return undefined;
}
/**
* Start performance tracking for a specific operation
*/
startPerformanceTimer(label) {
if (this.enablePerformanceTracking) {
this.performanceMarks.set(label, Date.now());
}
return this;
}
/**
* End performance tracking and log the duration
*/
endPerformanceTimer(label, message) {
if (this.enablePerformanceTracking && this.performanceMarks.has(label)) {
const startTime = this.performanceMarks.get(label);
const duration = Date.now() - startTime;
this.performanceMarks.delete(label);
const perfMessage = message || `Performance: ${label} completed`;
this.info(perfMessage, {
performanceTimer: label,
duration: `${duration}ms`,
timestamp: Date.now()
});
}
return this;
}
/**
* Log with automatic stack trace capture
*/
trace(message, data) {
const stackTrace = new Error().stack;
return this.log('debug', message, data, stackTrace);
}
/**
* Get current analytics data
*/
getAnalytics() {
return { ...this.analytics };
}
/**
* Get log history
*/
getLogHistory() {
return [...this.logHistory];
}
/**
* Clear log history
*/
clearHistory() {
this.logHistory = [];
return this;
}
/**
* Export logs as JSON
*/
exportLogs() {
return JSON.stringify({
sessionId: this.sessionId,
exportedAt: new Date().toISOString(),
analytics: this.analytics,
logs: this.logHistory
}, null, 2);
}
/**
* Search logs by criteria
*/
searchLogs(criteria) {
return this.logHistory.filter(entry => {
if (criteria.level && entry.level !== criteria.level)
return false;
if (criteria.context && entry.context !== criteria.context)
return false;
if (criteria.message && !entry.message.includes(criteria.message))
return false;
if (criteria.timeRange) {
if (entry.timestamp < criteria.timeRange.start || entry.timestamp > criteria.timeRange.end) {
return false;
}
}
return true;
});
}
/**
* Check if a log level should be output
*/
shouldLog(level) {
return this.enabled && this.levels[level] >= this.levels[this.level];
}
/**
* Format a log message
*/
formatMessage(level, message, data) {
let formatted = '';
if (this.useTimestamp) {
formatted += `[${new Date().toISOString()}] `;
}
formatted += `[${level.toUpperCase()}]`;
if (this.context) {
formatted += ` [${this.context}]`;
}
formatted += ` ${message}`;
if (data !== undefined && data !== null) {
if (typeof data === 'object') {
try {
formatted += ` ${JSON.stringify(data)}`;
}
catch (e) {
formatted += ` [Object (circular)]`;
}
}
else {
formatted += ` ${data}`;
}
}
return formatted;
}
/**
* Internal log method
*/
log(level, message, data, stackTrace) {
var _a;
if (!this.shouldLog(level)) {
return null;
}
const timestamp = new Date();
const formatted = this.formatMessage(level, message, data);
const logEntry = {
level,
message,
context: this.context,
data,
timestamp,
formatted,
sessionId: this.sessionId,
stackTrace: stackTrace
};
// Add performance and memory tracking
if (this.enablePerformanceTracking) {
logEntry.performance = {
timestamp: Date.now(),
memoryUsage: this.enableMemoryTracking ? (_a = this.getMemoryInfo()) === null || _a === void 0 ? void 0 : _a.used : undefined
};
}
if (this.enableMemoryTracking) {
logEntry.memory = this.getMemoryInfo();
}
// Update analytics
if (this.enableLogAnalytics) {
this.updateAnalytics(level, this.context);
}
// Add to history
this.logHistory.push(logEntry);
if (this.logHistory.length > this.maxLogHistory) {
this.logHistory.shift();
}
// Call interceptors
this.interceptors.forEach(interceptor => {
try {
interceptor(level, message, this.context, data);
}
catch (e) {
// Avoid infinite loops by using plain console.error
if (typeof console !== 'undefined' && console.error) {
console.error('Logger interceptor error:', e);
}
}
});
// Output to console
this.outputToConsole(level, formatted);
return logEntry;
}
/**
* Update analytics data
*/
updateAnalytics(level, context) {
this.analytics.totalLogs++;
this.analytics.logsByLevel[level]++;
if (level === 'error') {
this.analytics.errorRate = (this.analytics.logsByLevel.error / this.analytics.totalLogs) * 100;
}
if (context) {
const existingContext = this.analytics.topContexts.find(c => c.context === context);
if (existingContext) {
existingContext.count++;
}
else {
this.analytics.topContexts.push({ context, count: 1 });
}
// Keep only top 10 contexts
this.analytics.topContexts.sort((a, b) => b.count - a.count);
this.analytics.topContexts = this.analytics.topContexts.slice(0, 10);
}
}
/**
* Output formatted message to console with colors (browser only)
*/
outputToConsole(level, formatted) {
if (typeof console === 'undefined') {
return;
}
const color = this.colors[level];
// Browser environment - use colored output
if (typeof window !== 'undefined' && console.log) {
const weight = level === 'error' ? 'bold' : 'normal';
console.log(`%c${formatted}`, `color: ${color}; font-weight: ${weight}`);
}
// Node.js environment - use appropriate console method
else if (typeof process !== 'undefined') {
switch (level) {
case 'debug':
console.debug ? console.debug(formatted) : console.log(formatted);
break;
case 'info':
console.info(formatted);
break;
case 'warn':
console.warn(formatted);
break;
case 'error':
console.error(formatted);
break;
default:
console.log(formatted);
}
}
// Fallback
else if (console.log) {
console.log(formatted);
}
}
/**
* Log a debug message
*/
debug(message, data) {
return this.log('debug', message, data);
}
/**
* Log an info message
*/
info(message, data) {
return this.log('info', message, data);
}
/**
* Log a warning message
*/
warn(message, data) {
return this.log('warn', message, data);
}
/**
* Log an error message
*/
error(message, data) {
return this.log('error', message, data);
}
}
exports.Logger = Logger;
exports.default = Logger;
//# sourceMappingURL=weelog.cjs.js.map