From a6413da67a4579866f0fd0323ddb23a7b6b9654a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 18 Sep 2026 17:00:53 +0000
Subject: [PATCH] =Cleaning up the Integrations setup a bit, started the Helcim integration work too
---
inc/managers/Errors.php | 513 ++------------------------------------------------------
1 files changed, 20 insertions(+), 493 deletions(-)
diff --git a/inc/managers/Errors.php b/inc/managers/Errors.php
index e45050c..40d0252 100644
--- a/inc/managers/Errors.php
+++ b/inc/managers/Errors.php
@@ -13,503 +13,30 @@
class Errors
{
use Instance;
- protected int $notification_threshold = 5; // Critical errors within 1 hour
- protected array $error_levels = [
- 'debug' => 0,
- 'info' => 1,
- 'warning' => 2,
- 'error' => 3,
- 'critical' => 4
- ];
-
- protected CustomTable $table;
-
- private function __construct()
- {
- $this->defineTables();
-// global $wpdb;
-// $this->wpdb = $wpdb;
-// $this->tableName = $wpdb->prefix . BASE . 'error_log';
-
- add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
-
- // Hook our send function to the cron event
- add_action('jvb_daily_error_summary', [$this, 'sendErrorSummary']);
-
- jvb_register_do_once('error_admin_action_registered', [$this, 'registerAdminAction']);
-// add_filter(BASE.'admin_action_filter', [$this, 'adminActionFilter'], 10, 3);
- }
-
- public function defineTables():void
+ /**
+ * @param string $message
+ * @param array $context
+ * @return void
+ */
+ public static function log(string $message, array $context = []):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 = AdminPages::getInstance();
-
- $admin->registerSubpage(
- 'Error Log',
- 'Errors',
- 'manage_options',
- 'jvb-error-log',
- [$this, 'renderAdminPage'],
- );
- }
-
- /**
- * Process error log operations queued through OperationQueue
- *
- * @param WP_Error|array $result The result we are filtering
- * @param object $operation The operation information
- * @param array $data The current chunk of data to process
- *
- * @return WP_Error|array Either the unfiltered WP_Error, or the result of the operation
- */
- public function processOperation(WP_Error|array $result, object $operation, array $data):WP_Error|array
- {
- if ($operation->type !== 'error_log') {
- return $result;
- }
-
- try {
-
- // Extract error data
- $component = sanitize_text_field($data['component'] ?? '');
- $message = sanitize_textarea_field($data['message'] ?? '');
- $context = $data['context'] ?? [];
- $severity = sanitize_text_field($data['severity'] ?? 'error');
-
- // Validate severity level
- if (!array_key_exists($severity, $this->error_levels)) {
- $severity = 'error'; // Default to error if invalid severity provided
- }
-
- // Determine error type from context or use component as fallback
- $error_type = sanitize_text_field($context['error_type'] ?? $component);
-
- // Store context as JSON
- $context_json = json_encode($context);
- if (json_last_error() !== JSON_ERROR_NONE) {
- // If JSON encoding fails, store only basic info
- $context_json = json_encode([
- 'error' => 'Failed to encode original context',
- 'json_error' => json_last_error_msg()
- ]);
- }
-
- // Insert into database
- $result = $this->table->insert(
- [
- 'error_type' => $error_type,
- 'component' => $component,
- 'message' => $message,
- 'context' => $context_json,
- 'severity' => $severity,
- 'user_id' => get_current_user_id(),
- 'created_at' => current_time('mysql')
- ]
- );
-
- if (!$result) {
- // If insert fails, log to PHP error log as fallback
- error_log("[ErrorHandler] Database insert failed: " . $this->table->getLastError());
- return [
- 'success' => false,
- 'message' => "[ErrorHandler] Database insert failed: " . $this->table->getLastError()
- ];
- }
-
- // Potentially track error metrics or trigger additional actions for critical errors
- if ($severity === 'critical') {
- $this->checkErrorThreshold($error_type, $component);
- }
-
- return [
- 'success' => true,
- 'result' => $result
- ];
-
- } catch (Exception $e) {
- // Fallback to PHP error log if our error handler itself fails
- error_log("[ErrorHandler Exception] " . $e->getMessage());
- return [
- 'success' => false,
- 'message' => $e->getMessage()
- ];
- }
- }
-
- /**
- * Check if error threshold has been reached for a specific error type/component
- * and take additional actions if needed
- *
- * @param string $error_type The type of error
- * @param string $component The component where the error occurred
- */
- protected function checkErrorThreshold(string $error_type, string $component)
- {
- // Get count of similar critical errors in the last hour
- $count = $this->table->count([
- 'error_type' => $error_type,
- 'component' => $component,
- 'severity' => $this->error_levels[$error_type],
- 'created_at' => ['>','DATE_SUB(NOW(), INTERVAL 1 HOUR)']
- ]);
-
- // If threshold reached, take additional actions (e.g., notify developers)
- if ($count >= $this->notification_threshold) {
- // You could send an urgent notification, Slack message, etc.
- $admin_email = get_option('admin_email');
- $subject = "[URGENT] Error Threshold Exceeded for {$component}";
- $message = "The error '{$error_type}' in component '{$component}' has occurred {$count} times in the last hour.";
-
- wp_mail($admin_email, $subject, $message);
- }
- }
-
-
- /**
- * @param string $component Where the error is being logged from
- * @param string $message The error message, usually extracted from $e->getMessage())
- * @param array $context Additional context (i.e: data that was attempted to be acted on)
- * @param string $severity
- *
- * @return array
- */
- public function log(string $component, string $message, array $context = [], string $severity = 'error'): array
- {
- try {
- // Validate severity
- if (!array_key_exists($severity, $this->error_levels)) {
- $severity = 'error';
- }
-
- // Extract info
- $error_type = sanitize_text_field($context['error_type'] ?? $component);
- $method = isset($context['method']) ? sanitize_text_field($context['method']) : null;
- $page_url = isset($context['url']) ? esc_url_raw($context['url']) : null;
- $user_id = get_current_user_id();
- $user_was_logged_in = $user_id > 0 || (!empty($context['isLoggedIn']));
-
- // Determine source from context
- $source = $context['source'] ?? (isset($context['url']) ? 'frontend' : 'backend');
-
- $result = $this->table->insert(
- [
- 'error_type' => $error_type,
- 'component' => $component,
- 'method' => $method,
- 'page_url' => $page_url,
- 'message' => sanitize_textarea_field($message),
- 'context' => json_encode($context),
- 'severity' => $severity,
- 'user_id' => $user_id ?: null,
- 'user_was_logged_in' => $user_was_logged_in ? 1 : 0,
- 'source' => $source,
- 'created_at' => current_time('mysql')
- ]);
-
- if (!$result) {
- error_log("[ErrorHandler] Database insert failed: " . $this->table->getLastError());
- return ['success' => false, 'message' => $this->table->getLastError()];
- }
-
- if ($severity === 'critical') {
- $this->checkErrorThreshold($error_type, $component);
- }
-
- return ['success' => true, 'id' => $result];
-
- } catch (Exception $e) {
- error_log("[ErrorHandler Exception] " . $e->getMessage());
- return ['success' => false, 'message' => $e->getMessage()];
+ $msg = '{'.BASE.'}';
+ if (array_key_exists('class', $context)) {
+ $msg .= '['.$context['class'].']';
+ unset($context['class']);
}
- }
-
- /**
- * @param string $component What class or function logs the error
- * @param string $message Details on the error
- * @param array $context additional context of the data that was being processed
- *
- * @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 = "[" . get_bloginfo('name') . " Critical Error] {$component}";
-
- $body = Emails::getInstance()->alert(
- 'A critical error has occurred and requires immediate attention',
- 'error'
- );
-
- $body .= Emails::getInstance()->h2('Error Details');
- $body .= Emails::getInstance()->card(
- '<p><strong>Component:</strong> ' . esc_html($component) . '</p>' .
- '<p><strong>Message:</strong></p>' .
- Emails::getInstance()->codeBlock($message),
- 'Error Information'
- );
-
+ if (array_key_exists('method', $context)) {
+ $msg .= '::'.$context['method'];
+ unset($context['method']);
+ }
+ if (array_key_exists('function', $context)) {
+ $msg .= $context['function'];
+ unset($context['function']);
+ }
+ error_log($msg.' '.$message);
if (!empty($context)) {
- $body .= Emails::getInstance()->h3('Additional Context');
- $body .= Emails::getInstance()->codeBlock(json_encode($context, JSON_PRETTY_PRINT));
+ error_log('Context: '.print_r($context, true));
}
-
- return Emails::getInstance()->sendEmail($admin_email, $subject, $body, 'CRITICAL ERROR');
}
-
- /**
- * Gather summary of the most important errors
- * @param ?string $start_date Defaults to today
- * @param ?string $end_date Defaults to today
- * @return array
- */
- public function gatherErrorSummary(?string $start_date = null, ?string $end_date = null): array
- {
- if (!$start_date) {
- $start_date = gmdate('Y-m-d 00:00:00', strtotime('-1 day'));
- }
- if (!$end_date) {
- $end_date = gmdate('Y-m-d 23:59:59');
- }
-
- // Most frequent error patterns (deduplicated by component/method/message)
- $frequent = $this->table->getMany([
- 'where' => [
- 'created_at' => ['BETWEEN', "{$start_date} AND {$end_date}"]
- ]
- ]);
-
- return [
- 'errors' => $frequent,
- 'date_range' => ['start' => $start_date, 'end' => $end_date]
- ];
- }
-
- /**
- * Send daily error summary email to administrator
- * @return bool Whether email is sent
- */
- public function sendErrorSummary():bool
- {
- $summary = $this->gatherErrorSummary();
-
- if (empty($summary['frequent']) && empty($summary['critical'])) {
- return false;
- }
-
- $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');
-
- // Header with alert
- $body = Emails::getInstance()->h1('Daily Error Summary');
- $body .= sprintf('<p>Error summary for <strong>%s</strong></p>', $yesterday);
-
- // Summary stats in a grid
- if (!empty($summary['stats'])) {
- $stats = [
- Emails::getInstance()->stat($summary['stats']->total_errors, 'Total Errors'),
- Emails::getInstance()->stat($summary['stats']->critical_count, 'Critical', 'Requires attention'),
- Emails::getInstance()->stat($summary['stats']->error_count, 'Errors'),
- Emails::getInstance()->stat($summary['stats']->warning_count, 'Warnings')
- ];
- $body .= Emails::getInstance()->grid($stats, 4);
- }
-
- // Alert if critical errors exist
- if (!empty($summary['critical'])) {
- $body .= Emails::getInstance()->alert(
- sprintf('Found %d critical errors that need immediate attention', count($summary['critical'])),
- 'error'
- );
- }
-
- $body .= Emails::getInstance()->spacer();
-
- // Frequent errors section
- if (!empty($summary['frequent'])) {
- $body .= Emails::getInstance()->h2('Most Frequent Errors');
-
- foreach ($summary['frequent'] as $error) {
- $cardContent = Emails::getInstance()->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:' . Emails::getInstance()->colours['dark-200'] . ';">
- Source: ' . esc_html($error->source) . ' |
- Logged in: ' . $error->logged_in_count . ' |
- Logged out: ' . $error->logged_out_count . '
- </p>';
-
- $body .= Emails::getInstance()->card($cardContent, $error->component);
- }
- }
-
- // Critical errors section
- if (!empty($summary['critical'])) {
- $body .= Emails::getInstance()->spacer(30);
- $body .= Emails::getInstance()->h2('Recent Critical Errors');
-
- foreach ($summary['critical'] as $error) {
- $cardContent = '<p><strong>Time:</strong> ' . esc_html($error->created_at) . '</p>';
- $cardContent .= '<p><strong>Message:</strong></p>';
- $cardContent .= Emails::getInstance()->codeBlock($error->message);
-
- // 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 .= Emails::getInstance()->codeBlock(json_encode($context, JSON_PRETTY_PRINT));
- }
- }
-
- $body .= Emails::getInstance()->card($cardContent, $error->component . ': ' . $error->error_type);
- }
- }
-
- // Dashboard link
- $admin_url = admin_url('admin.php?page=jvb-error-logs');
- $body .= Emails::getInstance()->spacer(30);
- $body .= Emails::getInstance()->button($admin_url, 'View Detailed Logs');
-
- return Emails::getInstance()->sendEmail($admin_email, $subject, $body, 'ERROR SUMMARY');
- }
-
- /**
- * Get HTML version of the error summary for nicer emails
- *
- * @param array $summary The error summary data
- * @return string HTML formatted error summary
- */
- protected function getHtmlErrorSummary(array $summary):string
- {
- $html = '<div style="font-family: sans-serif; max-width: 800px; margin: 0 auto;">';
- $html .= '<h1 style="color:#FF0080;border-bottom:2px solid #1B1B1B;padding-bottom:10px;">Error Summary</h1>';
-
- // Frequent errors table
- if (!empty($summary['frequent'])) {
- $html .= '<h2>Most Frequent Errors</h2>';
- $html .= '<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;">';
- $html .= '<tr style="background-color: #1B1B1B; color: #EFEFEF;">';
- $html .= '<th style="padding: 8px; text-align: left; border: 1px solid #ddd;">Component</th>';
- $html .= '<th style="padding: 8px; text-align: left; border: 1px solid #ddd;">Error Type</th>';
- $html .= '<th style="padding: 8px; text-align: left; border: 1px solid #ddd;">Message</th>';
- $html .= '<th style="padding: 8px; text-align: center; border: 1px solid #ddd;">Count</th>';
- $html .= '</tr>';
-
- foreach ($summary['frequent'] as $error) {
- $html .= '<tr>';
- $html .= '<td style="padding:8px;border:1px solid #ddd;">' . esc_html($error->component) . '</td>';
- $html .= '<td style="padding:8px;border:1px solid #ddd;">' . esc_html($error->error_type) . '</td>';
- $html .= '<td style="padding:8px;border:1px solid #ddd;">' . esc_html(wp_trim_words($error->message, 10, '...')) . '</td>';
- $html .= '<td style="padding:8px;border:1px solid #ddd;text-align:center;"><strong>' . esc_html($error->count) . '</strong></td>';
- $html .= '</tr>';
- }
-
- $html .= '</table>';
- }
-
- // Critical errors section
- if (!empty($summary['critical'])) {
- $html .= '<h2>Recent Critical Errors</h2>';
-
- foreach ($summary['critical'] as $error) {
- $html .= '<div style="margin-bottom: 20px; padding: 15px; border-left: 4px solid #FF0080; background-color: #fff9fb;">';
- $html .= '<h3 style="margin-top: 0; color: #1B1B1B;">' . esc_html($error->component) . ': ' . esc_html($error->error_type) . '</h3>';
- $html .= '<p><strong>Time:</strong> ' . esc_html($error->created_at) . '</p>';
- $html .= '<p><strong>Message:</strong> ' . esc_html($error->message) . '</p>';
-
- // Context display
- if (!empty($error->context)) {
- $context = json_decode($error->context, true);
- if (is_array($context)) {
- $html .= '<div style="margin-top: 10px;">';
- $html .= '<strong>Context:</strong>';
- $html .= '<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px; overflow: auto; max-height: 200px;">';
- $html .= esc_html(json_encode($context, JSON_PRETTY_PRINT));
- $html .= '</pre>';
- $html .= '</div>';
- }
- }
-
- $html .= '</div>';
- }
- }
-
- // Dashboard link
- $admin_url = admin_url('admin.php?page=jvb-error-logs');
- $html .= '<p style="margin-top: 30px; padding-top: 15px; border-top: 1px solid #ddd;">';
- $html .= '<a href="' . esc_url($admin_url) . '" style="display: inline-block; background-color: #FF0080; color: white; padding: 10px 15px; text-decoration: none; border-radius: 4px;">View All Errors in Dashboard</a>';
- $html .= '</p>';
-
- $html .= '</div>';
-
- return $html;
- }
-
- public function renderAdminPage()
- {
- ?>
- <h1>Error Logs</h1>
- <?php
- }
-
- public function getErrors(WP_REST_Request $request) {
- $params = $this->buildParams($request);
-
- }
-
-
-
- protected function buildParams(WP_REST_Request $request):array {
- $allowedSeverity = [
- 'all',
- 'notice',
- 'warning',
- 'error',
- 'critical',
- ];
- $allowedComponents = [
- 'all',
- //Get all components
- ];
- $severity = $request->get_param('severity')??'all';
- $component = $request->get_param('component')??'all';
-
- return [
- 'page' => (int)$request->get_param('page'),
- 'severity' => (in_array($severity, $allowedSeverity)) ? $severity : 'all',
- 'component' => (in_array($component, $allowedComponents)) ? $component : 'all',
- ];
- }
}
--
Gitblit v1.10.0