PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.5.23
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.5.23
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.5.23, at telemetry/class-telemetry-manager.php

658 lines 21.6 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 // Generate error fingerprint for deduplication (only for error level messages)
375 $error_fingerprint = null;
376 if (in_array($level, ['error', 'fatal', 'critical'])) {
377 $error_fingerprint = $this->generate_error_fingerprint('message', $message, $context);
378
379 // Check if this error has already been sent
380 if ($this->is_error_already_sent($error_fingerprint)) {
381 return; // Skip sending duplicate error
382 }
383 }
384
385 // EMERGENCY MEMORY CHECK - Skip if memory usage is high
386 if (!$this->is_memory_usage_safe(0.5)) {
387 // NEW: Structured error logging with category and code
388 if (class_exists('Metasync_Error_Logger')) {
389 $memory_usage = memory_get_usage(true);
390 $memory_limit = $this->get_cached_memory_limit();
391 $memory_used_mb = round($memory_usage / 1024 / 1024, 2);
392 $memory_limit_mb = round($memory_limit / 1024 / 1024, 2);
393 $memory_percent = round(($memory_usage / $memory_limit) * 100, 1);
394
395 Metasync_Error_Logger::log(
396 Metasync_Error_Logger::CATEGORY_MEMORY_EXHAUSTED,
397 Metasync_Error_Logger::SEVERITY_CRITICAL,
398 'Memory limit exceeded - emergency telemetry shutdown',
399 [
400 'memory_used_mb' => $memory_used_mb,
401 'memory_limit_mb' => $memory_limit_mb,
402 'memory_percent' => $memory_percent,
403 'threshold' => '50%',
404 'operation' => 'send_error',
405 'action' => 'telemetry_disabled',
406 'error_type' => $error_type ?? 'unknown'
407 ]
408 );
409 }
410
411 // error_log('MetaSync Telemetry: EMERGENCY - Disabling due to high memory usage');
412 $this->telemetry_enabled = false; // Disable for this request
413 return;
414 }
415
416 // Send directly to Sentry
417 if ($this->sentry_integration) {
418 $success = $this->sentry_integration->captureMessage($message, $level, $context);
419
420 // Only mark as sent if the telemetry call was successful and it's an error-level message
421 if ($success && $error_fingerprint) {
422 $this->mark_error_as_sent($error_fingerprint);
423 }
424 }
425 }
426
427 /**
428 * Send activation telemetry to Sentry
429 *
430 * @param array $context Additional context
431 */
432 public function send_activation($context = array()) {
433 if (!$this->telemetry_enabled) return;
434
435 if ($this->sentry_integration && $this->telemetry_collector) {
436 $telemetry_data = $this->telemetry_collector->capture_activation($context);
437 $this->sentry_integration->captureMessage('Plugin activated', 'info', $context);
438 }
439 }
440
441 /**
442 * Send deactivation telemetry to Sentry
443 *
444 * @param array $context Additional context
445 */
446 public function send_deactivation($context = array()) {
447 if (!$this->telemetry_enabled) return;
448
449 if ($this->sentry_integration && $this->telemetry_collector) {
450 $telemetry_data = $this->telemetry_collector->capture_deactivation($context);
451 $this->sentry_integration->captureMessage('Plugin deactivated', 'info', $context);
452 }
453 }
454
455 /**
456 * Send performance telemetry to Sentry
457 *
458 * @param string $operation Operation name
459 * @param float $duration Duration in seconds
460 * @param array $context Additional context
461 * @param bool $use_queue Deprecated - kept for backward compatibility
462 */
463 public function send_performance($operation, $duration, $context = array(), $use_queue = true) {
464 if (!$this->telemetry_enabled) return;
465
466 if ($this->sentry_integration && $this->telemetry_collector) {
467 $telemetry_data = $this->telemetry_collector->capture_performance($operation, $duration, $context);
468 $this->sentry_integration->captureMessage("Performance: {$operation}", 'info', array_merge($context, array(
469 'duration' => $duration,
470 'operation' => $operation
471 )));
472 }
473 }
474
475 /**
476 * Test telemetry connection (Sentry)
477 *
478 * @return array Test results
479 */
480 public function test_telemetry_connection() {
481 if (!$this->telemetry_enabled) {
482 return array('success' => false, 'error' => 'Telemetry not enabled or not initialized');
483 }
484
485 return $this->test_sentry_connection();
486 }
487
488 /**
489 * Get telemetry statistics
490 *
491 * @return array Telemetry stats
492 */
493 public function get_telemetry_stats() {
494 if (!$this->telemetry_enabled) {
495 return array('enabled' => false);
496 }
497
498 $stats = array();
499 $stats['enabled'] = true;
500 $stats['php_version'] = PHP_VERSION;
501 $stats['plugin_version'] = defined('METASYNC_VERSION') ? METASYNC_VERSION : '1.0.0';
502 $stats['sentry_enabled'] = function_exists('metasync_sentry_capture_exception');
503 $stats['backend'] = 'sentry';
504
505 return $stats;
506 }
507
508 /**
509 * Test Sentry connection (now tests proxy connection)
510 *
511 * @return array Test results
512 */
513 public function test_sentry_connection() {
514 if (!$this->telemetry_enabled) {
515 return array('success' => false, 'error' => 'Telemetry not enabled');
516 }
517
518 // Test the new proxy connection
519 if ($this->sentry_integration && method_exists($this->sentry_integration, 'testProxyConnection')) {
520 return $this->sentry_integration->testProxyConnection();
521 }
522
523 // Fallback to legacy test if available
524 if (function_exists('metasync_sentry_test_connection')) {
525 return metasync_sentry_test_connection();
526 }
527
528 return array('success' => false, 'error' => 'Sentry WordPress integration not available');
529 }
530
531 /**
532 * Get cached memory limit to avoid repeated parsing
533 *
534 * @return int Memory limit in bytes
535 */
536 private function get_cached_memory_limit() {
537 if (self::$cached_memory_limit === null) {
538 self::$cached_memory_limit = $this->parse_memory_limit(ini_get('memory_limit'));
539 }
540 return self::$cached_memory_limit;
541 }
542
543 /**
544 * Check if memory check should be performed (reduced frequency)
545 *
546 * @return bool True if memory should be checked
547 */
548 private function should_check_memory() {
549 return (++self::$memory_check_counter % 10) === 0;
550 }
551
552 /**
553 * Optimized memory usage check with caching
554 *
555 * @param float $threshold Memory threshold (0.0 to 1.0)
556 * @return bool True if memory usage is below threshold
557 */
558 private function is_memory_usage_safe($threshold = 0.5) {
559 // Only check memory every 10th call to reduce overhead
560 if (!$this->should_check_memory()) {
561 return true; // Assume safe if not checking
562 }
563
564 $memory_usage = memory_get_usage(true);
565 $memory_limit = $this->get_cached_memory_limit();
566
567 return $memory_usage <= ($memory_limit * $threshold);
568 }
569
570 /**
571 * Parse memory limit string to bytes
572 *
573 * @param string $memory_limit Memory limit string (e.g., "256M", "1G")
574 * @return int Memory limit in bytes
575 */
576 private function parse_memory_limit($memory_limit) {
577 if ($memory_limit === '-1' || $memory_limit === -1) {
578 return PHP_INT_MAX; // Unlimited
579 }
580
581 $memory_limit = trim($memory_limit);
582 $last = strtolower($memory_limit[strlen($memory_limit) - 1]);
583 $value = (int) $memory_limit;
584
585 switch ($last) {
586 case 'g':
587 $value *= 1024;
588 case 'm':
589 $value *= 1024;
590 case 'k':
591 $value *= 1024;
592 }
593
594 return $value;
595 }
596
597 /**
598 * Generate a unique fingerprint for an error to detect duplicates
599 *
600 * @param string $error_type Type of error
601 * @param mixed $error_data Error data (exception, message, etc.)
602 * @param array $context Error context
603 * @return string Unique fingerprint
604 */
605 private function generate_error_fingerprint($error_type, $error_data, $context = array()) {
606 // Create a fingerprint based on key error characteristics
607 $fingerprint_data = array(
608 'error_type' => $error_type,
609 'message' => is_object($error_data) ? $error_data->getMessage() : (string)$error_data,
610 'file' => $context['file'] ?? (is_object($error_data) ? $error_data->getFile() : ''),
611 'line' => $context['line'] ?? (is_object($error_data) ? $error_data->getLine() : 0),
612 'exception_class' => is_object($error_data) ? get_class($error_data) : '',
613 'severity' => $context['severity'] ?? 0
614 );
615
616 // Create a hash of the fingerprint data
617 return md5(serialize($fingerprint_data));
618 }
619
620 /**
621 * Check if an error has already been sent
622 *
623 * @param string $error_fingerprint Error fingerprint
624 * @return bool True if already sent
625 */
626 private function is_error_already_sent($error_fingerprint) {
627 return isset($this->sent_errors[$error_fingerprint]);
628 }
629
630 /**
631 * Mark an error as sent to prevent duplicates
632 *
633 * @param string $error_fingerprint Error fingerprint
634 */
635 private function mark_error_as_sent($error_fingerprint) {
636 // Add to sent errors array
637 $this->sent_errors[$error_fingerprint] = time();
638
639 // Clean up old entries to prevent memory bloat
640 $this->cleanup_old_errors();
641 }
642
643 /**
644 * Clean up old error entries to prevent memory bloat
645 */
646 private function cleanup_old_errors() {
647 // If we have too many errors tracked, remove the oldest ones
648 if (count($this->sent_errors) > $this->max_tracked_errors) {
649 // Sort by timestamp (oldest first)
650 asort($this->sent_errors);
651
652 // Remove oldest entries, keeping only the most recent ones
653 $errors_to_remove = count($this->sent_errors) - $this->max_tracked_errors;
654 $this->sent_errors = array_slice($this->sent_errors, $errors_to_remove, null, true);
655 }
656 }
657 }
658