UNPKG

@ideal-photography/shared

Version:

Shared MongoDB and utility logic for Ideal Photography PWAs: users, products, services, bookings, orders/cart, galleries, reviews, notifications, campaigns, settings, audit logs, minimart items/orders, and push notification subscriptions.

236 lines (203 loc) 7.2 kB
import fs from 'fs'; import path from 'path'; /** * Enhanced logging system for notification operations * Provides structured logging with different levels and file rotation */ class NotificationLogger { constructor() { this.logDir = path.join(process.cwd(), 'logs', 'notifications'); this.ensureLogDirectory(); } ensureLogDirectory() { if (!fs.existsSync(this.logDir)) { fs.mkdirSync(this.logDir, { recursive: true }); } } getLogFileName(level) { const date = new Date().toISOString().split('T')[0]; return path.join(this.logDir, `notification-${level}-${date}.log`); } formatLogEntry(level, message, meta = {}) { const timestamp = new Date().toISOString(); const logEntry = { timestamp, level, message, ...meta }; return JSON.stringify(logEntry) + '\n'; } writeLog(level, message, meta = {}) { const logFile = this.getLogFileName(level); const logEntry = this.formatLogEntry(level, message, meta); fs.appendFileSync(logFile, logEntry); // Also log to console in development if (process.env.NODE_ENV === 'development') { console.log(`[${level.toUpperCase()}] ${message}`, meta); } } info(message, meta = {}) { this.writeLog('info', message, meta); } warn(message, meta = {}) { this.writeLog('warn', message, meta); } error(message, meta = {}) { this.writeLog('error', message, meta); } debug(message, meta = {}) { if (process.env.NODE_ENV === 'development') { this.writeLog('debug', message, meta); } } // Specific notification logging methods logNotificationCreated(notification, adminId) { this.info('Notification created', { notificationId: notification._id, title: notification.title, type: notification.type, priority: notification.priority, recipients: notification.recipients, adminId, timestamp: new Date().toISOString() }); } logNotificationSent(notification, result) { this.info('Notification sent', { notificationId: notification._id, title: notification.title, type: notification.type, priority: notification.priority, recipients: notification.recipients, pushEnabled: notification.channels.push, result, timestamp: new Date().toISOString() }); } logNotificationFailed(notification, error) { this.error('Notification failed to send', { notificationId: notification._id, title: notification.title, type: notification.type, priority: notification.priority, recipients: notification.recipients, error: error.message, stack: error.stack, timestamp: new Date().toISOString() }); } logPushNotificationSent(userId, notification, result) { this.info('Push notification sent', { userId, notificationId: notification._id, title: notification.title, type: notification.type, priority: notification.priority, result, timestamp: new Date().toISOString() }); } logPushNotificationFailed(userId, notification, error) { this.error('Push notification failed', { userId, notificationId: notification._id, title: notification.title, type: notification.type, priority: notification.priority, error: error.message, stack: error.stack, timestamp: new Date().toISOString() }); } logUserAction(userId, action, notificationId, meta = {}) { this.info('User notification action', { userId, action, notificationId, ...meta, timestamp: new Date().toISOString() }); } logAdminAction(adminId, action, notificationId, meta = {}) { this.info('Admin notification action', { adminId, action, notificationId, ...meta, timestamp: new Date().toISOString() }); } logSystemEvent(event, meta = {}) { this.info('System event', { event, ...meta, timestamp: new Date().toISOString() }); } logPerformance(operation, duration, meta = {}) { this.info('Performance metric', { operation, duration, ...meta, timestamp: new Date().toISOString() }); } // Error tracking and reporting logError(error, context = {}) { this.error('Notification system error', { message: error.message, stack: error.stack, name: error.name, context, timestamp: new Date().toISOString() }); } // Clean up old log files (run this periodically) cleanupOldLogs(daysToKeep = 30) { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysToKeep); try { const files = fs.readdirSync(this.logDir); files.forEach(file => { const filePath = path.join(this.logDir, file); const stats = fs.statSync(filePath); if (stats.mtime < cutoffDate) { fs.unlinkSync(filePath); this.info('Old log file deleted', { file }); } }); } catch (error) { this.error('Failed to cleanup old logs', { error: error.message }); } } // Get log statistics getLogStats() { try { const files = fs.readdirSync(this.logDir); const stats = { totalFiles: files.length, totalSize: 0, filesByLevel: {} }; files.forEach(file => { const filePath = path.join(this.logDir, file); const fileStats = fs.statSync(filePath); stats.totalSize += fileStats.size; // Extract level from filename const levelMatch = file.match(/notification-(\w+)-/); if (levelMatch) { const level = levelMatch[1]; stats.filesByLevel[level] = (stats.filesByLevel[level] || 0) + 1; } }); return stats; } catch (error) { this.error('Failed to get log stats', { error: error.message }); return null; } } } // Create singleton instance const notificationLogger = new NotificationLogger(); export default notificationLogger;