From 3eb66b0ebeb72fc68063fc5fe0cee2679edf191a Mon Sep 17 00:00:00 2001 From: magdev Date: Mon, 2 Feb 2026 14:41:09 +0100 Subject: [PATCH] feat: Add WooCommerce, cron, and transient metrics (v0.2.0) - WooCommerce integration metrics (products, orders, revenue, customers) - Cron job metrics (events by hook, overdue count, next run timestamp) - Transient cache metrics (total, expiring, expired) - Support for WooCommerce HPOS storage - Updated settings page with new metric categories - Updated translations and documentation Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 22 ++ CLAUDE.md | 33 ++- PLAN.md | 6 - README.md | 27 +++ languages/wp-prometheus-de_CH.mo | Bin 4260 -> 7822 bytes languages/wp-prometheus-de_CH.po | 363 +++++++++++++++++++++-------- languages/wp-prometheus.pot | 357 +++++++++++++++++++++-------- src/Admin/Settings.php | 66 ++++++ src/Metrics/Collector.php | 381 +++++++++++++++++++++++++++++++ wp-prometheus.php | 4 +- 10 files changed, 1050 insertions(+), 209 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acdad6f..196a0a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2026-02-02 + +### Added + +- WooCommerce integration metrics (when WooCommerce is active): + - `wordpress_woocommerce_products_total` - Products by status and type + - `wordpress_woocommerce_orders_total` - Orders by status + - `wordpress_woocommerce_revenue_total` - Revenue (all time, today, month) + - `wordpress_woocommerce_customers_total` - Customers (registered, guest) +- Cron job metrics: + - `wordpress_cron_events_total` - Scheduled cron events by hook + - `wordpress_cron_overdue_total` - Number of overdue cron events + - `wordpress_cron_next_run_timestamp` - Unix timestamp of next scheduled cron +- Transient cache metrics: + - `wordpress_transients_total` - Transients by type (total, with_expiration, persistent, expired) +- WooCommerce metrics section in settings (only visible when WooCommerce is active) +- Support for WooCommerce HPOS (High-Performance Order Storage) + +### Changed + +- Updated Help tab with new metrics reference + ## [0.1.1] - 2026-02-02 ### Changed diff --git a/CLAUDE.md b/CLAUDE.md index e5e9d2e..cf35e28 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,9 @@ This plugin provides a Prometheus `/metrics` endpoint and an extensible way to a - Prometheus compatible authenticated `/metrics` endpoint - Optional default metrics (users, posts, comments, plugins) - Runtime metrics (HTTP requests, request duration, database queries) +- Cron job metrics (scheduled events, overdue, next run) +- Transient cache metrics (total, expiring, expired) +- WooCommerce integration (products, orders, revenue, customers) - Dedicated plugin settings under 'Settings/Metrics' menu - Extensible by other plugins using `wp_prometheus_collect_metrics` action hook - License management integration @@ -27,11 +30,11 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w **Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file. -### Version 0.2.0 (Planned) +### Version 0.3.0 (Planned) -- WooCommerce integration metrics -- Cron job metrics -- Transient cache metrics +- Custom metric builder in admin +- Metric export/import +- Grafana dashboard templates ## Technical Stack @@ -282,6 +285,28 @@ add_action( 'wp_prometheus_collect_metrics', function( $collector ) { ## Session History +### 2026-02-02 - Extended Metrics (v0.2.0) + +- Added WooCommerce integration metrics (only when WooCommerce is active): + - `wordpress_woocommerce_products_total` - Products by status and type + - `wordpress_woocommerce_orders_total` - Orders by status + - `wordpress_woocommerce_revenue_total` - Revenue (all time, today, month) + - `wordpress_woocommerce_customers_total` - Customers (registered, guest) +- Added cron job metrics: + - `wordpress_cron_events_total` - Scheduled cron events by hook (top 20) + - `wordpress_cron_overdue_total` - Number of overdue cron events + - `wordpress_cron_next_run_timestamp` - Unix timestamp of next scheduled cron +- Added transient cache metrics: + - `wordpress_transients_total` - Transients by type (total, with_expiration, persistent, expired) +- Updated Settings page with new metric categories +- Updated Help tab with new metrics reference +- **Key Learning**: WooCommerce HPOS (High-Performance Order Storage) requires different queries + - Check `OrderUtil::custom_orders_table_usage_is_enabled()` to determine storage type + - HPOS uses `wc_orders` table instead of `posts` and `postmeta` +- **Key Learning**: Cron event labeling requires cardinality control + - Limit to top 20 hooks to prevent label explosion + - Use `arsort()` to get most frequent hooks first + ### 2026-02-02 - Runtime Metrics (v0.1.0) - Implemented runtime metrics collection for HTTP requests and database queries diff --git a/PLAN.md b/PLAN.md index 1a5a796..54e8b89 100644 --- a/PLAN.md +++ b/PLAN.md @@ -161,12 +161,6 @@ https://example.com/metrics/?token=your-auth-token ## Future Enhancements -### Version 0.2.0 - -- WooCommerce integration metrics -- Cron job metrics -- Transient cache metrics - ### Version 0.3.0 - Custom metric builder in admin diff --git a/README.md b/README.md index b01f6e6..b2a3b62 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,33 @@ scrape_configs: **Note:** Runtime metrics aggregate data across requests. Enable only the metrics you need to minimize performance impact. +### Cron Metrics (v0.2.0+) + +| Metric | Type | Labels | Description | +|--------|------|--------|-------------| +| wordpress_cron_events_total | Gauge | hook | Scheduled cron events by hook | +| wordpress_cron_overdue_total | Gauge | - | Number of overdue cron events | +| wordpress_cron_next_run_timestamp | Gauge | - | Unix timestamp of next scheduled cron | + +### Transient Metrics (v0.2.0+) + +| Metric | Type | Labels | Description | +|--------|------|--------|-------------| +| wordpress_transients_total | Gauge | type | Transients by type (total, with_expiration, persistent, expired) | + +### WooCommerce Metrics (v0.2.0+) + +These metrics are only available when WooCommerce is active. + +| Metric | Type | Labels | Description | +|--------|------|--------|-------------| +| wordpress_woocommerce_products_total | Gauge | status, type | Products by status and type | +| wordpress_woocommerce_orders_total | Gauge | status | Orders by status | +| wordpress_woocommerce_revenue_total | Gauge | period, currency | Revenue (all_time, today, month) | +| wordpress_woocommerce_customers_total | Gauge | type | Customers (registered, guest) | + +**Note:** WooCommerce metrics support both legacy post-based orders and HPOS (High-Performance Order Storage). + ## Extending with Custom Metrics Add your own metrics using the `wp_prometheus_collect_metrics` action: diff --git a/languages/wp-prometheus-de_CH.mo b/languages/wp-prometheus-de_CH.mo index 4f1dcca9582c5b54c7974873598b98cdc9bf6c08..beb6cbcf828cd806f7378a4234bd503dab46521e 100644 GIT binary patch literal 7822 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 delta 1343 zcmZY8OK4PA9LMo9zA|GHHD)rIQDbk+%f<#Dv0cO{_{buV=_V*iYbDprNi!y$By(?6 z5E`VeM0{|Nh%Q92(nTL?pdh*td|{WRixiYJDo7MsT-2^gzrRVW3;%HL=bk%v&iSAJ zIrrq&E7fC9mM=JKh$h-9+Odc+Tk+LAF2vM)W0v6`_#wW=#kgvLF-x!koADE@#RIq$ zPtAD|i|PM_)p!dx;$w^(GiH`9G^UCNwWt?&;Bx#FHL;H=%p-p=C$I)Dp%T6~_k0@5 z>AyiuRJ_QTN^HU+Y(tHgMvb!%lgw`dZnUBkSb^i%fIna<{)SO}ib;HhAK*epU5$09 ziFaWOcA+M67~&P|z!ZaQz&`B4arK$s{Kd_;l)XA;Od0(LWTTQ!q7o~mEP9^AE!c?K zioUt$r%?%9MkV|+DzUrxF+N2lo+_E$=1r&sdvHtxk8q=se1m%N947EOuEB?>EqaNX zD8?+RR29}?Gpgj>$Xd)1)c=p6-n)Pr_d4p#JVGTnSxWtPa`O)llu;|ca%vd#%Z7#6h+qX@p9kjincc?$x+wW!O=KgASZZ912?BU^|z`CK5 zl>5G9aN_UM^AUG%*?M=htTwW7;%eEmh`U(c>PF(_?z4Dhb7$!1GJZbOR7iio=FIki zFncg-^P!zxf}VBwcC;__{QZShJdW>iCo9r!x}wJ2Pb_i&R?N6R6E!7mEMPP@palf( oOl70HlZZ!yVN0eq@2({iW$(@AUL?1<_LM(yW5vyg`>|^Mf2M=Sy#N3J diff --git a/languages/wp-prometheus-de_CH.po b/languages/wp-prometheus-de_CH.po index 48f9c60..08483cf 100644 --- a/languages/wp-prometheus-de_CH.po +++ b/languages/wp-prometheus-de_CH.po @@ -3,7 +3,7 @@ # This file is distributed under the GPL v2 or later. msgid "" msgstr "" -"Project-Id-Version: WP Prometheus 0.1.0\n" +"Project-Id-Version: WP Prometheus 0.2.0\n" "Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wp-prometheus/issues\n" "POT-Creation-Date: 2026-02-02T00:00:00+00:00\n" "PO-Revision-Date: 2026-02-02T00:00:00+00:00\n" @@ -15,184 +15,347 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/Admin/Settings.php:40 +#: src/Admin/Settings.php msgid "Metrics Settings" msgstr "Metriken-Einstellungen" -#: src/Admin/Settings.php:41 +#: src/Admin/Settings.php msgid "Metrics" msgstr "Metriken" -#: src/Admin/Settings.php:58 -msgid "License Settings" -msgstr "Lizenz-Einstellungen" +#: src/Admin/Settings.php +msgid "License" +msgstr "Lizenz" -#: src/Admin/Settings.php:65 -msgid "Authentication" -msgstr "Authentifizierung" +#: src/Admin/Settings.php +msgid "Help" +msgstr "Hilfe" -#: src/Admin/Settings.php:73 -msgid "Default Metrics" -msgstr "Standard-Metriken" - -#: src/Admin/Settings.php:150 +#: src/Admin/Settings.php msgid "License settings saved." msgstr "Lizenz-Einstellungen gespeichert." -#: src/Admin/Settings.php:195 +#: src/Admin/Settings.php msgid "License is active and valid." msgstr "Lizenz ist aktiv und gueltig." -#: src/Admin/Settings.php:196 +#: src/Admin/Settings.php msgid "License is invalid." msgstr "Lizenz ist ungueltig." -#: src/Admin/Settings.php:197 +#: src/Admin/Settings.php msgid "License has expired." msgstr "Lizenz ist abgelaufen." -#: src/Admin/Settings.php:198 +#: src/Admin/Settings.php msgid "License has been revoked." msgstr "Lizenz wurde widerrufen." -#: src/Admin/Settings.php:199 +#: src/Admin/Settings.php msgid "License is inactive." msgstr "Lizenz ist inaktiv." -#: src/Admin/Settings.php:200 +#: src/Admin/Settings.php msgid "License has not been validated yet." msgstr "Lizenz wurde noch nicht validiert." -#: src/Admin/Settings.php:201 +#: src/Admin/Settings.php msgid "License server is not configured." msgstr "Lizenz-Server ist nicht konfiguriert." +#: src/Admin/Settings.php +msgid "Unknown status." +msgstr "Unbekannter Status." + #. translators: %s: Expiration date -#: src/Admin/Settings.php:214 +#: src/Admin/Settings.php msgid "Expires: %s" msgstr "Laeuft ab: %s" #. translators: %s: Time ago -#: src/Admin/Settings.php:225 +#: src/Admin/Settings.php msgid "Last checked: %s ago" msgstr "Zuletzt geprueft: vor %s" -#: src/Admin/Settings.php:239 +#: src/Admin/Settings.php msgid "License Server URL" msgstr "Lizenz-Server URL" -#: src/Admin/Settings.php:249 +#: src/Admin/Settings.php msgid "License Key" msgstr "Lizenzschluessel" -#: src/Admin/Settings.php:259 +#: src/Admin/Settings.php msgid "Server Secret" msgstr "Server-Geheimnis" -#: src/Admin/Settings.php:264 +#: src/Admin/Settings.php msgid "Leave empty to keep existing." msgstr "Leer lassen, um bestehenden Wert zu behalten." -#: src/Admin/Settings.php:270 +#: src/Admin/Settings.php msgid "Save License Settings" msgstr "Lizenz-Einstellungen speichern" -#: src/Admin/Settings.php:272 +#: src/Admin/Settings.php msgid "Validate License" msgstr "Lizenz validieren" -#: src/Admin/Settings.php:275 +#: src/Admin/Settings.php msgid "Activate License" msgstr "Lizenz aktivieren" -#: src/Admin/Settings.php:301 -msgid "Configure authentication for the /metrics endpoint." -msgstr "Authentifizierung fuer den /metrics-Endpunkt konfigurieren." +#: src/Admin/Settings.php +msgid "Authentication" +msgstr "Authentifizierung" -#: src/Admin/Settings.php:310 -msgid "Select which default metrics to expose." -msgstr "Waehlen Sie, welche Standard-Metriken bereitgestellt werden sollen." - -#: src/Admin/Settings.php:324 -msgid "Regenerate" -msgstr "Neu generieren" - -#: src/Admin/Settings.php:327 -msgid "Use this token to authenticate Prometheus scrape requests." -msgstr "Verwenden Sie diesen Token zur Authentifizierung von Prometheus-Abfragen." - -#: src/Admin/Settings.php:340 -msgid "WordPress Info (version, PHP version, multisite)" -msgstr "WordPress-Info (Version, PHP-Version, Multisite)" - -#: src/Admin/Settings.php:341 -msgid "Total Users by Role" -msgstr "Benutzer nach Rolle" - -#: src/Admin/Settings.php:342 -msgid "Total Posts by Type and Status" -msgstr "Beitraege nach Typ und Status" - -#: src/Admin/Settings.php:343 -msgid "Total Comments by Status" -msgstr "Kommentare nach Status" - -#: src/Admin/Settings.php:344 -msgid "Total Plugins (active/inactive)" -msgstr "Plugins (aktiv/inaktiv)" - -#: src/Admin/Settings.php:345 -msgid "HTTP Requests Total (by method, status, endpoint)" -msgstr "HTTP-Anfragen gesamt (nach Methode, Status, Endpunkt)" - -#: src/Admin/Settings.php:346 -msgid "HTTP Request Duration (histogram)" -msgstr "HTTP-Anfragedauer (Histogramm)" - -#: src/Admin/Settings.php:347 -msgid "Database Queries Total (by endpoint)" -msgstr "Datenbank-Abfragen gesamt (nach Endpunkt)" - -#: src/Admin/Settings.php:369 -msgid "Prometheus Configuration" -msgstr "Prometheus-Konfiguration" - -#: src/Admin/Settings.php:370 -msgid "Add the following to your prometheus.yml:" -msgstr "Fuegen Sie Folgendes zu Ihrer prometheus.yml hinzu:" - -#. translators: %s: Endpoint URL -#: src/Admin/Settings.php:385 -msgid "Metrics endpoint: %s" -msgstr "Metriken-Endpunkt: %s" - -#: src/Admin/Settings.php:93 -msgid "Auth Token" -msgstr "Auth-Token" - -#: src/Admin/Settings.php:101 +#: src/Admin/Settings.php msgid "Enabled Metrics" msgstr "Aktivierte Metriken" -#: src/Plugin.php:120 +#: src/Admin/Settings.php +msgid "Configure authentication for the /metrics endpoint." +msgstr "Authentifizierung fuer den /metrics-Endpunkt konfigurieren." + +#: src/Admin/Settings.php +msgid "Select which metrics to expose on the /metrics endpoint." +msgstr "Waehlen Sie, welche Metriken auf dem /metrics-Endpunkt bereitgestellt werden sollen." + +#: src/Admin/Settings.php +msgid "Auth Token" +msgstr "Auth-Token" + +#: src/Admin/Settings.php +msgid "Select Metrics" +msgstr "Metriken auswaehlen" + +#: src/Admin/Settings.php +msgid "Regenerate" +msgstr "Neu generieren" + +#: src/Admin/Settings.php +msgid "Use this token to authenticate Prometheus scrape requests." +msgstr "Verwenden Sie diesen Token zur Authentifizierung von Prometheus-Abfragen." + +#: src/Admin/Settings.php +msgid "Static Metrics" +msgstr "Statische Metriken" + +#: src/Admin/Settings.php +msgid "WordPress Info (version, PHP version, multisite)" +msgstr "WordPress-Info (Version, PHP-Version, Multisite)" + +#: src/Admin/Settings.php +msgid "Total Users by Role" +msgstr "Benutzer nach Rolle" + +#: src/Admin/Settings.php +msgid "Total Posts by Type and Status" +msgstr "Beitraege nach Typ und Status" + +#: src/Admin/Settings.php +msgid "Total Comments by Status" +msgstr "Kommentare nach Status" + +#: src/Admin/Settings.php +msgid "Total Plugins (active/inactive)" +msgstr "Plugins (aktiv/inaktiv)" + +#: src/Admin/Settings.php +msgid "Cron Events (scheduled tasks, overdue, next run)" +msgstr "Cron-Ereignisse (geplante Aufgaben, ueberfaellig, naechste Ausfuehrung)" + +#: src/Admin/Settings.php +msgid "Transients (total, expiring, expired)" +msgstr "Transienten (gesamt, ablaufend, abgelaufen)" + +#: src/Admin/Settings.php +msgid "Runtime Metrics" +msgstr "Laufzeit-Metriken" + +#: src/Admin/Settings.php +msgid "Runtime metrics track data across requests. Enable only what you need to minimize performance impact." +msgstr "Laufzeit-Metriken erfassen Daten ueber Anfragen hinweg. Aktivieren Sie nur, was Sie benoetigen, um Auswirkungen auf die Leistung zu minimieren." + +#: src/Admin/Settings.php +msgid "HTTP Requests Total (by method, status, endpoint)" +msgstr "HTTP-Anfragen gesamt (nach Methode, Status, Endpunkt)" + +#: src/Admin/Settings.php +msgid "HTTP Request Duration (histogram)" +msgstr "HTTP-Anfragedauer (Histogramm)" + +#: src/Admin/Settings.php +msgid "Database Queries Total (by endpoint)" +msgstr "Datenbank-Abfragen gesamt (nach Endpunkt)" + +#: src/Admin/Settings.php +msgid "WooCommerce Metrics" +msgstr "WooCommerce-Metriken" + +#: src/Admin/Settings.php +msgid "Metrics specific to WooCommerce stores. Only available when WooCommerce is active." +msgstr "Metriken speziell fuer WooCommerce-Shops. Nur verfuegbar, wenn WooCommerce aktiv ist." + +#: src/Admin/Settings.php +msgid "WooCommerce Products (by status and type)" +msgstr "WooCommerce-Produkte (nach Status und Typ)" + +#: src/Admin/Settings.php +msgid "WooCommerce Orders (by status)" +msgstr "WooCommerce-Bestellungen (nach Status)" + +#: src/Admin/Settings.php +msgid "WooCommerce Revenue (all time, today, month)" +msgstr "WooCommerce-Umsatz (gesamt, heute, Monat)" + +#: src/Admin/Settings.php +msgid "WooCommerce Customers (registered, guest)" +msgstr "WooCommerce-Kunden (registriert, Gast)" + +#: src/Admin/Settings.php +msgid "Prometheus Configuration" +msgstr "Prometheus-Konfiguration" + +#: src/Admin/Settings.php +msgid "Add the following to your prometheus.yml:" +msgstr "Fuegen Sie Folgendes zu Ihrer prometheus.yml hinzu:" + +#: src/Admin/Settings.php +msgid "Endpoint Information" +msgstr "Endpunkt-Informationen" + +#: src/Admin/Settings.php +msgid "Metrics URL" +msgstr "Metriken-URL" + +#: src/Admin/Settings.php +msgid "Testing the Endpoint" +msgstr "Endpunkt testen" + +#: src/Admin/Settings.php +msgid "You can test the endpoint using curl:" +msgstr "Sie koennen den Endpunkt mit curl testen:" + +#: src/Admin/Settings.php +msgid "Available Metrics" +msgstr "Verfuegbare Metriken" + +#: src/Admin/Settings.php +msgid "Metric" +msgstr "Metrik" + +#: src/Admin/Settings.php +msgid "Type" +msgstr "Typ" + +#: src/Admin/Settings.php +msgid "Description" +msgstr "Beschreibung" + +#: src/Admin/Settings.php +msgid "Gauge" +msgstr "Gauge" + +#: src/Admin/Settings.php +msgid "Counter" +msgstr "Counter" + +#: src/Admin/Settings.php +msgid "Histogram" +msgstr "Histogramm" + +#: src/Admin/Settings.php +msgid "WordPress installation info" +msgstr "WordPress-Installationsinformationen" + +#: src/Admin/Settings.php +msgid "Total users by role" +msgstr "Benutzer gesamt nach Rolle" + +#: src/Admin/Settings.php +msgid "Total posts by type and status" +msgstr "Beitraege gesamt nach Typ und Status" + +#: src/Admin/Settings.php +msgid "Total comments by status" +msgstr "Kommentare gesamt nach Status" + +#: src/Admin/Settings.php +msgid "Total plugins by status" +msgstr "Plugins gesamt nach Status" + +#: src/Admin/Settings.php +msgid "HTTP requests by method, status, endpoint" +msgstr "HTTP-Anfragen nach Methode, Status, Endpunkt" + +#: src/Admin/Settings.php +msgid "HTTP request duration distribution" +msgstr "HTTP-Anfragedauer-Verteilung" + +#: src/Admin/Settings.php +msgid "Database queries by endpoint" +msgstr "Datenbank-Abfragen nach Endpunkt" + +#: src/Admin/Settings.php +msgid "Scheduled cron events by hook" +msgstr "Geplante Cron-Ereignisse nach Hook" + +#: src/Admin/Settings.php +msgid "Number of overdue cron events" +msgstr "Anzahl ueberfaelliger Cron-Ereignisse" + +#: src/Admin/Settings.php +msgid "Unix timestamp of next scheduled cron" +msgstr "Unix-Zeitstempel des naechsten geplanten Crons" + +#: src/Admin/Settings.php +msgid "Total transients by type" +msgstr "Transienten gesamt nach Typ" + +#: src/Admin/Settings.php +msgid "WooCommerce products by status and type" +msgstr "WooCommerce-Produkte nach Status und Typ" + +#: src/Admin/Settings.php +msgid "WooCommerce orders by status" +msgstr "WooCommerce-Bestellungen nach Status" + +#: src/Admin/Settings.php +msgid "WooCommerce revenue by period" +msgstr "WooCommerce-Umsatz nach Zeitraum" + +#: src/Admin/Settings.php +msgid "WooCommerce customers by type" +msgstr "WooCommerce-Kunden nach Typ" + +#: src/Admin/Settings.php +msgid "Custom Metrics" +msgstr "Benutzerdefinierte Metriken" + +#: src/Admin/Settings.php +msgid "You can add custom metrics using the wp_prometheus_collect_metrics action:" +msgstr "Sie koennen benutzerdefinierte Metriken mit der wp_prometheus_collect_metrics-Aktion hinzufuegen:" + +#: src/Plugin.php msgid "Settings" msgstr "Einstellungen" #. translators: 1: Required PHP version, 2: Current PHP version -#: wp-prometheus.php:112 +#: wp-prometheus.php msgid "WP Prometheus requires PHP version %1$s or higher. You are running PHP %2$s." msgstr "WP Prometheus erfordert PHP-Version %1$s oder hoeher. Sie verwenden PHP %2$s." #. translators: 1: Required WordPress version, 2: Current WordPress version -#: wp-prometheus.php:127 +#: wp-prometheus.php msgid "WP Prometheus requires WordPress version %1$s or higher. You are running WordPress %2$s." msgstr "WP Prometheus erfordert WordPress-Version %1$s oder hoeher. Sie verwenden WordPress %2$s." -#: wp-prometheus.php:140 +#: wp-prometheus.php msgid "WP Prometheus requires Composer dependencies to be installed. Please run \"composer install\" in the plugin directory." msgstr "WP Prometheus erfordert installierte Composer-Abhaengigkeiten. Bitte fuehren Sie \"composer install\" im Plugin-Verzeichnis aus." #. translators: %s: Required PHP version -#: wp-prometheus.php:156 +#: wp-prometheus.php msgid "WP Prometheus requires PHP version %s or higher." msgstr "WP Prometheus erfordert PHP-Version %s oder hoeher." diff --git a/languages/wp-prometheus.pot b/languages/wp-prometheus.pot index 5028cbd..f145c15 100644 --- a/languages/wp-prometheus.pot +++ b/languages/wp-prometheus.pot @@ -2,7 +2,7 @@ # This file is distributed under the GPL v2 or later. msgid "" msgstr "" -"Project-Id-Version: WP Prometheus 0.1.0\n" +"Project-Id-Version: WP Prometheus 0.2.0\n" "Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wp-prometheus/issues\n" "POT-Creation-Date: 2026-02-02T00:00:00+00:00\n" "MIME-Version: 1.0\n" @@ -12,184 +12,347 @@ msgstr "" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" -#: src/Admin/Settings.php:40 +#: src/Admin/Settings.php msgid "Metrics Settings" msgstr "" -#: src/Admin/Settings.php:41 +#: src/Admin/Settings.php msgid "Metrics" msgstr "" -#: src/Admin/Settings.php:58 -msgid "License Settings" +#: src/Admin/Settings.php +msgid "License" msgstr "" -#: src/Admin/Settings.php:65 -msgid "Authentication" +#: src/Admin/Settings.php +msgid "Help" msgstr "" -#: src/Admin/Settings.php:73 -msgid "Default Metrics" -msgstr "" - -#: src/Admin/Settings.php:150 +#: src/Admin/Settings.php msgid "License settings saved." msgstr "" -#: src/Admin/Settings.php:195 +#: src/Admin/Settings.php msgid "License is active and valid." msgstr "" -#: src/Admin/Settings.php:196 +#: src/Admin/Settings.php msgid "License is invalid." msgstr "" -#: src/Admin/Settings.php:197 +#: src/Admin/Settings.php msgid "License has expired." msgstr "" -#: src/Admin/Settings.php:198 +#: src/Admin/Settings.php msgid "License has been revoked." msgstr "" -#: src/Admin/Settings.php:199 +#: src/Admin/Settings.php msgid "License is inactive." msgstr "" -#: src/Admin/Settings.php:200 +#: src/Admin/Settings.php msgid "License has not been validated yet." msgstr "" -#: src/Admin/Settings.php:201 +#: src/Admin/Settings.php msgid "License server is not configured." msgstr "" +#: src/Admin/Settings.php +msgid "Unknown status." +msgstr "" + #. translators: %s: Expiration date -#: src/Admin/Settings.php:214 +#: src/Admin/Settings.php msgid "Expires: %s" msgstr "" #. translators: %s: Time ago -#: src/Admin/Settings.php:225 +#: src/Admin/Settings.php msgid "Last checked: %s ago" msgstr "" -#: src/Admin/Settings.php:239 +#: src/Admin/Settings.php msgid "License Server URL" msgstr "" -#: src/Admin/Settings.php:249 +#: src/Admin/Settings.php msgid "License Key" msgstr "" -#: src/Admin/Settings.php:259 +#: src/Admin/Settings.php msgid "Server Secret" msgstr "" -#: src/Admin/Settings.php:264 +#: src/Admin/Settings.php msgid "Leave empty to keep existing." msgstr "" -#: src/Admin/Settings.php:270 +#: src/Admin/Settings.php msgid "Save License Settings" msgstr "" -#: src/Admin/Settings.php:272 +#: src/Admin/Settings.php msgid "Validate License" msgstr "" -#: src/Admin/Settings.php:275 +#: src/Admin/Settings.php msgid "Activate License" msgstr "" -#: src/Admin/Settings.php:301 -msgid "Configure authentication for the /metrics endpoint." +#: src/Admin/Settings.php +msgid "Authentication" msgstr "" -#: src/Admin/Settings.php:310 -msgid "Select which default metrics to expose." -msgstr "" - -#: src/Admin/Settings.php:324 -msgid "Regenerate" -msgstr "" - -#: src/Admin/Settings.php:327 -msgid "Use this token to authenticate Prometheus scrape requests." -msgstr "" - -#: src/Admin/Settings.php:340 -msgid "WordPress Info (version, PHP version, multisite)" -msgstr "" - -#: src/Admin/Settings.php:341 -msgid "Total Users by Role" -msgstr "" - -#: src/Admin/Settings.php:342 -msgid "Total Posts by Type and Status" -msgstr "" - -#: src/Admin/Settings.php:343 -msgid "Total Comments by Status" -msgstr "" - -#: src/Admin/Settings.php:344 -msgid "Total Plugins (active/inactive)" -msgstr "" - -#: src/Admin/Settings.php:345 -msgid "HTTP Requests Total (by method, status, endpoint)" -msgstr "" - -#: src/Admin/Settings.php:346 -msgid "HTTP Request Duration (histogram)" -msgstr "" - -#: src/Admin/Settings.php:347 -msgid "Database Queries Total (by endpoint)" -msgstr "" - -#: src/Admin/Settings.php:369 -msgid "Prometheus Configuration" -msgstr "" - -#: src/Admin/Settings.php:370 -msgid "Add the following to your prometheus.yml:" -msgstr "" - -#. translators: %s: Endpoint URL -#: src/Admin/Settings.php:385 -msgid "Metrics endpoint: %s" -msgstr "" - -#: src/Admin/Settings.php:93 -msgid "Auth Token" -msgstr "" - -#: src/Admin/Settings.php:101 +#: src/Admin/Settings.php msgid "Enabled Metrics" msgstr "" -#: src/Plugin.php:120 +#: src/Admin/Settings.php +msgid "Configure authentication for the /metrics endpoint." +msgstr "" + +#: src/Admin/Settings.php +msgid "Select which metrics to expose on the /metrics endpoint." +msgstr "" + +#: src/Admin/Settings.php +msgid "Auth Token" +msgstr "" + +#: src/Admin/Settings.php +msgid "Select Metrics" +msgstr "" + +#: src/Admin/Settings.php +msgid "Regenerate" +msgstr "" + +#: src/Admin/Settings.php +msgid "Use this token to authenticate Prometheus scrape requests." +msgstr "" + +#: src/Admin/Settings.php +msgid "Static Metrics" +msgstr "" + +#: src/Admin/Settings.php +msgid "WordPress Info (version, PHP version, multisite)" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total Users by Role" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total Posts by Type and Status" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total Comments by Status" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total Plugins (active/inactive)" +msgstr "" + +#: src/Admin/Settings.php +msgid "Cron Events (scheduled tasks, overdue, next run)" +msgstr "" + +#: src/Admin/Settings.php +msgid "Transients (total, expiring, expired)" +msgstr "" + +#: src/Admin/Settings.php +msgid "Runtime Metrics" +msgstr "" + +#: src/Admin/Settings.php +msgid "Runtime metrics track data across requests. Enable only what you need to minimize performance impact." +msgstr "" + +#: src/Admin/Settings.php +msgid "HTTP Requests Total (by method, status, endpoint)" +msgstr "" + +#: src/Admin/Settings.php +msgid "HTTP Request Duration (histogram)" +msgstr "" + +#: src/Admin/Settings.php +msgid "Database Queries Total (by endpoint)" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce Metrics" +msgstr "" + +#: src/Admin/Settings.php +msgid "Metrics specific to WooCommerce stores. Only available when WooCommerce is active." +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce Products (by status and type)" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce Orders (by status)" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce Revenue (all time, today, month)" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce Customers (registered, guest)" +msgstr "" + +#: src/Admin/Settings.php +msgid "Prometheus Configuration" +msgstr "" + +#: src/Admin/Settings.php +msgid "Add the following to your prometheus.yml:" +msgstr "" + +#: src/Admin/Settings.php +msgid "Endpoint Information" +msgstr "" + +#: src/Admin/Settings.php +msgid "Metrics URL" +msgstr "" + +#: src/Admin/Settings.php +msgid "Testing the Endpoint" +msgstr "" + +#: src/Admin/Settings.php +msgid "You can test the endpoint using curl:" +msgstr "" + +#: src/Admin/Settings.php +msgid "Available Metrics" +msgstr "" + +#: src/Admin/Settings.php +msgid "Metric" +msgstr "" + +#: src/Admin/Settings.php +msgid "Type" +msgstr "" + +#: src/Admin/Settings.php +msgid "Description" +msgstr "" + +#: src/Admin/Settings.php +msgid "Gauge" +msgstr "" + +#: src/Admin/Settings.php +msgid "Counter" +msgstr "" + +#: src/Admin/Settings.php +msgid "Histogram" +msgstr "" + +#: src/Admin/Settings.php +msgid "WordPress installation info" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total users by role" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total posts by type and status" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total comments by status" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total plugins by status" +msgstr "" + +#: src/Admin/Settings.php +msgid "HTTP requests by method, status, endpoint" +msgstr "" + +#: src/Admin/Settings.php +msgid "HTTP request duration distribution" +msgstr "" + +#: src/Admin/Settings.php +msgid "Database queries by endpoint" +msgstr "" + +#: src/Admin/Settings.php +msgid "Scheduled cron events by hook" +msgstr "" + +#: src/Admin/Settings.php +msgid "Number of overdue cron events" +msgstr "" + +#: src/Admin/Settings.php +msgid "Unix timestamp of next scheduled cron" +msgstr "" + +#: src/Admin/Settings.php +msgid "Total transients by type" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce products by status and type" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce orders by status" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce revenue by period" +msgstr "" + +#: src/Admin/Settings.php +msgid "WooCommerce customers by type" +msgstr "" + +#: src/Admin/Settings.php +msgid "Custom Metrics" +msgstr "" + +#: src/Admin/Settings.php +msgid "You can add custom metrics using the wp_prometheus_collect_metrics action:" +msgstr "" + +#: src/Plugin.php msgid "Settings" msgstr "" #. translators: 1: Required PHP version, 2: Current PHP version -#: wp-prometheus.php:112 +#: wp-prometheus.php msgid "WP Prometheus requires PHP version %1$s or higher. You are running PHP %2$s." msgstr "" #. translators: 1: Required WordPress version, 2: Current WordPress version -#: wp-prometheus.php:127 +#: wp-prometheus.php msgid "WP Prometheus requires WordPress version %1$s or higher. You are running WordPress %2$s." msgstr "" -#: wp-prometheus.php:140 +#: wp-prometheus.php msgid "WP Prometheus requires Composer dependencies to be installed. Please run \"composer install\" in the plugin directory." msgstr "" #. translators: %s: Required PHP version -#: wp-prometheus.php:156 +#: wp-prometheus.php msgid "WP Prometheus requires PHP version %s or higher." msgstr "" diff --git a/src/Admin/Settings.php b/src/Admin/Settings.php index c86a550..0169ce1 100644 --- a/src/Admin/Settings.php +++ b/src/Admin/Settings.php @@ -447,6 +447,46 @@ class Settings { + + wordpress_cron_events_total + + + + + wordpress_cron_overdue_total + + + + + wordpress_cron_next_run_timestamp + + + + + wordpress_transients_total + + + + + wordpress_woocommerce_products_total + + + + + wordpress_woocommerce_orders_total + + + + + wordpress_woocommerce_revenue_total + + + + + wordpress_woocommerce_customers_total + + + @@ -514,6 +554,8 @@ class Settings { 'wordpress_posts_total' => __( 'Total Posts by Type and Status', 'wp-prometheus' ), 'wordpress_comments_total' => __( 'Total Comments by Status', 'wp-prometheus' ), 'wordpress_plugins_total' => __( 'Total Plugins (active/inactive)', 'wp-prometheus' ), + 'wordpress_cron_events_total' => __( 'Cron Events (scheduled tasks, overdue, next run)', 'wp-prometheus' ), + 'wordpress_transients_total' => __( 'Transients (total, expiring, expired)', 'wp-prometheus' ), ); $runtime_metrics = array( @@ -522,6 +564,13 @@ class Settings { 'wordpress_db_queries_total' => __( 'Database Queries Total (by endpoint)', 'wp-prometheus' ), ); + $woocommerce_metrics = array( + 'wordpress_woocommerce_products_total' => __( 'WooCommerce Products (by status and type)', 'wp-prometheus' ), + 'wordpress_woocommerce_orders_total' => __( 'WooCommerce Orders (by status)', 'wp-prometheus' ), + 'wordpress_woocommerce_revenue_total' => __( 'WooCommerce Revenue (all time, today, month)', 'wp-prometheus' ), + 'wordpress_woocommerce_customers_total' => __( 'WooCommerce Customers (registered, guest)', 'wp-prometheus' ), + ); + echo '
'; echo '' . esc_html__( 'Static Metrics', 'wp-prometheus' ) . ''; echo '

' . esc_html__( 'Static Metrics', 'wp-prometheus' ) . '

'; @@ -551,6 +600,23 @@ class Settings {

' . esc_html__( 'WooCommerce Metrics', 'wp-prometheus' ) . '

'; + echo '

' . esc_html__( 'Metrics specific to WooCommerce stores. Only available when WooCommerce is active.', 'wp-prometheus' ) . '

'; + + foreach ( $woocommerce_metrics as $key => $label ) { + ?> + + '; } diff --git a/src/Metrics/Collector.php b/src/Metrics/Collector.php index 628ce95..4a7eda2 100644 --- a/src/Metrics/Collector.php +++ b/src/Metrics/Collector.php @@ -95,6 +95,21 @@ class Collector { $this->collect_plugins_total(); } + // Collect cron metrics. + if ( in_array( 'wordpress_cron_events_total', $enabled_metrics, true ) ) { + $this->collect_cron_metrics(); + } + + // Collect transient metrics. + if ( in_array( 'wordpress_transients_total', $enabled_metrics, true ) ) { + $this->collect_transient_metrics(); + } + + // Collect WooCommerce metrics (if WooCommerce is active). + if ( $this->is_woocommerce_active() ) { + $this->collect_woocommerce_metrics( $enabled_metrics ); + } + // Collect runtime metrics (HTTP requests, DB queries). $this->collect_runtime_metrics( $enabled_metrics ); @@ -236,6 +251,372 @@ class Collector { $gauge->set( count( $all_plugins ) - count( $active_plugins ), array( 'inactive' ) ); } + /** + * Collect cron metrics. + * + * @return void + */ + private function collect_cron_metrics(): void { + $cron_array = _get_cron_array(); + + if ( ! is_array( $cron_array ) ) { + return; + } + + // Events total gauge. + $events_gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'cron_events_total', + 'Total number of scheduled cron events', + array( 'hook' ) + ); + + // Count events by hook. + $hook_counts = array(); + $total_events = 0; + $overdue_count = 0; + $current_time = time(); + $next_run = PHP_INT_MAX; + + foreach ( $cron_array as $timestamp => $cron ) { + if ( $timestamp < $next_run ) { + $next_run = $timestamp; + } + + foreach ( $cron as $hook => $events ) { + $event_count = count( $events ); + $total_events += $event_count; + + if ( ! isset( $hook_counts[ $hook ] ) ) { + $hook_counts[ $hook ] = 0; + } + $hook_counts[ $hook ] += $event_count; + + // Check if overdue. + if ( $timestamp < $current_time ) { + $overdue_count += $event_count; + } + } + } + + // Set events by hook (limit to top 20 to avoid cardinality explosion). + arsort( $hook_counts ); + $hook_counts = array_slice( $hook_counts, 0, 20, true ); + foreach ( $hook_counts as $hook => $count ) { + $events_gauge->set( $count, array( $hook ) ); + } + + // Overdue events gauge. + $overdue_gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'cron_overdue_total', + 'Number of overdue cron events', + array() + ); + $overdue_gauge->set( $overdue_count, array() ); + + // Next run timestamp. + $next_run_gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'cron_next_run_timestamp', + 'Unix timestamp of next scheduled cron event', + array() + ); + if ( $next_run !== PHP_INT_MAX ) { + $next_run_gauge->set( $next_run, array() ); + } + } + + /** + * Collect transient metrics. + * + * @return void + */ + private function collect_transient_metrics(): void { + global $wpdb; + + // Count all transients. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $transient_count = $wpdb->get_var( + "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' AND option_name NOT LIKE '_transient_timeout_%'" + ); + + // Count transients with expiration. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $expiring_count = $wpdb->get_var( + "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%'" + ); + + // Count expired transients. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $expired_count = $wpdb->get_var( + $wpdb->prepare( + "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%%' AND option_value < %d", + time() + ) + ); + + // Transients total gauge. + $transients_gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'transients_total', + 'Total number of transients in database', + array( 'type' ) + ); + + $transients_gauge->set( (int) $transient_count, array( 'total' ) ); + $transients_gauge->set( (int) $expiring_count, array( 'with_expiration' ) ); + $transients_gauge->set( (int) $transient_count - (int) $expiring_count, array( 'persistent' ) ); + $transients_gauge->set( (int) $expired_count, array( 'expired' ) ); + + // Site transients (for multisite). + if ( is_multisite() ) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $site_transient_count = $wpdb->get_var( + "SELECT COUNT(*) FROM {$wpdb->sitemeta} WHERE meta_key LIKE '_site_transient_%' AND meta_key NOT LIKE '_site_transient_timeout_%'" + ); + + $transients_gauge->set( (int) $site_transient_count, array( 'site_transients' ) ); + } + } + + /** + * Check if WooCommerce is active. + * + * @return bool + */ + private function is_woocommerce_active(): bool { + return class_exists( 'WooCommerce' ); + } + + /** + * Collect WooCommerce metrics. + * + * @param array $enabled_metrics List of enabled metrics. + * @return void + */ + private function collect_woocommerce_metrics( array $enabled_metrics ): void { + // Products total. + if ( in_array( 'wordpress_woocommerce_products_total', $enabled_metrics, true ) ) { + $this->collect_woocommerce_products(); + } + + // Orders total. + if ( in_array( 'wordpress_woocommerce_orders_total', $enabled_metrics, true ) ) { + $this->collect_woocommerce_orders(); + } + + // Revenue. + if ( in_array( 'wordpress_woocommerce_revenue_total', $enabled_metrics, true ) ) { + $this->collect_woocommerce_revenue(); + } + + // Customers. + if ( in_array( 'wordpress_woocommerce_customers_total', $enabled_metrics, true ) ) { + $this->collect_woocommerce_customers(); + } + } + + /** + * Collect WooCommerce products metrics. + * + * @return void + */ + private function collect_woocommerce_products(): void { + $gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'woocommerce_products_total', + 'Total number of WooCommerce products by status and type', + array( 'status', 'type' ) + ); + + // Get product counts by status. + $product_counts = wp_count_posts( 'product' ); + $product_types = wc_get_product_types(); + + foreach ( get_object_vars( $product_counts ) as $status => $count ) { + if ( (int) $count > 0 ) { + $gauge->set( (int) $count, array( $status, 'all' ) ); + } + } + + // Count by product type (for published products only). + foreach ( array_keys( $product_types ) as $type ) { + $args = array( + 'status' => 'publish', + 'type' => $type, + 'limit' => -1, + 'return' => 'ids', + ); + $products = wc_get_products( $args ); + $gauge->set( count( $products ), array( 'publish', $type ) ); + } + } + + /** + * Collect WooCommerce orders metrics. + * + * @return void + */ + private function collect_woocommerce_orders(): void { + $gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'woocommerce_orders_total', + 'Total number of WooCommerce orders by status', + array( 'status' ) + ); + + // Use WooCommerce's built-in order count function. + $order_counts = wc_orders_count(); + + foreach ( $order_counts as $status => $count ) { + $gauge->set( (int) $count, array( $status ) ); + } + } + + /** + * Collect WooCommerce revenue metrics. + * + * @return void + */ + private function collect_woocommerce_revenue(): void { + global $wpdb; + + $gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'woocommerce_revenue_total', + 'Total WooCommerce revenue', + array( 'period', 'currency' ) + ); + + $currency = get_woocommerce_currency(); + + // Check if HPOS (High-Performance Order Storage) is enabled. + $hpos_enabled = class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) + && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled(); + + if ( $hpos_enabled ) { + $orders_table = $wpdb->prefix . 'wc_orders'; + + // Total revenue (all time) - completed and processing orders. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $total_revenue = $wpdb->get_var( + "SELECT SUM(total_amount) FROM {$orders_table} WHERE status IN ('wc-completed', 'wc-processing')" + ); + + // Today's revenue. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $today_revenue = $wpdb->get_var( + $wpdb->prepare( + "SELECT SUM(total_amount) FROM {$orders_table} WHERE status IN ('wc-completed', 'wc-processing') AND DATE(date_created_gmt) = %s", + gmdate( 'Y-m-d' ) + ) + ); + + // This month's revenue. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $month_revenue = $wpdb->get_var( + $wpdb->prepare( + "SELECT SUM(total_amount) FROM {$orders_table} WHERE status IN ('wc-completed', 'wc-processing') AND YEAR(date_created_gmt) = %d AND MONTH(date_created_gmt) = %d", + gmdate( 'Y' ), + gmdate( 'm' ) + ) + ); + } else { + // Legacy post-based orders. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $total_revenue = $wpdb->get_var( + "SELECT SUM(meta_value) FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = '_order_total' + AND p.post_type = 'shop_order' + AND p.post_status IN ('wc-completed', 'wc-processing')" + ); + + // Today's revenue. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $today_revenue = $wpdb->get_var( + $wpdb->prepare( + "SELECT SUM(meta_value) FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = '_order_total' + AND p.post_type = 'shop_order' + AND p.post_status IN ('wc-completed', 'wc-processing') + AND DATE(p.post_date_gmt) = %s", + gmdate( 'Y-m-d' ) + ) + ); + + // This month's revenue. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $month_revenue = $wpdb->get_var( + $wpdb->prepare( + "SELECT SUM(meta_value) FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = '_order_total' + AND p.post_type = 'shop_order' + AND p.post_status IN ('wc-completed', 'wc-processing') + AND YEAR(p.post_date_gmt) = %d + AND MONTH(p.post_date_gmt) = %d", + gmdate( 'Y' ), + gmdate( 'm' ) + ) + ); + } + + $gauge->set( (float) ( $total_revenue ?? 0 ), array( 'all_time', $currency ) ); + $gauge->set( (float) ( $today_revenue ?? 0 ), array( 'today', $currency ) ); + $gauge->set( (float) ( $month_revenue ?? 0 ), array( 'month', $currency ) ); + } + + /** + * Collect WooCommerce customers metrics. + * + * @return void + */ + private function collect_woocommerce_customers(): void { + $gauge = $this->registry->getOrRegisterGauge( + $this->namespace, + 'woocommerce_customers_total', + 'Total number of WooCommerce customers', + array( 'type' ) + ); + + // Count users with customer role. + $customer_count = count_users(); + $customers = $customer_count['avail_roles']['customer'] ?? 0; + + $gauge->set( $customers, array( 'registered' ) ); + + // Count guest orders (orders without user_id). + global $wpdb; + + // Check if HPOS is enabled. + $hpos_enabled = class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) + && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled(); + + if ( $hpos_enabled ) { + $orders_table = $wpdb->prefix . 'wc_orders'; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $guest_orders = $wpdb->get_var( + "SELECT COUNT(DISTINCT billing_email) FROM {$orders_table} WHERE customer_id = 0 AND billing_email != ''" + ); + } else { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery + $guest_orders = $wpdb->get_var( + "SELECT COUNT(DISTINCT pm.meta_value) FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + LEFT JOIN {$wpdb->postmeta} pm2 ON pm2.post_id = p.ID AND pm2.meta_key = '_customer_user' + WHERE pm.meta_key = '_billing_email' + AND p.post_type = 'shop_order' + AND (pm2.meta_value = '0' OR pm2.meta_value IS NULL)" + ); + } + + $gauge->set( (int) $guest_orders, array( 'guest' ) ); + } + /** * Collect runtime metrics from stored data. * diff --git a/wp-prometheus.php b/wp-prometheus.php index 657cdea..d51c2e8 100644 --- a/wp-prometheus.php +++ b/wp-prometheus.php @@ -3,7 +3,7 @@ * Plugin Name: WP Prometheus * Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-prometheus * Description: Prometheus metrics endpoint for WordPress with extensible hooks for custom metrics. - * Version: 0.1.1 + * Version: 0.2.0 * Requires at least: 6.4 * Requires PHP: 8.3 * Author: Marco Graetsch @@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) { * * @var string */ -define( 'WP_PROMETHEUS_VERSION', '0.1.1' ); +define( 'WP_PROMETHEUS_VERSION', '0.2.0' ); /** * Plugin file path.