231 lines
6.6 KiB
PHP
231 lines
6.6 KiB
PHP
<?php
|
|
/**
|
|
* Error logging system.
|
|
*
|
|
* @package WPFMJ
|
|
* @subpackage WPFMJ/includes
|
|
*/
|
|
|
|
class WPFMJ_Error_Logger {
|
|
|
|
/**
|
|
* Table name.
|
|
*/
|
|
private $table_name;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function __construct() {
|
|
global $wpdb;
|
|
$this->table_name = $wpdb->prefix . 'wpfmj_error_log';
|
|
}
|
|
|
|
/**
|
|
* Log an error.
|
|
*/
|
|
public function log($automation_id, $form_entry_id, $error_type, $error_message, $retry_count = 0) {
|
|
global $wpdb;
|
|
|
|
return $wpdb->insert(
|
|
$this->table_name,
|
|
array(
|
|
'automation_id' => intval($automation_id),
|
|
'form_entry_id' => intval($form_entry_id),
|
|
'error_type' => sanitize_text_field($error_type),
|
|
'error_message' => sanitize_textarea_field($error_message),
|
|
'retry_count' => intval($retry_count),
|
|
'resolved' => 0
|
|
),
|
|
array('%d', '%d', '%s', '%s', '%d', '%d')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get errors for an automation.
|
|
*
|
|
* IMPORTANT: Results contain user-generated error messages.
|
|
* Always escape output when displaying: esc_html(), esc_attr(), etc.
|
|
*/
|
|
public function get_errors($automation_id, $limit = 50, $resolved = null) {
|
|
global $wpdb;
|
|
|
|
// Build query with proper parameter binding
|
|
if ($resolved !== null) {
|
|
$sql = $wpdb->prepare(
|
|
"SELECT * FROM {$this->table_name}
|
|
WHERE automation_id = %d AND resolved = %d
|
|
ORDER BY created_at DESC
|
|
LIMIT %d",
|
|
$automation_id,
|
|
$resolved ? 1 : 0,
|
|
$limit
|
|
);
|
|
} else {
|
|
$sql = $wpdb->prepare(
|
|
"SELECT * FROM {$this->table_name}
|
|
WHERE automation_id = %d
|
|
ORDER BY created_at DESC
|
|
LIMIT %d",
|
|
$automation_id,
|
|
$limit
|
|
);
|
|
}
|
|
|
|
$results = $wpdb->get_results($sql, ARRAY_A);
|
|
|
|
// Sanitize error messages for safe output
|
|
if (is_array($results)) {
|
|
foreach ($results as &$result) {
|
|
$result['error_message'] = sanitize_text_field($result['error_message']);
|
|
$result['error_type'] = sanitize_text_field($result['error_type']);
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get error count for an automation.
|
|
*/
|
|
public function get_error_count($automation_id, $resolved = null) {
|
|
global $wpdb;
|
|
|
|
// Build query with proper parameter binding
|
|
if ($resolved !== null) {
|
|
$sql = $wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$this->table_name} WHERE automation_id = %d AND resolved = %d",
|
|
$automation_id,
|
|
$resolved ? 1 : 0
|
|
);
|
|
} else {
|
|
$sql = $wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$this->table_name} WHERE automation_id = %d",
|
|
$automation_id
|
|
);
|
|
}
|
|
|
|
return (int) $wpdb->get_var($sql);
|
|
}
|
|
|
|
/**
|
|
* Get error counts for multiple automations in a single query.
|
|
* Prevents N+1 query problems when displaying dashboard.
|
|
*
|
|
* @param array $automation_ids Array of automation IDs
|
|
* @param bool|null $resolved Filter by resolved status (null = all)
|
|
* @return array Associative array of automation_id => count
|
|
*/
|
|
public function get_error_counts_bulk($automation_ids, $resolved = null) {
|
|
global $wpdb;
|
|
|
|
if (empty($automation_ids)) {
|
|
return array();
|
|
}
|
|
|
|
// Sanitize all IDs
|
|
$automation_ids = array_map('absint', $automation_ids);
|
|
$placeholders = implode(',', array_fill(0, count($automation_ids), '%d'));
|
|
|
|
// Build query
|
|
if ($resolved !== null) {
|
|
$sql = $wpdb->prepare(
|
|
"SELECT automation_id, COUNT(*) as error_count
|
|
FROM {$this->table_name}
|
|
WHERE automation_id IN ($placeholders) AND resolved = %d
|
|
GROUP BY automation_id",
|
|
array_merge($automation_ids, array($resolved ? 1 : 0))
|
|
);
|
|
} else {
|
|
$sql = $wpdb->prepare(
|
|
"SELECT automation_id, COUNT(*) as error_count
|
|
FROM {$this->table_name}
|
|
WHERE automation_id IN ($placeholders)
|
|
GROUP BY automation_id",
|
|
$automation_ids
|
|
);
|
|
}
|
|
|
|
$results = $wpdb->get_results($sql, ARRAY_A);
|
|
|
|
// Convert to associative array
|
|
$counts = array();
|
|
foreach ($results as $row) {
|
|
$counts[(int)$row['automation_id']] = (int)$row['error_count'];
|
|
}
|
|
|
|
return $counts;
|
|
}
|
|
|
|
/**
|
|
* Mark error as resolved.
|
|
*/
|
|
public function mark_resolved($error_id) {
|
|
global $wpdb;
|
|
|
|
return $wpdb->update(
|
|
$this->table_name,
|
|
array('resolved' => 1),
|
|
array('id' => $error_id),
|
|
array('%d'),
|
|
array('%d')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Clean up old error logs.
|
|
*
|
|
* @param int $days Number of days to retain resolved errors. Default 90 days.
|
|
* Can be filtered using 'wpfmj_error_log_retention_days'.
|
|
*/
|
|
public function cleanup_old_logs($days = null) {
|
|
global $wpdb;
|
|
|
|
// Allow filtering of retention period
|
|
if ($days === null) {
|
|
$days = apply_filters('wpfmj_error_log_retention_days', 90);
|
|
}
|
|
|
|
// Ensure positive integer
|
|
$days = absint($days);
|
|
if ($days < 1) {
|
|
$days = 90; // Fallback to default
|
|
}
|
|
|
|
$date = gmdate('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$deleted = $wpdb->query(
|
|
$wpdb->prepare(
|
|
"DELETE FROM {$this->table_name} WHERE created_at < %s AND resolved = 1",
|
|
$date
|
|
)
|
|
);
|
|
|
|
// Log cleanup activity
|
|
if ($deleted > 0) {
|
|
error_log("WPFMJ: Cleaned up {$deleted} error log entries older than {$days} days");
|
|
}
|
|
|
|
return $deleted;
|
|
}
|
|
|
|
/**
|
|
* Get recent errors across all automations.
|
|
*/
|
|
public function get_recent_errors($limit = 20) {
|
|
global $wpdb;
|
|
|
|
$sql = "SELECT * FROM {$this->table_name}
|
|
ORDER BY created_at DESC
|
|
LIMIT %d";
|
|
|
|
return $wpdb->get_results($wpdb->prepare($sql, $limit), ARRAY_A);
|
|
}
|
|
}
|
|
|
|
// Register cleanup cron job handler
|
|
add_action('wpfmj_cleanup_error_logs', function() {
|
|
$logger = new WPFMJ_Error_Logger();
|
|
// Retention period can be customized via filter
|
|
$logger->cleanup_old_logs();
|
|
});
|