PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.17
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.17
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-sentry-telemetry.php

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

426 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Sentry-Compatible Telemetry Collector
4 *
5 * Collects and formats telemetry data in Sentry-compatible format
6 * Compatible with PHP 7.1+
7 *
8 * @package Search Engine Labs SEO
9 * @subpackage Telemetry
10 * @since 1.0.0
11 */
12
13 // If this file is called directly, abort.
14 if (!defined('WPINC')) {
15 die;
16 }
17
18 /**
19 * Sentry Telemetry Collector Class
20 *
21 * Provides Sentry-compatible telemetry data collection and formatting
22 */
23 class Metasync_Sentry_Telemetry {
24
25 /**
26 * Plugin version
27 * @var string
28 */
29 private $plugin_version;
30
31 /**
32 * Environment (production, staging, development)
33 * @var string
34 */
35 private $environment;
36
37 /**
38 * Release identifier
39 * @var string
40 */
41 private $release;
42
43 /**
44 * Constructor
45 */
46 public function __construct() {
47 $this->plugin_version = defined('METASYNC_VERSION') ? METASYNC_VERSION : '1.0.0';
48 $this->environment = $this->detect_environment();
49 $this->release = $this->plugin_version;
50 }
51
52 /**
53 * Detect the current environment
54 *
55 * @return string
56 */
57 private function detect_environment() {
58 if (defined('WP_DEBUG') && WP_DEBUG) {
59 return 'development';
60 }
61
62 $host = parse_url(home_url(), PHP_URL_HOST);
63 if ($host && (strpos($host, 'staging') !== false || strpos($host, 'dev') !== false)) {
64 return 'staging';
65 }
66
67 return 'production';
68 }
69
70 /**
71 * Capture an exception/error
72 *
73 * @param Exception|Error|string $exception The exception/error to capture
74 * @param array $extra Additional context data
75 * @return array Sentry-compatible error data
76 */
77 public function capture_exception($exception, $extra = array()) {
78 $error_data = array();
79
80 if (is_object($exception)) {
81 $error_data = array(
82 'exception' => array(
83 'values' => array(
84 array(
85 'type' => get_class($exception),
86 'value' => $exception->getMessage(),
87 'stacktrace' => $this->format_stacktrace($exception->getTrace()),
88 'module' => $this->get_module_from_file($exception->getFile())
89 )
90 )
91 )
92 );
93 } else {
94 // Handle string errors
95 $error_data = array(
96 'message' => array(
97 'message' => is_string($exception) ? $exception : 'Unknown error',
98 'formatted' => is_string($exception) ? $exception : 'Unknown error'
99 )
100 );
101 }
102
103 return $this->create_telemetry_payload('error', $error_data, $extra);
104 }
105
106 /**
107 * Capture a message/event
108 *
109 * @param string $message The message to capture
110 * @param string $level The log level (error, warning, info, debug)
111 * @param array $extra Additional context data
112 * @return array Sentry-compatible message data
113 */
114 public function capture_message($message, $level = 'info', $extra = array()) {
115 $message_data = array(
116 'message' => array(
117 'message' => $message,
118 'formatted' => $message
119 )
120 );
121
122 return $this->create_telemetry_payload($level, $message_data, $extra);
123 }
124
125 /**
126 * Capture plugin activation event
127 *
128 * @param array $plugin_data Plugin information
129 * @return array Telemetry data
130 */
131 public function capture_activation($plugin_data = array()) {
132 $context = array_merge($plugin_data, array(
133 'event_type' => 'plugin_activation',
134 'wp_version' => get_bloginfo('version'),
135 'php_version' => PHP_VERSION,
136 'plugins_count' => count(get_option('active_plugins', array())),
137 'theme' => get_template()
138 ));
139
140 return $this->capture_message('Plugin activated', 'info', $context);
141 }
142
143 /**
144 * Capture plugin deactivation event
145 *
146 * @param array $context Additional context
147 * @return array Telemetry data
148 */
149 public function capture_deactivation($context = array()) {
150 $context = array_merge($context, array(
151 'event_type' => 'plugin_deactivation'
152 ));
153
154 return $this->capture_message('Plugin deactivated', 'info', $context);
155 }
156
157 /**
158 * Capture performance metrics
159 *
160 * @param string $operation Operation name
161 * @param float $duration Duration in seconds
162 * @param array $context Additional context
163 * @return array Telemetry data
164 */
165 public function capture_performance($operation, $duration, $context = array()) {
166 $performance_data = array_merge($context, array(
167 'event_type' => 'performance',
168 'operation' => $operation,
169 'duration' => $duration,
170 'memory_usage' => memory_get_usage(true),
171 'memory_peak' => memory_get_peak_usage(true)
172 ));
173
174 return $this->capture_message("Performance: {$operation}", 'info', $performance_data);
175 }
176
177 /**
178 * Create base telemetry payload
179 *
180 * @param string $level Log level
181 * @param array $data Event-specific data
182 * @param array $extra Additional context
183 * @return array Complete telemetry payload
184 */
185 private function create_telemetry_payload($level, $data, $extra = array()) {
186 $payload = array(
187 'event_id' => $this->generate_event_id(),
188 'timestamp' => gmdate('c'),
189 'level' => $level,
190 'platform' => 'php',
191 'sdk' => array(
192 'name' => 'metasync-telemetry',
193 'version' => $this->plugin_version
194 ),
195 'server_name' => parse_url(home_url(), PHP_URL_HOST),
196 'release' => $this->release,
197 'environment' => $this->environment,
198 'contexts' => array(
199 'runtime' => array(
200 'name' => 'php',
201 'version' => PHP_VERSION
202 ),
203 'os' => array(
204 'name' => PHP_OS_FAMILY,
205 'version' => php_uname('r')
206 ),
207 'app' => array(
208 'app_name' => 'Search Engine Labs SEO',
209 'app_version' => $this->plugin_version,
210 'app_identifier' => 'metasync'
211 )
212 ),
213 'tags' => array(
214 'wp_version' => get_bloginfo('version'),
215 'php_version' => PHP_VERSION,
216 'environment' => $this->environment,
217 'plugin_version' => $this->plugin_version
218 ),
219 'user' => array(
220 'id' => $this->get_user_hash(),
221 'ip_address' => $this->get_client_ip()
222 ),
223 'extra' => array_merge($this->get_system_context(), $extra)
224 );
225
226 return array_merge($payload, $data);
227 }
228
229 /**
230 * Generate a unique event ID
231 *
232 * @return string 32-character hex event ID
233 */
234 private function generate_event_id() {
235 return str_replace('-', '', wp_generate_uuid4());
236 }
237
238 /**
239 * Format stacktrace for Sentry compatibility
240 *
241 * @param array $trace PHP stack trace
242 * @return array Formatted stacktrace
243 */
244 private function format_stacktrace($trace) {
245 $frames = array();
246
247 foreach ($trace as $frame) {
248 $frames[] = array(
249 'filename' => isset($frame['file']) ? $frame['file'] : '<unknown>',
250 'lineno' => isset($frame['line']) ? $frame['line'] : 0,
251 'function' => isset($frame['function']) ? $frame['function'] : '<unknown>',
252 'module' => isset($frame['class']) ? $frame['class'] : null,
253 'in_app' => $this->is_in_app($frame),
254 'context_line' => $this->get_source_line($frame)
255 );
256 }
257
258 return array('frames' => array_reverse($frames));
259 }
260
261 /**
262 * Check if a stack frame is in application code
263 *
264 * @param array $frame Stack frame
265 * @return bool True if in application code
266 */
267 private function is_in_app($frame) {
268 if (!isset($frame['file'])) {
269 return false;
270 }
271
272 $wp_content_dir = defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : ABSPATH . 'wp-content';
273 return strpos($frame['file'], $wp_content_dir) !== false;
274 }
275
276 /**
277 * Get source line from a stack frame
278 *
279 * @param array $frame Stack frame
280 * @return string|null Source line or null
281 */
282 private function get_source_line($frame) {
283 if (!isset($frame['file']) || !isset($frame['line']) || !is_readable($frame['file'])) {
284 return null;
285 }
286
287 $lines = file($frame['file']);
288 $line_index = $frame['line'] - 1;
289
290 return isset($lines[$line_index]) ? rtrim($lines[$line_index]) : null;
291 }
292
293 /**
294 * Get module name from file path
295 *
296 * @param string $file File path
297 * @return string Module name
298 */
299 private function get_module_from_file($file) {
300 if (strpos($file, plugin_dir_path(__FILE__)) === 0) {
301 return 'metasync';
302 }
303
304 return basename(dirname($file));
305 }
306
307 /**
308 * Get anonymous user hash
309 *
310 * @return string Hashed user identifier
311 */
312 private function get_user_hash() {
313 $site_url = home_url();
314 return substr(md5($site_url), 0, 16);
315 }
316
317 /**
318 * Get client IP address
319 *
320 * @return string Client IP address
321 */
322 private function get_client_ip() {
323 // Return anonymized IP for privacy
324 return '0.0.0.0';
325 }
326
327 /**
328 * Get system context information
329 *
330 * @return array System context
331 */
332 private function get_system_context() {
333 return array(
334 'wordpress_version' => get_bloginfo('version'),
335 'php_version' => PHP_VERSION,
336 'mysql_version' => $this->get_mysql_version(),
337 'server_software' => isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : 'unknown',
338 'active_plugins' => count(get_option('active_plugins', array())),
339 'active_theme' => get_template(),
340 'multisite' => is_multisite(),
341 'memory_limit' => ini_get('memory_limit'),
342 'max_execution_time' => ini_get('max_execution_time')
343 );
344 }
345
346 /**
347 * Get MySQL version
348 *
349 * @return string MySQL version
350 */
351 private function get_mysql_version() {
352 global $wpdb;
353 return $wpdb->get_var('SELECT VERSION()');
354 }
355
356 /**
357 * Capture WordPress hook execution
358 *
359 * @param string $hook Hook name
360 * @param array $args Hook arguments
361 * @param float $execution_time Execution time
362 * @return array Telemetry data
363 */
364 public function capture_hook_execution($hook, $args, $execution_time) {
365 $context = array(
366 'event_type' => 'hook_execution',
367 'hook_name' => $hook,
368 'args_count' => count($args),
369 'execution_time' => $execution_time
370 );
371
372 return $this->capture_message("Hook executed: {$hook}", 'debug', $context);
373 }
374
375 /**
376 * Capture database query performance
377 *
378 * @param string $query SQL query
379 * @param float $execution_time Execution time
380 * @param array $context Additional context
381 * @return array Telemetry data
382 */
383 public function capture_db_query($query, $execution_time, $context = array()) {
384 $query_context = array_merge($context, array(
385 'event_type' => 'database_query',
386 'query' => $this->sanitize_query($query),
387 'execution_time' => $execution_time,
388 'query_type' => $this->get_query_type($query)
389 ));
390
391 return $this->capture_message("Database query executed", 'debug', $query_context);
392 }
393
394 /**
395 * Sanitize SQL query for logging
396 *
397 * @param string $query SQL query
398 * @return string Sanitized query
399 */
400 private function sanitize_query($query) {
401 // Remove sensitive data and truncate long queries
402 $query = preg_replace('/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/', 'XXXX-XXXX-XXXX-XXXX', $query);
403 $query = preg_replace('/\b[\w\.-]+@[\w\.-]+\.\w+\b/', '[email]', $query);
404
405 if (strlen($query) > 500) {
406 $query = substr($query, 0, 497) . '...';
407 }
408
409 return $query;
410 }
411
412 /**
413 * Get query type from SQL
414 *
415 * @param string $query SQL query
416 * @return string Query type
417 */
418 private function get_query_type($query) {
419 $query = strtoupper(trim($query));
420 if (preg_match('/^(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP)\b/', $query, $matches)) {
421 return strtolower($matches[1]);
422 }
423 return 'unknown';
424 }
425 }
426