outreach/lib/logger.js
2025-08-15 01:03:38 -08:00

160 lines
4.0 KiB
JavaScript

const winston = require("winston");
const path = require("path");
const config = require("../config");
// Create logs directory if it doesn't exist
const fs = require("fs");
const logsDir = path.join(__dirname, "..", "logs");
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir);
}
// Custom format for console output
const consoleFormat = winston.format.combine(
winston.format.timestamp({ format: "HH:mm:ss" }),
winston.format.colorize(),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
let metaStr = "";
if (Object.keys(meta).length > 0) {
metaStr = " " + JSON.stringify(meta);
}
return `${timestamp} [${level}] ${message}${metaStr}`;
})
);
// Custom format for file output
const fileFormat = winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
);
// Create the logger
const logger = winston.createLogger({
level: config.logging.level,
format: fileFormat,
defaultMeta: {
service: "outreach-engine",
environment: config.env,
},
transports: [
// Error log file
new winston.transports.File({
filename: path.join(logsDir, "error.log"),
level: "error",
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
// Combined log file
new winston.transports.File({
filename: path.join(logsDir, "combined.log"),
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
// Email activity log
new winston.transports.File({
filename: path.join(logsDir, "email-activity.log"),
level: "info",
maxsize: 10485760, // 10MB
maxFiles: 10,
}),
],
});
// Add console transport for development
if (config.isDevelopment) {
logger.add(
new winston.transports.Console({
format: consoleFormat,
})
);
}
// Custom logging methods for email activities
logger.emailSent = (recipient, subject, firmName, testMode = false) => {
logger.info("Email sent successfully", {
event: "email_sent",
recipient,
subject,
firmName,
testMode,
timestamp: new Date().toISOString(),
});
};
logger.emailFailed = (recipient, error, errorType, firmName) => {
logger.error("Email failed to send", {
event: "email_failed",
recipient,
error: error.message,
errorType,
firmName,
stack: error.stack,
timestamp: new Date().toISOString(),
});
};
logger.emailRetry = (recipient, attempt, maxRetries, retryDelay, errorType) => {
logger.warn("Email retry scheduled", {
event: "email_retry",
recipient,
attempt,
maxRetries,
retryDelay,
errorType,
timestamp: new Date().toISOString(),
});
};
logger.emailPermanentFailure = (recipient, errorType, attempts) => {
logger.error("Email permanent failure", {
event: "email_permanent_failure",
recipient,
errorType,
attempts,
timestamp: new Date().toISOString(),
});
};
logger.campaignStart = (totalEmails, testMode) => {
logger.info("Email campaign started", {
event: "campaign_start",
totalEmails,
testMode,
timestamp: new Date().toISOString(),
});
};
logger.campaignComplete = (stats) => {
logger.info("Email campaign completed", {
event: "campaign_complete",
...stats,
timestamp: new Date().toISOString(),
});
};
logger.rateLimitPause = (duration, reason) => {
logger.warn("Rate limit pause activated", {
event: "rate_limit_pause",
duration,
reason,
timestamp: new Date().toISOString(),
});
};
// Helper method to log with email context
logger.withEmail = (recipient, firmName) => {
return {
info: (message, meta = {}) =>
logger.info(message, { ...meta, recipient, firmName }),
warn: (message, meta = {}) =>
logger.warn(message, { ...meta, recipient, firmName }),
error: (message, meta = {}) =>
logger.error(message, { ...meta, recipient, firmName }),
};
};
module.exports = logger;