@454creative/easy-email
Version:
A framework-agnostic email service library for Node.js with observability and monitoring
144 lines (124 loc) • 3.47 kB
text/typescript
export enum LogLevel {
DEBUG = 'debug',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
}
export interface LogEntry {
timestamp: string;
level: LogLevel;
message: string;
details?: any;
}
export interface Metrics {
totalEmailsSent: number;
successfulEmails: number;
failedEmails: number;
averageSendTime: number;
lastError?: LogEntry;
}
export class Logger {
private static instance: Logger;
private logLevel: LogLevel = LogLevel.INFO;
private logs: LogEntry[] = [];
private metrics: Metrics = {
totalEmailsSent: 0,
successfulEmails: 0,
failedEmails: 0,
averageSendTime: 0,
};
private startTime?: number;
private constructor() {}
static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
setLogLevel(level: LogLevel): void {
this.logLevel = level;
}
debug(message: string, details?: any): void {
this.log(LogLevel.DEBUG, message, details);
}
info(message: string, details?: any): void {
this.log(LogLevel.INFO, message, details);
}
warn(message: string, details?: any): void {
this.log(LogLevel.WARN, message, details);
}
error(message: string, details?: any): void {
this.log(LogLevel.ERROR, message, details);
this.metrics.lastError = {
timestamp: new Date().toISOString(),
level: LogLevel.ERROR,
message,
details,
};
}
private log(level: LogLevel, message: string, details?: any): void {
if (this.shouldLog(level)) {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
details,
};
this.logs.push(entry);
console.log(`[${entry.timestamp}] ${level.toUpperCase()}: ${message}`, details || '');
}
}
private shouldLog(level: LogLevel): boolean {
const levels = Object.values(LogLevel);
return levels.indexOf(level) >= levels.indexOf(this.logLevel);
}
startOperation(): void {
this.startTime = Date.now();
}
endOperation(success: boolean): void {
if (this.startTime) {
const duration = Date.now() - this.startTime;
this.metrics.totalEmailsSent++;
if (success) {
this.metrics.successfulEmails++;
} else {
this.metrics.failedEmails++;
}
this.metrics.averageSendTime =
(this.metrics.averageSendTime * (this.metrics.totalEmailsSent - 1) + duration) /
this.metrics.totalEmailsSent;
this.startTime = undefined;
}
}
getMetrics(): Metrics {
return { ...this.metrics };
}
getLogs(level?: LogLevel): LogEntry[] {
if (level) {
return this.logs.filter(log => log.level === level);
}
return [...this.logs];
}
clearLogs(): void {
this.logs = [];
}
resetMetrics(): void {
this.metrics = {
totalEmailsSent: 0,
successfulEmails: 0,
failedEmails: 0,
averageSendTime: 0,
};
}
/**
* Create a standardized log entry for email operations
* @param operation - The email operation being performed
* @param success - Whether the operation was successful
* @param details - Additional details about the operation
*/
logEmailOperation(operation: string, success: boolean, details?: any): void {
const level = success ? LogLevel.INFO : LogLevel.ERROR;
const message = `${operation} ${success ? 'completed successfully' : 'failed'}`;
this.log(level, message, details);
}
}