init
This commit is contained in:
199
src/lib/logger/index.ts
Normal file
199
src/lib/logger/index.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
type LogLevel = 'info' | 'success' | 'warn' | 'error';
|
||||
|
||||
interface LoggerOptions {
|
||||
enabled?: boolean;
|
||||
showTimestamp?: boolean;
|
||||
collapsed?: boolean;
|
||||
colors?: boolean;
|
||||
}
|
||||
|
||||
// Cache for performance - update once per second max
|
||||
let cachedTimestamp = '';
|
||||
let lastTimestampUpdate = 0;
|
||||
|
||||
/**
|
||||
* Get formatted timestamp with caching for performance
|
||||
* Format: MM/DD HH:mm:ss
|
||||
*/
|
||||
function getTimestamp(): string {
|
||||
const now = Date.now();
|
||||
|
||||
// Update cache only if more than 1 second has passed
|
||||
if (now - lastTimestampUpdate > 1000) {
|
||||
const date = new Date(now);
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
cachedTimestamp = `${month}/${day} ${hours}:${minutes}:${seconds}`;
|
||||
lastTimestampUpdate = now;
|
||||
}
|
||||
|
||||
return cachedTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color and emoji for each log level
|
||||
*/
|
||||
function getLevelStyle(level: LogLevel): { color: string; label: string } {
|
||||
const styles = {
|
||||
info: { color: '#f5f5f5', label: 'INFO' },
|
||||
success: { color: '#10B981', label: 'SUCCESS' },
|
||||
warn: { color: '#F59E0B', label: 'WARN' },
|
||||
error: { color: '#EF4444', label: 'ERROR' },
|
||||
};
|
||||
|
||||
return styles[level] || styles.info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main logger class
|
||||
*/
|
||||
class Logger {
|
||||
private options: LoggerOptions;
|
||||
private context?: string;
|
||||
|
||||
constructor(context?: string, options: LoggerOptions = {}) {
|
||||
this.context = context;
|
||||
this.options = {
|
||||
enabled: process.env.NODE_ENV !== 'production',
|
||||
showTimestamp: true,
|
||||
collapsed: true,
|
||||
colors: true,
|
||||
...options
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a child logger with a specific context
|
||||
*/
|
||||
child(context: string, options?: LoggerOptions): Logger {
|
||||
const childContext = this.context ? `${this.context} > ${context}` : context;
|
||||
return new Logger(childContext, { ...this.options, ...options });
|
||||
}
|
||||
|
||||
/**
|
||||
* Core logging method
|
||||
*/
|
||||
private log(
|
||||
level: LogLevel,
|
||||
label: string,
|
||||
data?: any,
|
||||
...rest: any[]
|
||||
): void {
|
||||
if (!this.options.enabled) return;
|
||||
|
||||
const style = getLevelStyle(level);
|
||||
const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : '';
|
||||
const context = this.context ? ` │ ${this.context}` : '';
|
||||
|
||||
const groupLabel = `${timestamp}${style.label}${context} │ ${label}`;
|
||||
|
||||
const group = this.options.collapsed ? console.groupCollapsed : console.group;
|
||||
|
||||
if (this.options.colors && typeof window !== 'undefined') {
|
||||
group(
|
||||
`%c${groupLabel}`,
|
||||
`color: ${style.color}; font-weight: bold;`
|
||||
);
|
||||
} else {
|
||||
group(groupLabel);
|
||||
}
|
||||
|
||||
if (data !== undefined) {
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
if (rest.length > 0) {
|
||||
for (const item of rest) {
|
||||
console.log(item);
|
||||
}
|
||||
}
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* log level methods
|
||||
*/
|
||||
info(label: string, data?: any, ...rest: any[]): void {
|
||||
this.log('info', label, data, ...rest);
|
||||
}
|
||||
|
||||
success(label: string, data?: any, ...rest: any[]): void {
|
||||
this.log('success', label, data, ...rest);
|
||||
}
|
||||
|
||||
warn(label: string, data?: any, ...rest: any[]): void {
|
||||
this.log('warn', label, data, ...rest);
|
||||
}
|
||||
|
||||
error(label: string, data?: any, ...rest: any[]): void {
|
||||
this.log('error', label, data, ...rest);
|
||||
}
|
||||
|
||||
simple(message: string): void {
|
||||
if (!this.options.enabled) return;
|
||||
|
||||
const style = getLevelStyle('info');
|
||||
const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : '';
|
||||
const context = this.context ? ` │ ${this.context}` : '';
|
||||
|
||||
const logMessage = `${timestamp}${style.label}${context} │ ${message}`;
|
||||
|
||||
if (this.options.colors && typeof window !== 'undefined') {
|
||||
console.log(`%c${logMessage}`, `color: ${style.color};`);
|
||||
} else {
|
||||
console.log(logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure performance of an operation
|
||||
*/
|
||||
async measure<T>(label: string, fn: () => Promise<T>): Promise<T> {
|
||||
const start = performance.now();
|
||||
|
||||
try {
|
||||
const result = await fn();
|
||||
const duration = (performance.now() - start).toFixed(2);
|
||||
|
||||
this.success(`${label} completed`, {
|
||||
duration: `${duration}ms`,
|
||||
result
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const duration = (performance.now() - start).toFixed(2);
|
||||
|
||||
this.error(`${label} failed`, {
|
||||
duration: `${duration}ms`,
|
||||
error
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a table log
|
||||
*/
|
||||
table(label: string, data: any[]): void {
|
||||
if (!this.options.enabled) return;
|
||||
|
||||
const timestamp = this.options.showTimestamp ? `${getTimestamp()} │ ` : '';
|
||||
const context = this.context ? ` │ ${this.context}` : '';
|
||||
|
||||
console.group(`${timestamp}TABLE${context} │ ${label}`);
|
||||
console.table(data);
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const logger = new Logger();
|
||||
|
||||
export { Logger };
|
||||
Reference in New Issue
Block a user