| | |
| | | 'critical' => 4 |
| | | ]; |
| | | |
| | | protected CustomTable $table; |
| | | |
| | | public function __construct() |
| | | { |
| | | global $wpdb; |
| | | $this->wpdb = $wpdb; |
| | | $this->tableName = $wpdb->prefix . BASE . 'error_log'; |
| | | $this->defineTables(); |
| | | // global $wpdb; |
| | | // $this->wpdb = $wpdb; |
| | | // $this->tableName = $wpdb->prefix . BASE . 'error_log'; |
| | | |
| | | add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3); |
| | | |
| | |
| | | // add_filter(BASE.'admin_action_filter', [$this, 'adminActionFilter'], 10, 3); |
| | | } |
| | | |
| | | public function defineTables():void |
| | | { |
| | | $table = CustomTable::for('error_log'); |
| | | $table->setColumns([ |
| | | 'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT', |
| | | 'error_type' => 'varchar(50) NOT NULL', |
| | | 'component' => 'varchar(100) NOT NULL', |
| | | 'method' => 'varchar(100) DEFAULT NULL', |
| | | 'page_url' => 'varchar(255) DEFAULT NULL', |
| | | 'message' => 'text NOT NULL', |
| | | 'context' => 'JSON', |
| | | 'severity' => 'ENUM(\'high\',\'normal\',\'low\') DEFAULT \'normal\'', |
| | | 'user_id' => $table->getUserIDType().' DEFAULT NULL', |
| | | 'user_was_logged_in' => 'tinyint(1) NOT NULL', |
| | | 'source' => 'ENUM(\'frontend\', \'backend\') NOT NULL', |
| | | 'created_at' => 'timestamp DEFAULT CURRENT_TIMESTAMP', |
| | | ]); |
| | | |
| | | $table->setKeys([ |
| | | ['key' => 'PRIMARY', 'value' => 'id'], |
| | | '`created_at` (`created_at`)', |
| | | '`component_severity_date` (`component`, `severity`, `created_at`)', |
| | | '`error_type_date` (`error_type`, `created_at`)', |
| | | '`severity_date` (`severity`, `created_at`)' |
| | | ]); |
| | | |
| | | $table->defineTable(); |
| | | $this->table = $table; |
| | | } |
| | | public function registerAdminAction():void |
| | | { |
| | | $admin = JVB()->admin(); |
| | |
| | | * |
| | | * @return bool Whether the notification is sent successfully |
| | | */ |
| | | protected function notifyAdmin(string $component, string $message, array $context):bool |
| | | { |
| | | $admin_email = get_option('admin_email'); |
| | | $subject = "[edmonton.ink Critical Error] {$component}"; |
| | | $body = "Error: {$message}\n\nContext: " . print_r($context, true); |
| | | protected function notifyAdmin(string $component, string $message, array $context):bool |
| | | { |
| | | $admin_email = get_option('admin_email'); |
| | | $subject = "[" . get_bloginfo('name') . " Critical Error] {$component}"; |
| | | |
| | | return JVB()->email()->sendEmail($admin_email, $subject, $body); |
| | | } |
| | | $body = JVB()->email()->alert( |
| | | 'A critical error has occurred and requires immediate attention', |
| | | 'error' |
| | | ); |
| | | |
| | | $body .= JVB()->email()->h2('Error Details'); |
| | | $body .= JVB()->email()->card( |
| | | '<p><strong>Component:</strong> ' . esc_html($component) . '</p>' . |
| | | '<p><strong>Message:</strong></p>' . |
| | | JVB()->email()->codeBlock($message), |
| | | 'Error Information' |
| | | ); |
| | | |
| | | if (!empty($context)) { |
| | | $body .= JVB()->email()->h3('Additional Context'); |
| | | $body .= JVB()->email()->codeBlock(json_encode($context, JSON_PRETTY_PRINT)); |
| | | } |
| | | |
| | | return JVB()->email()->sendEmail($admin_email, $subject, $body, 'CRITICAL ERROR'); |
| | | } |
| | | |
| | | /** |
| | | * Gather summary of the most important errors |
| | |
| | | * Send daily error summary email to administrator |
| | | * @return bool Whether email is sent |
| | | */ |
| | | public function sendErrorSummary():bool |
| | | { |
| | | // Get summary data |
| | | $summary = $this->gatherErrorSummary(); |
| | | public function sendErrorSummary():bool |
| | | { |
| | | $summary = $this->gatherErrorSummary(); |
| | | |
| | | // Only send if there are errors |
| | | if (empty($summary['frequent']) && empty($summary['critical'])) { |
| | | return false; |
| | | } |
| | | if (empty($summary['frequent']) && empty($summary['critical'])) { |
| | | return false; |
| | | } |
| | | |
| | | $admin_email = get_option('admin_email'); |
| | | $site_name = get_bloginfo('name'); |
| | | $today = date('Y-m-d'); |
| | | $yesterday = date('Y-m-d', strtotime('-1 day')); |
| | | $admin_email = get_option('admin_email'); |
| | | $site_name = get_bloginfo('name'); |
| | | $yesterday = date('Y-m-d', strtotime('-1 day')); |
| | | $subject = "[{$site_name}] Daily Error Summary - " . date('Y-m-d'); |
| | | |
| | | $subject = "[{$site_name}] Daily Error Summary - {$today}"; |
| | | // Header with alert |
| | | $body = JVB()->email()->h1('Daily Error Summary'); |
| | | $body .= sprintf('<p>Error summary for <strong>%s</strong></p>', $yesterday); |
| | | |
| | | // Build email body |
| | | $body = "= Error Summary for {$yesterday} =\n\n"; |
| | | // Summary stats in a grid |
| | | if (!empty($summary['stats'])) { |
| | | $stats = [ |
| | | JVB()->email()->stat($summary['stats']->total_errors, 'Total Errors'), |
| | | JVB()->email()->stat($summary['stats']->critical_count, 'Critical', 'Requires attention'), |
| | | JVB()->email()->stat($summary['stats']->error_count, 'Errors'), |
| | | JVB()->email()->stat($summary['stats']->warning_count, 'Warnings') |
| | | ]; |
| | | $body .= JVB()->email()->grid($stats, 4); |
| | | } |
| | | |
| | | // Add frequent errors section |
| | | if (!empty($summary['frequent'])) { |
| | | $body .= "== Most Frequent Errors ==\n\n"; |
| | | // Alert if critical errors exist |
| | | if (!empty($summary['critical'])) { |
| | | $body .= JVB()->email()->alert( |
| | | sprintf('Found %d critical errors that need immediate attention', count($summary['critical'])), |
| | | 'error' |
| | | ); |
| | | } |
| | | |
| | | foreach ($summary['frequent'] as $index => $error) { |
| | | $body .= ($index + 1) . ". [{$error->component}] {$error->error_type}\n"; |
| | | $body .= " Message: " . wp_trim_words($error->message, 20, '...') . "\n"; |
| | | $body .= " Count: {$error->count}\n\n"; |
| | | } |
| | | } |
| | | $body .= JVB()->email()->spacer(20); |
| | | |
| | | // Add critical errors section |
| | | if (!empty($summary['critical'])) { |
| | | $body .= "== Recent Critical Errors ==\n\n"; |
| | | // Frequent errors section |
| | | if (!empty($summary['frequent'])) { |
| | | $body .= JVB()->email()->h2('Most Frequent Errors'); |
| | | |
| | | foreach ($summary['critical'] as $index => $error) { |
| | | $body .= ($index + 1) . ". [{$error->component}] {$error->error_type}\n"; |
| | | $body .= " Time: {$error->created_at}\n"; |
| | | $body .= " Message: " . $error->message . "\n\n"; |
| | | foreach ($summary['frequent'] as $error) { |
| | | $cardContent = JVB()->email()->badge($error->count . 'x', 'warning') . ' '; |
| | | $cardContent .= '<strong>' . esc_html($error->error_type) . '</strong>'; |
| | | $cardContent .= '<p style="margin:10px 0 5px 0;font-size:13px;">' . esc_html(wp_trim_words($error->message, 15)) . '</p>'; |
| | | $cardContent .= '<p style="margin:0;font-size:12px;color:' . JVB()->email()->colours['dark-200'] . ';"> |
| | | Source: ' . esc_html($error->source) . ' | |
| | | Logged in: ' . $error->logged_in_count . ' | |
| | | Logged out: ' . $error->logged_out_count . ' |
| | | </p>'; |
| | | |
| | | // Include context for critical errors if available |
| | | if (!empty($error->context)) { |
| | | $context = json_decode($error->context, true); |
| | | if (is_array($context)) { |
| | | $body .= " Context:\n"; |
| | | foreach ($context as $key => $value) { |
| | | if (is_array($value) || is_object($value)) { |
| | | $value = json_encode($value); |
| | | } |
| | | $body .= " - {$key}: {$value}\n"; |
| | | } |
| | | } |
| | | $body .= "\n"; |
| | | } |
| | | } |
| | | } |
| | | $body .= JVB()->email()->card($cardContent, $error->component); |
| | | } |
| | | } |
| | | |
| | | // Add dashboard link if available |
| | | $admin_url = admin_url('admin.php?page=jvb-error-logs'); |
| | | $body .= "View detailed error logs in the dashboard: {$admin_url}\n\n"; |
| | | // Critical errors section |
| | | if (!empty($summary['critical'])) { |
| | | $body .= JVB()->email()->spacer(30); |
| | | $body .= JVB()->email()->h2('Recent Critical Errors'); |
| | | |
| | | // Send the email |
| | | $sent = JVB()->email()->sendEmail($admin_email, $subject, $body, 'ERROR SUMMARY'); |
| | | foreach ($summary['critical'] as $error) { |
| | | $cardContent = '<p><strong>Time:</strong> ' . esc_html($error->created_at) . '</p>'; |
| | | $cardContent .= '<p><strong>Message:</strong></p>'; |
| | | $cardContent .= JVB()->email()->codeBlock($error->message); |
| | | |
| | | // Log that summary was sent |
| | | if ($sent) { |
| | | error_log("[ErrorHandler] Daily error summary sent to {$admin_email}"); |
| | | } else { |
| | | error_log("[ErrorHandler] Daily error summary was not sent."); |
| | | } |
| | | // Include context if available |
| | | if (!empty($error->context)) { |
| | | $context = json_decode($error->context, true); |
| | | if (is_array($context)) { |
| | | $cardContent .= '<p><strong>Context:</strong></p>'; |
| | | $cardContent .= JVB()->email()->codeBlock(json_encode($context, JSON_PRETTY_PRINT)); |
| | | } |
| | | } |
| | | |
| | | return $sent; |
| | | } |
| | | $body .= JVB()->email()->card($cardContent, $error->component . ': ' . $error->error_type); |
| | | } |
| | | } |
| | | |
| | | // Dashboard link |
| | | $admin_url = admin_url('admin.php?page=jvb-error-logs'); |
| | | $body .= JVB()->email()->spacer(30); |
| | | $body .= JVB()->email()->button($admin_url, 'View Detailed Logs'); |
| | | |
| | | return JVB()->email()->sendEmail($admin_email, $subject, $body, 'ERROR SUMMARY'); |
| | | } |
| | | |
| | | /** |
| | | * Get HTML version of the error summary for nicer emails |