- Implemented a comprehensive health check utility to monitor system dependencies including NocoDB, SMTP, Represent API, disk space, and memory usage. - Created a logger utility using Winston for structured logging with daily rotation and various log levels. - Developed a metrics utility using Prometheus client to track application performance metrics such as email sends, HTTP requests, and user activity. - Added a backup script for automated backups of NocoDB data, uploaded files, and environment configurations with optional S3 support. - Introduced a toggle script to switch between development (MailHog) and production (ProtonMail) SMTP configurations.
191 lines
4.2 KiB
JavaScript
191 lines
4.2 KiB
JavaScript
const winston = require('winston');
|
|
const DailyRotateFile = require('winston-daily-rotate-file');
|
|
const path = require('path');
|
|
|
|
// Define log levels
|
|
const levels = {
|
|
error: 0,
|
|
warn: 1,
|
|
info: 2,
|
|
http: 3,
|
|
debug: 4,
|
|
};
|
|
|
|
// Define colors for each level
|
|
const colors = {
|
|
error: 'red',
|
|
warn: 'yellow',
|
|
info: 'green',
|
|
http: 'magenta',
|
|
debug: 'white',
|
|
};
|
|
|
|
// Tell winston about our colors
|
|
winston.addColors(colors);
|
|
|
|
// Define which level to log based on environment
|
|
const level = () => {
|
|
const env = process.env.NODE_ENV || 'development';
|
|
const isDevelopment = env === 'development';
|
|
return isDevelopment ? 'debug' : 'info';
|
|
};
|
|
|
|
// Define log format
|
|
const logFormat = winston.format.combine(
|
|
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
|
|
winston.format.errors({ stack: true }),
|
|
winston.format.splat(),
|
|
winston.format.json()
|
|
);
|
|
|
|
// Console format for development (pretty print)
|
|
const consoleFormat = winston.format.combine(
|
|
winston.format.colorize({ all: true }),
|
|
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
winston.format.printf(
|
|
(info) => `${info.timestamp} ${info.level}: ${info.message}${info.stack ? '\n' + info.stack : ''}`
|
|
)
|
|
);
|
|
|
|
// Define transports
|
|
const transports = [
|
|
// Console output
|
|
new winston.transports.Console({
|
|
format: consoleFormat,
|
|
handleExceptions: true,
|
|
}),
|
|
|
|
// Error logs - rotate daily
|
|
new DailyRotateFile({
|
|
filename: path.join(__dirname, '../../logs/error-%DATE%.log'),
|
|
datePattern: 'YYYY-MM-DD',
|
|
level: 'error',
|
|
format: logFormat,
|
|
maxSize: '20m',
|
|
maxFiles: '14d',
|
|
handleExceptions: true,
|
|
}),
|
|
|
|
// Combined logs - rotate daily
|
|
new DailyRotateFile({
|
|
filename: path.join(__dirname, '../../logs/combined-%DATE%.log'),
|
|
datePattern: 'YYYY-MM-DD',
|
|
format: logFormat,
|
|
maxSize: '20m',
|
|
maxFiles: '14d',
|
|
}),
|
|
|
|
// HTTP logs - rotate daily
|
|
new DailyRotateFile({
|
|
filename: path.join(__dirname, '../../logs/http-%DATE%.log'),
|
|
datePattern: 'YYYY-MM-DD',
|
|
level: 'http',
|
|
format: logFormat,
|
|
maxSize: '20m',
|
|
maxFiles: '7d',
|
|
}),
|
|
];
|
|
|
|
// Create the logger
|
|
const logger = winston.createLogger({
|
|
level: level(),
|
|
levels,
|
|
format: logFormat,
|
|
transports,
|
|
exitOnError: false,
|
|
});
|
|
|
|
// Create a stream object for Morgan HTTP logger
|
|
logger.stream = {
|
|
write: (message) => {
|
|
logger.http(message.trim());
|
|
},
|
|
};
|
|
|
|
// Helper methods for common logging patterns
|
|
logger.logRequest = (req, res, duration) => {
|
|
const logData = {
|
|
method: req.method,
|
|
url: req.url,
|
|
status: res.statusCode,
|
|
duration: `${duration}ms`,
|
|
ip: req.ip,
|
|
userAgent: req.get('user-agent'),
|
|
};
|
|
|
|
if (res.statusCode >= 500) {
|
|
logger.error('Request failed', logData);
|
|
} else if (res.statusCode >= 400) {
|
|
logger.warn('Request error', logData);
|
|
} else {
|
|
logger.http('Request completed', logData);
|
|
}
|
|
};
|
|
|
|
logger.logEmailSent = (recipient, campaign, status) => {
|
|
logger.info('Email sent', {
|
|
event: 'email_sent',
|
|
recipient,
|
|
campaign,
|
|
status,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
logger.logEmailFailed = (recipient, campaign, error) => {
|
|
logger.error('Email failed', {
|
|
event: 'email_failed',
|
|
recipient,
|
|
campaign,
|
|
error: error.message,
|
|
stack: error.stack,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
logger.logAuth = (action, username, success, ip) => {
|
|
const level = success ? 'info' : 'warn';
|
|
logger[level]('Auth action', {
|
|
event: 'auth',
|
|
action,
|
|
username,
|
|
success,
|
|
ip,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
logger.logCampaignAction = (action, campaignId, userId, ip) => {
|
|
logger.info('Campaign action', {
|
|
event: 'campaign_action',
|
|
action,
|
|
campaignId,
|
|
userId,
|
|
ip,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
logger.logRateLimitHit = (endpoint, ip, limit) => {
|
|
logger.warn('Rate limit hit', {
|
|
event: 'rate_limit',
|
|
endpoint,
|
|
ip,
|
|
limit,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
logger.logHealthCheck = (service, status, details = {}) => {
|
|
const level = status === 'healthy' ? 'info' : 'error';
|
|
logger[level]('Health check', {
|
|
event: 'health_check',
|
|
service,
|
|
status,
|
|
...details,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
module.exports = logger;
|