Steps 1-11, still need to do 12

This commit is contained in:
2025-08-07 15:29:23 -05:00
parent b66a516d20
commit 4019e921d3
15 changed files with 3731 additions and 55 deletions

View File

@@ -83,32 +83,60 @@ class Logger {
/**
* Logs operation start with configuration details (Requirement 3.1)
* @param {Object} config - Configuration object
* @param {Object} schedulingContext - Optional scheduling context
* @returns {Promise<void>}
*/
async logOperationStart(config) {
await this.info(`Starting price update operation with configuration:`);
async logOperationStart(config, schedulingContext = null) {
if (schedulingContext && schedulingContext.isScheduled) {
await this.info(
`Starting scheduled price update operation with configuration:`
);
await this.info(
` Scheduled Time: ${schedulingContext.scheduledTime.toLocaleString()}`
);
await this.info(
` Original Schedule Input: ${schedulingContext.originalInput}`
);
} else {
await this.info(`Starting price update operation with configuration:`);
}
await this.info(` Target Tag: ${config.targetTag}`);
await this.info(` Price Adjustment: ${config.priceAdjustmentPercentage}%`);
await this.info(` Shop Domain: ${config.shopDomain}`);
// Also log to progress file
await this.progressService.logOperationStart(config);
// Also log to progress file with scheduling context
await this.progressService.logOperationStart(config, schedulingContext);
}
/**
* Logs rollback operation start with configuration details (Requirements 3.1, 7.1, 8.3)
* @param {Object} config - Configuration object
* @param {Object} schedulingContext - Optional scheduling context
* @returns {Promise<void>}
*/
async logRollbackStart(config) {
await this.info(`Starting price rollback operation with configuration:`);
async logRollbackStart(config, schedulingContext = null) {
if (schedulingContext && schedulingContext.isScheduled) {
await this.info(
`Starting scheduled price rollback operation with configuration:`
);
await this.info(
` Scheduled Time: ${schedulingContext.scheduledTime.toLocaleString()}`
);
await this.info(
` Original Schedule Input: ${schedulingContext.originalInput}`
);
} else {
await this.info(`Starting price rollback operation with configuration:`);
}
await this.info(` Target Tag: ${config.targetTag}`);
await this.info(` Operation Mode: rollback`);
await this.info(` Shop Domain: ${config.shopDomain}`);
// Also log to progress file with rollback-specific format
// Also log to progress file with rollback-specific format and scheduling context
try {
await this.progressService.logRollbackStart(config);
await this.progressService.logRollbackStart(config, schedulingContext);
} catch (error) {
// Progress logging should not block main operations
console.warn(`Warning: Failed to log to progress file: ${error.message}`);
@@ -267,15 +295,20 @@ class Logger {
* @param {string} entry.productId - Product ID
* @param {string} entry.variantId - Variant ID (optional)
* @param {string} entry.errorMessage - Error message
* @param {Object} schedulingContext - Optional scheduling context for error logging (Requirements 5.3, 5.4)
* @returns {Promise<void>}
*/
async logProductError(entry) {
async logProductError(entry, schedulingContext = null) {
const variantInfo = entry.variantId ? ` (Variant: ${entry.variantId})` : "";
const message = `${this.colors.red}${this.colors.reset} Failed to update "${entry.productTitle}"${variantInfo}: ${entry.errorMessage}`;
const schedulingInfo =
schedulingContext && schedulingContext.isScheduled
? ` [Scheduled Operation]`
: "";
const message = `${this.colors.red}${this.colors.reset} Failed to update "${entry.productTitle}"${variantInfo}: ${entry.errorMessage}${schedulingInfo}`;
console.error(message);
// Also log to progress file
await this.progressService.logError(entry);
// Also log to progress file with scheduling context
await this.progressService.logError(entry, schedulingContext);
}
/**
@@ -312,24 +345,182 @@ class Logger {
await this.warning(`Skipped "${productTitle}": ${reason}`);
}
/**
* Logs scheduling confirmation with operation details (Requirements 2.1, 2.3)
* @param {Object} schedulingInfo - Scheduling information
* @param {Date} schedulingInfo.scheduledTime - Target execution time
* @param {string} schedulingInfo.originalInput - Original datetime input
* @param {string} schedulingInfo.operationType - Type of operation (update/rollback)
* @param {Object} schedulingInfo.config - Operation configuration
* @returns {Promise<void>}
*/
async logSchedulingConfirmation(schedulingInfo) {
const { scheduledTime, originalInput, operationType, config } =
schedulingInfo;
await this.info("=".repeat(50));
await this.info("SCHEDULED OPERATION CONFIRMED");
await this.info("=".repeat(50));
await this.info(`Operation Type: ${operationType}`);
await this.info(`Scheduled Time: ${scheduledTime.toLocaleString()}`);
await this.info(`Original Input: ${originalInput}`);
await this.info(`Target Tag: ${config.targetTag}`);
if (operationType === "update") {
await this.info(`Price Adjustment: ${config.priceAdjustmentPercentage}%`);
}
await this.info(`Shop Domain: ${config.shopDomain}`);
const delay = scheduledTime.getTime() - new Date().getTime();
const timeRemaining = this.formatTimeRemaining(delay);
await this.info(`Time Remaining: ${timeRemaining}`);
await this.info("Press Ctrl+C to cancel the scheduled operation");
await this.info("=".repeat(50));
// Also log to progress file
await this.progressService.logSchedulingConfirmation(schedulingInfo);
}
/**
* Logs countdown updates during scheduled wait period (Requirements 2.2, 2.3)
* @param {Object} countdownInfo - Countdown information
* @param {Date} countdownInfo.scheduledTime - Target execution time
* @param {number} countdownInfo.remainingMs - Milliseconds remaining
* @returns {Promise<void>}
*/
async logCountdownUpdate(countdownInfo) {
const { scheduledTime, remainingMs } = countdownInfo;
const timeRemaining = this.formatTimeRemaining(remainingMs);
await this.info(
`Scheduled execution in: ${timeRemaining} (at ${scheduledTime.toLocaleString()})`
);
}
/**
* Logs the start of scheduled operation execution (Requirements 2.3, 5.4)
* @param {Object} executionInfo - Execution information
* @param {Date} executionInfo.scheduledTime - Original scheduled time
* @param {Date} executionInfo.actualTime - Actual execution time
* @param {string} executionInfo.operationType - Type of operation
* @returns {Promise<void>}
*/
async logScheduledExecutionStart(executionInfo) {
const { scheduledTime, actualTime, operationType } = executionInfo;
const delay = actualTime.getTime() - scheduledTime.getTime();
const delayText =
Math.abs(delay) < 1000
? "on time"
: delay > 0
? `${Math.round(delay / 1000)}s late`
: `${Math.round(Math.abs(delay) / 1000)}s early`;
await this.info("=".repeat(50));
await this.info("SCHEDULED OPERATION STARTING");
await this.info("=".repeat(50));
await this.info(`Operation Type: ${operationType}`);
await this.info(`Scheduled Time: ${scheduledTime.toLocaleString()}`);
await this.info(`Actual Start Time: ${actualTime.toLocaleString()}`);
await this.info(`Timing: ${delayText}`);
await this.info("=".repeat(50));
// Also log to progress file
await this.progressService.logScheduledExecutionStart(executionInfo);
}
/**
* Logs scheduled operation cancellation (Requirements 3.1, 3.2)
* @param {Object} cancellationInfo - Cancellation information
* @param {Date} cancellationInfo.scheduledTime - Original scheduled time
* @param {Date} cancellationInfo.cancelledTime - Time when cancelled
* @param {string} cancellationInfo.operationType - Type of operation
* @param {string} cancellationInfo.reason - Cancellation reason
* @returns {Promise<void>}
*/
async logScheduledOperationCancellation(cancellationInfo) {
const { scheduledTime, cancelledTime, operationType, reason } =
cancellationInfo;
const timeRemaining = scheduledTime.getTime() - cancelledTime.getTime();
const remainingText = this.formatTimeRemaining(timeRemaining);
await this.info("=".repeat(50));
await this.info("SCHEDULED OPERATION CANCELLED");
await this.info("=".repeat(50));
await this.info(`Operation Type: ${operationType}`);
await this.info(`Scheduled Time: ${scheduledTime.toLocaleString()}`);
await this.info(`Cancelled Time: ${cancelledTime.toLocaleString()}`);
await this.info(`Time Remaining: ${remainingText}`);
await this.info(`Reason: ${reason}`);
await this.info("=".repeat(50));
// Also log to progress file
await this.progressService.logScheduledOperationCancellation(
cancellationInfo
);
}
/**
* Format time remaining into human-readable string
* @param {number} milliseconds - Time remaining in milliseconds
* @returns {string} Formatted time string (e.g., "2h 30m 15s")
*/
formatTimeRemaining(milliseconds) {
if (milliseconds <= 0) {
return "0s";
}
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const remainingHours = hours % 24;
const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 60;
const parts = [];
if (days > 0) parts.push(`${days}d`);
if (remainingHours > 0) parts.push(`${remainingHours}h`);
if (remainingMinutes > 0) parts.push(`${remainingMinutes}m`);
if (remainingSeconds > 0 || parts.length === 0)
parts.push(`${remainingSeconds}s`);
return parts.join(" ");
}
/**
* Logs comprehensive error analysis and recommendations
* @param {Array} errors - Array of error objects
* @param {Object} summary - Operation summary statistics
* @param {Object} schedulingContext - Optional scheduling context for error analysis (Requirements 5.3, 5.4)
* @returns {Promise<void>}
*/
async logErrorAnalysis(errors, summary) {
async logErrorAnalysis(errors, summary, schedulingContext = null) {
if (!errors || errors.length === 0) {
return;
}
const operationType =
summary.successfulRollbacks !== undefined ? "ROLLBACK" : "UPDATE";
const schedulingPrefix =
schedulingContext && schedulingContext.isScheduled ? "SCHEDULED " : "";
await this.info("=".repeat(50));
await this.info(`${operationType} ERROR ANALYSIS`);
await this.info(`${schedulingPrefix}${operationType} ERROR ANALYSIS`);
await this.info("=".repeat(50));
if (schedulingContext && schedulingContext.isScheduled) {
await this.info(
`Scheduled Time: ${schedulingContext.scheduledTime.toLocaleString()}`
);
await this.info(
`Original Schedule Input: ${schedulingContext.originalInput}`
);
await this.info("=".repeat(50));
}
// Enhanced categorization for rollback operations
const categories = {};
const retryableErrors = [];
@@ -411,8 +602,8 @@ class Logger {
await this.info("\nRecommendations:");
await this.provideErrorRecommendations(categories, summary, operationType);
// Log to progress file as well
await this.progressService.logErrorAnalysis(errors);
// Log to progress file as well with scheduling context
await this.progressService.logErrorAnalysis(errors, schedulingContext);
}
/**