licenseManager = $licenseManager; $this->versionManager = $versionManager; } /** * Register hooks for Prometheus metrics collection */ public function register(): void { // Only register if metrics are enabled if (!SettingsController::isMetricsEnabled()) { return; } add_action('wp_prometheus_collect_metrics', [$this, 'collectMetrics']); add_action('wp_prometheus_register_dashboards', [$this, 'registerDashboard']); } /** * Register Grafana dashboard with wp-prometheus * * @param object $provider The dashboard provider object */ public function registerDashboard(object $provider): void { $dashboardFile = WC_LICENSED_PRODUCT_PLUGIN_DIR . 'docs/grafana-dashboard.json'; if (!file_exists($dashboardFile)) { return; } $provider->register_dashboard('wc-licensed-product', [ 'title' => __('WC Licensed Product - License Metrics', 'wc-licensed-product'), 'description' => __('Monitor license status, downloads, API usage, and validation errors.', 'wc-licensed-product'), 'icon' => 'dashicons-admin-network', 'file' => $dashboardFile, 'plugin' => 'WC Licensed Product', ]); } /** * Collect and register all metrics * * @param object $collector The Prometheus collector object */ public function collectMetrics(object $collector): void { $this->collectLicenseMetrics($collector); $this->collectDownloadMetrics($collector); $this->collectApiMetrics($collector); } /** * Collect license-related metrics */ private function collectLicenseMetrics(object $collector): void { $stats = $this->licenseManager->getStatistics(); // License count by status (gauge) $licensesByStatus = $collector->register_gauge( 'wclp_licenses_total', 'Total number of licenses by status', ['status'] ); foreach ($stats['by_status'] as $status => $count) { $licensesByStatus->set($count, [$status]); } // Lifetime licenses (gauge) $lifetimeLicenses = $collector->register_gauge( 'wclp_licenses_lifetime_total', 'Total number of lifetime licenses' ); $lifetimeLicenses->set($stats['lifetime']); // Expiring licenses (gauge) $expiringLicenses = $collector->register_gauge( 'wclp_licenses_expiring_total', 'Total number of licenses with expiration date' ); $expiringLicenses->set($stats['expiring']); // Licenses expiring soon - next 30 days (gauge) $expiringSoon = $collector->register_gauge( 'wclp_licenses_expiring_soon', 'Licenses expiring within 30 days' ); $expiringSoon->set($stats['expiring_soon']); } /** * Collect download-related metrics */ private function collectDownloadMetrics(object $collector): void { $stats = $this->versionManager->getDownloadStatistics(); // Total downloads (gauge) $totalDownloads = $collector->register_gauge( 'wclp_downloads_total', 'Total number of file downloads' ); $totalDownloads->set($stats['total']); // Active versions count (gauge) $activeVersions = $collector->register_gauge( 'wclp_versions_active_total', 'Total number of active product versions' ); $activeVersions->set($this->countActiveVersions()); } /** * Collect API-related metrics (counters) */ private function collectApiMetrics(object $collector): void { $counters = $this->getCounters(); // API requests by endpoint and result (counter) $apiRequests = $collector->register_counter( 'wclp_api_requests_total', 'Total API requests by endpoint and result', ['endpoint', 'result'] ); foreach ($counters['api_requests'] ?? [] as $key => $count) { [$endpoint, $result] = explode(':', $key); $apiRequests->incBy($count, [$endpoint, $result]); } // Rate limit exceeded events (counter) $rateLimitExceeded = $collector->register_counter( 'wclp_rate_limit_exceeded_total', 'Total rate limit exceeded events by endpoint', ['endpoint'] ); foreach ($counters['rate_limit'] ?? [] as $endpoint => $count) { $rateLimitExceeded->incBy($count, [$endpoint]); } // Validation errors by type (counter) $validationErrors = $collector->register_counter( 'wclp_validation_errors_total', 'Total validation errors by error type', ['error_type'] ); foreach ($counters['validation_errors'] ?? [] as $errorType => $count) { $validationErrors->incBy($count, [$errorType]); } } /** * Count active product versions */ private function countActiveVersions(): int { global $wpdb; $tableName = \Jeremias\WcLicensedProduct\Installer::getVersionsTable(); return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$tableName} WHERE is_active = 1" ); } /** * Get stored counters */ private function getCounters(): array { $counters = get_option(self::COUNTERS_OPTION, []); return is_array($counters) ? $counters : []; } /** * Increment an API request counter * * @param string $endpoint The API endpoint (validate, status, activate, update-check) * @param string $result The result (success or error) */ public static function incrementApiRequest(string $endpoint, string $result): void { if (!SettingsController::isMetricsEnabled()) { return; } $counters = get_option(self::COUNTERS_OPTION, []); if (!is_array($counters)) { $counters = []; } $key = "{$endpoint}:{$result}"; $counters['api_requests'][$key] = ($counters['api_requests'][$key] ?? 0) + 1; update_option(self::COUNTERS_OPTION, $counters, false); } /** * Increment rate limit exceeded counter * * @param string $endpoint The API endpoint */ public static function incrementRateLimitExceeded(string $endpoint): void { if (!SettingsController::isMetricsEnabled()) { return; } $counters = get_option(self::COUNTERS_OPTION, []); if (!is_array($counters)) { $counters = []; } $counters['rate_limit'][$endpoint] = ($counters['rate_limit'][$endpoint] ?? 0) + 1; update_option(self::COUNTERS_OPTION, $counters, false); } /** * Increment validation error counter * * @param string $errorType The error type (license_not_found, domain_mismatch, etc.) */ public static function incrementValidationError(string $errorType): void { if (!SettingsController::isMetricsEnabled()) { return; } $counters = get_option(self::COUNTERS_OPTION, []); if (!is_array($counters)) { $counters = []; } $counters['validation_errors'][$errorType] = ($counters['validation_errors'][$errorType] ?? 0) + 1; update_option(self::COUNTERS_OPTION, $counters, false); } /** * Reset all counters (useful for testing or maintenance) */ public static function resetCounters(): void { delete_option(self::COUNTERS_OPTION); } }