PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.21
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.21
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / telemetry / class-telemetry-manager.php

class-telemetry-manager.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.21, at telemetry/class-telemetry-manager.php

663 lines 21.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // If this file is called directly, abort.
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Main Telemetry Manager Class
9 *
10 * Handles telemetry system initialization and WordPress integration
11 */
12 class Metasync_Telemetry_Manager {
13
14 /**
15 * Telemetry collector instance (Sentry format)
16 * @var Metasync_Sentry_Telemetry
17 */
18 private $telemetry_collector;
19
20 /**
21 * Sentry integration instance
22 * @var Metasync_Sentry_Integration
23 */
24 private $sentry_integration;
25
26 /**
27 * Plugin start time for performance tracking
28 * @var float
29 */
30 private $plugin_start_time;
31
32 /**
33 * Page start time for performance tracking
34 * @var float
35 */
36 private $page_start_time;
37
38 /**
39 * Whether telemetry is enabled
40 * @var bool
41 */
42 private $telemetry_enabled = true;
43
44 /**
45 * Track sent errors to prevent duplicates
46 * @var array
47 */
48 private $sent_errors = array();
49
50 /**
51 * Maximum number of errors to track in memory
52 * @var int
53 */
54 private $max_tracked_errors = 100;
55
56 /**
57 * Cached memory limit to avoid repeated parsing
58 * @var int|null
59 */
60 private static $cached_memory_limit = null;
61
62 /**
63 * Memory check counter for reduced frequency
64 * @var int
65 */
66 private static $memory_check_counter = 0;
67
68 /**
69 * Singleton instance
70 * @var Metasync_Telemetry_Manager
71 */
72 private static $instance = null;
73
74 /**
75 * Get singleton instance
76 *
77 * @return Metasync_Telemetry_Manager
78 */
79 public static function get_instance() {
80 if (self::$instance === null) {
81 self::$instance = new self();
82 }
83 return self::$instance;
84 }
85
86 /**
87 * Private constructor for singleton
88 */
89 private function __construct() {
90 $this->plugin_start_time = microtime(true);
91 $this->init_telemetry();
92 #$this->setup_hooks();
93 }
94
95 /**
96 * Initialize telemetry system
97 */
98 private function init_telemetry() {
99 // Check if telemetry is enabled (allow opt-out)
100 $this->telemetry_enabled = $this->is_telemetry_enabled();
101
102 if (!$this->telemetry_enabled) {
103 return;
104 }
105
106 try {
107 $this->telemetry_collector = new Metasync_Sentry_Telemetry();
108
109 // Initialize WordPress-native Sentry integration
110 global $metasync_sentry_wordpress;
111 $this->sentry_integration = $metasync_sentry_wordpress;
112
113 } catch (Exception $e) {
114 // Fallback if telemetry initialization fails
115 // error_log('MetaSync: Telemetry initialization failed: ' . $e->getMessage());
116 $this->telemetry_enabled = false;
117 }
118 }
119
120 /**
121 * Setup WordPress hooks
122 */
123 private function setup_hooks() {
124 if (!$this->telemetry_enabled) {
125 return;
126 }
127
128 // Plugin lifecycle hooks
129 add_action('init', array($this, 'on_plugin_init'), 1);
130 add_action('wp_loaded', array($this, 'on_wp_loaded'));
131 add_action('shutdown', array($this, 'on_shutdown'));
132
133 // Error handling hooks - removed global handlers to prevent capturing system-wide errors
134 // Global error handlers are now managed by wordpress-error-handler.php with plugin-specific filtering
135
136 // Performance monitoring hooks
137 add_action('wp_head', array($this, 'start_page_timer'));
138 add_action('wp_footer', array($this, 'end_page_timer'));
139
140 // Database query monitoring (if SAVEQUERIES is enabled)
141 if (defined('SAVEQUERIES') && constant('SAVEQUERIES')) {
142 add_action('shutdown', array($this, 'analyze_db_queries'));
143 }
144
145 // Removed: Plugin activation/deactivation hooks - already handled by wordpress-error-handler.php
146 // Removed: Queue processing - now using Sentry directly
147
148 // Hook into existing log manager for integration
149 add_action('metasync_log_preparation', array($this, 'on_log_preparation'));
150 }
151
152 /**
153 * Check if telemetry is enabled
154 *
155 * @return bool
156 */
157 private function is_telemetry_enabled() {
158 // Allow users to opt out via wp-config.php
159 if (defined('METASYNC_DISABLE_TELEMETRY') && constant('METASYNC_DISABLE_TELEMETRY')) {
160 return false;
161 }
162
163 // Allow admin to disable via options
164 $options = get_option('metasync_options', array());
165 if (isset($options['disable_telemetry']) && $options['disable_telemetry']) {
166 return false;
167 }
168
169 // Check if we're in development/testing environment
170 if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_LOCAL_DEV') && constant('WP_LOCAL_DEV')) {
171 return false;
172 }
173
174 return true;
175 }
176
177 /**
178 * Handle plugin initialization
179 */
180 public function on_plugin_init() {
181 if (!$this->telemetry_enabled) return;
182
183 $this->send_message('Plugin initialized', 'debug', array(
184 'wp_version' => get_bloginfo('version'),
185 'php_version' => PHP_VERSION,
186 'plugin_version' => defined('METASYNC_VERSION') ? METASYNC_VERSION : '1.0.0'
187 ));
188 }
189
190 /**
191 * Handle WordPress loaded event
192 */
193 public function on_wp_loaded() {
194 if (!$this->telemetry_enabled) return;
195
196 $load_time = microtime(true) - $this->plugin_start_time;
197 $this->send_performance('plugin_load', $load_time, array(
198 'memory_usage' => memory_get_usage(true),
199 'active_plugins' => count(get_option('active_plugins', array()))
200 ));
201 }
202
203 /**
204 * Handle shutdown event
205 */
206 public function on_shutdown() {
207 if (!$this->telemetry_enabled) return;
208
209 $total_time = microtime(true) - $this->plugin_start_time;
210 $peak_memory = memory_get_peak_usage(true);
211
212 // Send final performance metrics
213 $this->send_performance('plugin_shutdown', $total_time, array(
214 'peak_memory' => $peak_memory,
215 'final_memory' => memory_get_usage(true)
216 ), false); // Don't queue on shutdown
217 }
218
219 /**
220 * Capture WordPress die events - REMOVED
221 * This method was removed to prevent capturing system-wide wp_die events
222 * Error handling is now managed by wordpress-error-handler.php with plugin-specific filtering
223 */
224 // public function capture_wp_die($message) - REMOVED TO PREVENT SYSTEM-WIDE ERROR CAPTURE
225
226 /**
227 * Capture PHP errors - REMOVED
228 * This method was removed to prevent capturing system-wide PHP errors
229 * Error handling is now managed by wordpress-error-handler.php with plugin-specific filtering
230 */
231 // public function capture_php_error($errno, $errstr, $errfile, $errline) - REMOVED TO PREVENT SYSTEM-WIDE ERROR CAPTURE
232
233 /**
234 * Capture uncaught exceptions - REMOVED
235 * This method was removed to prevent capturing system-wide exceptions
236 * Error handling is now managed by wordpress-error-handler.php with plugin-specific filtering
237 */
238 // public function capture_uncaught_exception($exception) - REMOVED TO PREVENT SYSTEM-WIDE ERROR CAPTURE
239
240 /**
241 * Start page load timer
242 */
243 public function start_page_timer() {
244 if (!$this->telemetry_enabled) return;
245
246 $this->page_start_time = microtime(true);
247 }
248
249 /**
250 * End page load timer and send performance data
251 */
252 public function end_page_timer() {
253 if (!$this->telemetry_enabled || !isset($this->page_start_time)) return;
254
255 $page_load_time = microtime(true) - $this->page_start_time;
256
257 $this->send_performance('page_load', $page_load_time, array(
258 'is_admin' => is_admin(),
259 'query_count' => get_num_queries(),
260 'memory_usage' => memory_get_usage(true)
261 ));
262 }
263
264 /**
265 * Analyze database queries if SAVEQUERIES is enabled
266 */
267 public function analyze_db_queries() {
268 if (!$this->telemetry_enabled) return;
269
270 global $wpdb;
271 if (!isset($wpdb->queries) || empty($wpdb->queries)) {
272 return;
273 }
274
275 $slow_queries = array();
276 $total_time = 0;
277
278 foreach ($wpdb->queries as $query) {
279 $query_time = $query[1];
280 $total_time += $query_time;
281
282 // Log slow queries (> 1 second)
283 if ($query_time > 1.0) {
284 $slow_queries[] = array(
285 'query' => $query[0],
286 'time' => $query_time,
287 'calling_function' => $query[2]
288 );
289 }
290 }
291
292 if (!empty($slow_queries)) {
293 $this->send_message('Slow database queries detected', 'warning', array(
294 'slow_query_count' => count($slow_queries),
295 'total_queries' => count($wpdb->queries),
296 'total_time' => $total_time,
297 'slow_queries' => array_slice($slow_queries, 0, 5) // Limit to first 5
298 ));
299 }
300 }
301
302 /**
303 * REMOVED: Plugin activation/deactivation tracking
304 * These are now handled automatically by MetaSync_WordPress_Error_Handler
305 * in wordpress-error-handler.php (lines 98-99, 198-224)
306 */
307
308 /**
309 * Handle log preparation event
310 */
311 public function on_log_preparation() {
312 if (!$this->telemetry_enabled) return;
313
314 $this->send_message('Log preparation started', 'debug', array(
315 'event_type' => 'log_preparation'
316 ));
317 }
318
319
320 /**
321 * Flush telemetry queue - REMOVED (now using Sentry directly)
322 */
323 public function flush_telemetry_queue() {
324 // No longer needed - Sentry handles sending directly
325 }
326
327 /**
328 * Send exception telemetry to Sentry
329 *
330 * @param Exception|Error|string $exception Exception to send
331 * @param array $context Additional context
332 * @param bool $use_queue Deprecated - kept for backward compatibility
333 */
334 public function send_exception($exception, $context = array(), $use_queue = true, $background = true) {
335 if (!$this->telemetry_enabled) return;
336
337 // Generate error fingerprint for deduplication
338 $error_fingerprint = $this->generate_error_fingerprint('exception', $exception, $context);
339
340 // Check if this error has already been sent
341 if ($this->is_error_already_sent($error_fingerprint)) {
342 return; // Skip sending duplicate error
343 }
344
345 // Check memory usage before sending telemetry
346 if (!$this->is_memory_usage_safe(0.5)) {
347 // error_log('MetaSync Telemetry: EMERGENCY - Disabling due to high memory usage');
348 $this->telemetry_enabled = false; // Disable for this request
349 return;
350 }
351
352 // Send directly to Sentry
353 if ($this->sentry_integration) {
354 $success = $this->sentry_integration->captureException($exception, $context);
355
356 // Only mark as sent if the telemetry call was successful
357 if ($success) {
358 $this->mark_error_as_sent($error_fingerprint);
359 }
360 }
361 }
362
363 /**
364 * Send message telemetry to Sentry
365 *
366 * @param string $message Message to send
367 * @param string $level Log level
368 * @param array $context Additional context
369 * @param bool $use_queue Deprecated - kept for backward compatibility
370 */
371 public function send_message($message, $level = 'info', $context = array(), $use_queue = true) {
372 if (!$this->telemetry_enabled) return;
373
374 // Only send fatal/error-level messages — drop info, warning, debug
375 if (!in_array($level, ['error', 'fatal', 'critical'], true)) {
376 return;
377 }
378
379 // Generate error fingerprint for deduplication (only for error level messages)
380 $error_fingerprint = null;
381 if (in_array($level, ['error', 'fatal', 'critical'])) {
382 $error_fingerprint = $this->generate_error_fingerprint('message', $message, $context);
383
384 // Check if this error has already been sent
385 if ($this->is_error_already_sent($error_fingerprint)) {
386 return; // Skip sending duplicate error
387 }
388 }
389
390 // EMERGENCY MEMORY CHECK - Skip if memory usage is high
391 if (!$this->is_memory_usage_safe(0.5)) {
392 // NEW: Structured error logging with category and code
393 if (class_exists('Metasync_Error_Logger')) {
394 $memory_usage = memory_get_usage(true);
395 $memory_limit = $this->get_cached_memory_limit();
396 $memory_used_mb = round($memory_usage / 1024 / 1024, 2);
397 $memory_limit_mb = round($memory_limit / 1024 / 1024, 2);
398 $memory_percent = round(($memory_usage / $memory_limit) * 100, 1);
399
400 Metasync_Error_Logger::log(
401 Metasync_Error_Logger::CATEGORY_MEMORY_EXHAUSTED,
402 Metasync_Error_Logger::SEVERITY_CRITICAL,
403 'Memory limit exceeded - emergency telemetry shutdown',
404 [
405 'memory_used_mb' => $memory_used_mb,
406 'memory_limit_mb' => $memory_limit_mb,
407 'memory_percent' => $memory_percent,
408 'threshold' => '50%',
409 'operation' => 'send_error',
410 'action' => 'telemetry_disabled',
411 'error_type' => $error_type ?? 'unknown'
412 ]
413 );
414 }
415
416 // error_log('MetaSync Telemetry: EMERGENCY - Disabling due to high memory usage');
417 $this->telemetry_enabled = false; // Disable for this request
418 return;
419 }
420
421 // Send directly to Sentry
422 if ($this->sentry_integration) {
423 $success = $this->sentry_integration->captureMessage($message, $level, $context);
424
425 // Only mark as sent if the telemetry call was successful and it's an error-level message
426 if ($success && $error_fingerprint) {
427 $this->mark_error_as_sent($error_fingerprint);
428 }
429 }
430 }
431
432 /**
433 * Send activation telemetry to Sentry
434 *
435 * @param array $context Additional context
436 */
437 public function send_activation($context = array()) {
438 if (!$this->telemetry_enabled) return;
439
440 if ($this->sentry_integration && $this->telemetry_collector) {
441 $telemetry_data = $this->telemetry_collector->capture_activation($context);
442 $this->sentry_integration->captureMessage('Plugin activated', 'info', $context);
443 }
444 }
445
446 /**
447 * Send deactivation telemetry to Sentry
448 *
449 * @param array $context Additional context
450 */
451 public function send_deactivation($context = array()) {
452 if (!$this->telemetry_enabled) return;
453
454 if ($this->sentry_integration && $this->telemetry_collector) {
455 $telemetry_data = $this->telemetry_collector->capture_deactivation($context);
456 $this->sentry_integration->captureMessage('Plugin deactivated', 'info', $context);
457 }
458 }
459
460 /**
461 * Send performance telemetry to Sentry
462 *
463 * @param string $operation Operation name
464 * @param float $duration Duration in seconds
465 * @param array $context Additional context
466 * @param bool $use_queue Deprecated - kept for backward compatibility
467 */
468 public function send_performance($operation, $duration, $context = array(), $use_queue = true) {
469 if (!$this->telemetry_enabled) return;
470
471 if ($this->sentry_integration && $this->telemetry_collector) {
472 $telemetry_data = $this->telemetry_collector->capture_performance($operation, $duration, $context);
473 $this->sentry_integration->captureMessage("Performance: {$operation}", 'info', array_merge($context, array(
474 'duration' => $duration,
475 'operation' => $operation
476 )));
477 }
478 }
479
480 /**
481 * Test telemetry connection (Sentry)
482 *
483 * @return array Test results
484 */
485 public function test_telemetry_connection() {
486 if (!$this->telemetry_enabled) {
487 return array('success' => false, 'error' => 'Telemetry not enabled or not initialized');
488 }
489
490 return $this->test_sentry_connection();
491 }
492
493 /**
494 * Get telemetry statistics
495 *
496 * @return array Telemetry stats
497 */
498 public function get_telemetry_stats() {
499 if (!$this->telemetry_enabled) {
500 return array('enabled' => false);
501 }
502
503 $stats = array();
504 $stats['enabled'] = true;
505 $stats['php_version'] = PHP_VERSION;
506 $stats['plugin_version'] = defined('METASYNC_VERSION') ? METASYNC_VERSION : '1.0.0';
507 $stats['sentry_enabled'] = function_exists('metasync_sentry_capture_exception');
508 $stats['backend'] = 'sentry';
509
510 return $stats;
511 }
512
513 /**
514 * Test Sentry connection (now tests proxy connection)
515 *
516 * @return array Test results
517 */
518 public function test_sentry_connection() {
519 if (!$this->telemetry_enabled) {
520 return array('success' => false, 'error' => 'Telemetry not enabled');
521 }
522
523 // Test the new proxy connection
524 if ($this->sentry_integration && method_exists($this->sentry_integration, 'testProxyConnection')) {
525 return $this->sentry_integration->testProxyConnection();
526 }
527
528 // Fallback to legacy test if available
529 if (function_exists('metasync_sentry_test_connection')) {
530 return metasync_sentry_test_connection();
531 }
532
533 return array('success' => false, 'error' => 'Sentry WordPress integration not available');
534 }
535
536 /**
537 * Get cached memory limit to avoid repeated parsing
538 *
539 * @return int Memory limit in bytes
540 */
541 private function get_cached_memory_limit() {
542 if (self::$cached_memory_limit === null) {
543 self::$cached_memory_limit = $this->parse_memory_limit(ini_get('memory_limit'));
544 }
545 return self::$cached_memory_limit;
546 }
547
548 /**
549 * Check if memory check should be performed (reduced frequency)
550 *
551 * @return bool True if memory should be checked
552 */
553 private function should_check_memory() {
554 return (++self::$memory_check_counter % 10) === 0;
555 }
556
557 /**
558 * Optimized memory usage check with caching
559 *
560 * @param float $threshold Memory threshold (0.0 to 1.0)
561 * @return bool True if memory usage is below threshold
562 */
563 private function is_memory_usage_safe($threshold = 0.5) {
564 // Only check memory every 10th call to reduce overhead
565 if (!$this->should_check_memory()) {
566 return true; // Assume safe if not checking
567 }
568
569 $memory_usage = memory_get_usage(true);
570 $memory_limit = $this->get_cached_memory_limit();
571
572 return $memory_usage <= ($memory_limit * $threshold);
573 }
574
575 /**
576 * Parse memory limit string to bytes
577 *
578 * @param string $memory_limit Memory limit string (e.g., "256M", "1G")
579 * @return int Memory limit in bytes
580 */
581 private function parse_memory_limit($memory_limit) {
582 if ($memory_limit === '-1' || $memory_limit === -1) {
583 return PHP_INT_MAX; // Unlimited
584 }
585
586 $memory_limit = trim($memory_limit);
587 $last = strtolower($memory_limit[strlen($memory_limit) - 1]);
588 $value = (int) $memory_limit;
589
590 switch ($last) {
591 case 'g':
592 $value *= 1024;
593 case 'm':
594 $value *= 1024;
595 case 'k':
596 $value *= 1024;
597 }
598
599 return $value;
600 }
601
602 /**
603 * Generate a unique fingerprint for an error to detect duplicates
604 *
605 * @param string $error_type Type of error
606 * @param mixed $error_data Error data (exception, message, etc.)
607 * @param array $context Error context
608 * @return string Unique fingerprint
609 */
610 private function generate_error_fingerprint($error_type, $error_data, $context = array()) {
611 // Create a fingerprint based on key error characteristics
612 $fingerprint_data = array(
613 'error_type' => $error_type,
614 'message' => is_object($error_data) ? $error_data->getMessage() : (string)$error_data,
615 'file' => $context['file'] ?? (is_object($error_data) ? $error_data->getFile() : ''),
616 'line' => $context['line'] ?? (is_object($error_data) ? $error_data->getLine() : 0),
617 'exception_class' => is_object($error_data) ? get_class($error_data) : '',
618 'severity' => $context['severity'] ?? 0
619 );
620
621 // Create a hash of the fingerprint data
622 return md5(serialize($fingerprint_data));
623 }
624
625 /**
626 * Check if an error has already been sent
627 *
628 * @param string $error_fingerprint Error fingerprint
629 * @return bool True if already sent
630 */
631 private function is_error_already_sent($error_fingerprint) {
632 return isset($this->sent_errors[$error_fingerprint]);
633 }
634
635 /**
636 * Mark an error as sent to prevent duplicates
637 *
638 * @param string $error_fingerprint Error fingerprint
639 */
640 private function mark_error_as_sent($error_fingerprint) {
641 // Add to sent errors array
642 $this->sent_errors[$error_fingerprint] = time();
643
644 // Clean up old entries to prevent memory bloat
645 $this->cleanup_old_errors();
646 }
647
648 /**
649 * Clean up old error entries to prevent memory bloat
650 */
651 private function cleanup_old_errors() {
652 // If we have too many errors tracked, remove the oldest ones
653 if (count($this->sent_errors) > $this->max_tracked_errors) {
654 // Sort by timestamp (oldest first)
655 asort($this->sent_errors);
656
657 // Remove oldest entries, keeping only the most recent ones
658 $errors_to_remove = count($this->sent_errors) - $this->max_tracked_errors;
659 $this->sent_errors = array_slice($this->sent_errors, $errors_to_remove, null, true);
660 }
661 }
662 }
663