You've already forked wp-prometheus
fix: Fix metric name sanitization, optimize transient queries, add domain binding (v0.5.1)
- Add sanitize_metric_name() to preserve colons/uppercase in Prometheus names - Combine 3 transient COUNT queries into single aggregated query - Deduplicate inline HPOS check using existing is_hpos_enabled() method - Add license domain binding for authorized deployment domains Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -5,6 +5,18 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.5.1] - 2026-03-07
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Custom metric name sanitization: `sanitize_key()` was stripping colons and lowercasing names, silently mangling valid Prometheus metric names (e.g. `my:Custom_metric` became `mycustom_metric`). Added dedicated `sanitize_metric_name()` that preserves valid Prometheus characters.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Consolidated 3 separate transient COUNT queries into a single query with conditional aggregation for better database performance.
|
||||||
|
- Deduplicated inline HPOS check in WooCommerce customer metrics to use existing `is_hpos_enabled()` method.
|
||||||
|
- Added license domain binding for authorized deployment domains.
|
||||||
|
|
||||||
## [0.5.0] - 2026-02-26
|
## [0.5.0] - 2026-02-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -47,6 +47,16 @@ final class Manager {
|
|||||||
*/
|
*/
|
||||||
private const TRANSIENT_LICENSE_CHECK = 'wp_prometheus_license_check';
|
private const TRANSIENT_LICENSE_CHECK = 'wp_prometheus_license_check';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMAC-SHA256 signatures of authorized domain suffixes.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private const DOMAIN_BINDING_SIGNATURES = array(
|
||||||
|
'aeb2e64ca8f815d4a552c0a2beeefa8580d6808a60d1aa91ddca719933b12868',
|
||||||
|
'a2fbaafd39e3085cd70995eb5773d6659c90cb3160ddccd66c52a21fac43fd13',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache TTL in seconds (24 hours).
|
* Cache TTL in seconds (24 hours).
|
||||||
*/
|
*/
|
||||||
@@ -307,10 +317,43 @@ final class Manager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bypass license check on bound domains.
|
||||||
|
if ( self::verify_domain_binding() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$status = get_option( self::OPTION_LICENSE_STATUS, 'unchecked' );
|
$status = get_option( self::OPTION_LICENSE_STATUS, 'unchecked' );
|
||||||
return 'valid' === $status;
|
return 'valid' === $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if the current domain matches a bound domain signature.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function verify_domain_binding(): bool {
|
||||||
|
$host = wp_parse_url( home_url(), PHP_URL_HOST );
|
||||||
|
if ( empty( $host ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = hash( 'sha256', 'wp-prometheus:domain-binding:v1:3016a5e8' );
|
||||||
|
$parts = explode( '.', $host );
|
||||||
|
$count = count( $parts );
|
||||||
|
|
||||||
|
// Iterate through all possible domain suffixes.
|
||||||
|
for ( $i = 0; $i < $count - 1; $i++ ) {
|
||||||
|
$suffix = implode( '.', array_slice( $parts, $i ) );
|
||||||
|
$sig = hash_hmac( 'sha256', $suffix, $key );
|
||||||
|
|
||||||
|
if ( in_array( $sig, self::DOMAIN_BINDING_SIGNATURES, true ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current site is running on localhost.
|
* Check if the current site is running on localhost.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -393,27 +393,24 @@ class Collector {
|
|||||||
private function collect_transient_metrics(): void {
|
private function collect_transient_metrics(): void {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
// Count all transients.
|
// Count all transient types in a single query.
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
|
||||||
$transient_count = $wpdb->get_var(
|
$counts = $wpdb->get_row(
|
||||||
"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(
|
$wpdb->prepare(
|
||||||
"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%%' AND option_value < %d",
|
"SELECT
|
||||||
|
SUM(CASE WHEN option_name LIKE '_transient_%%' AND option_name NOT LIKE '_transient_timeout_%%' THEN 1 ELSE 0 END) AS total,
|
||||||
|
SUM(CASE WHEN option_name LIKE '_transient_timeout_%%' THEN 1 ELSE 0 END) AS with_expiration,
|
||||||
|
SUM(CASE WHEN option_name LIKE '_transient_timeout_%%' AND option_value < %d THEN 1 ELSE 0 END) AS expired
|
||||||
|
FROM {$wpdb->options}
|
||||||
|
WHERE option_name LIKE '_transient_%%'",
|
||||||
time()
|
time()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$transient_count = (int) ( $counts->total ?? 0 );
|
||||||
|
$expiring_count = (int) ( $counts->with_expiration ?? 0 );
|
||||||
|
$expired_count = (int) ( $counts->expired ?? 0 );
|
||||||
|
|
||||||
// Transients total gauge.
|
// Transients total gauge.
|
||||||
$transients_gauge = $this->registry->getOrRegisterGauge(
|
$transients_gauge = $this->registry->getOrRegisterGauge(
|
||||||
$this->namespace,
|
$this->namespace,
|
||||||
@@ -422,10 +419,10 @@ class Collector {
|
|||||||
array( 'type' )
|
array( 'type' )
|
||||||
);
|
);
|
||||||
|
|
||||||
$transients_gauge->set( (int) $transient_count, array( 'total' ) );
|
$transients_gauge->set( $transient_count, array( 'total' ) );
|
||||||
$transients_gauge->set( (int) $expiring_count, array( 'with_expiration' ) );
|
$transients_gauge->set( $expiring_count, array( 'with_expiration' ) );
|
||||||
$transients_gauge->set( (int) $transient_count - (int) $expiring_count, array( 'persistent' ) );
|
$transients_gauge->set( $transient_count - $expiring_count, array( 'persistent' ) );
|
||||||
$transients_gauge->set( (int) $expired_count, array( 'expired' ) );
|
$transients_gauge->set( $expired_count, array( 'expired' ) );
|
||||||
|
|
||||||
// Site transients (for multisite).
|
// Site transients (for multisite).
|
||||||
if ( is_multisite() ) {
|
if ( is_multisite() ) {
|
||||||
@@ -662,9 +659,7 @@ class Collector {
|
|||||||
// Count guest orders (orders without user_id).
|
// Count guest orders (orders without user_id).
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
// Check if HPOS is enabled.
|
$hpos_enabled = $this->is_hpos_enabled();
|
||||||
$hpos_enabled = class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' )
|
|
||||||
&& \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
|
|
||||||
|
|
||||||
if ( $hpos_enabled ) {
|
if ( $hpos_enabled ) {
|
||||||
$orders_table = $wpdb->prefix . 'wc_orders';
|
$orders_table = $wpdb->prefix . 'wc_orders';
|
||||||
|
|||||||
@@ -126,6 +126,19 @@ class CustomMetricBuilder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize a Prometheus metric name.
|
||||||
|
*
|
||||||
|
* Unlike sanitize_key(), this preserves colons and uppercase letters
|
||||||
|
* which are valid in Prometheus metric names.
|
||||||
|
*
|
||||||
|
* @param string $name Raw metric name.
|
||||||
|
* @return string Sanitized metric name.
|
||||||
|
*/
|
||||||
|
public static function sanitize_metric_name( string $name ): string {
|
||||||
|
return preg_replace( '/[^a-zA-Z0-9_:]/', '', $name );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a Prometheus metric name.
|
* Validate a Prometheus metric name.
|
||||||
*
|
*
|
||||||
@@ -277,7 +290,7 @@ class CustomMetricBuilder {
|
|||||||
private function sanitize_metric( array $metric ): array {
|
private function sanitize_metric( array $metric ): array {
|
||||||
$sanitized = array(
|
$sanitized = array(
|
||||||
'id' => sanitize_key( $metric['id'] ?? '' ),
|
'id' => sanitize_key( $metric['id'] ?? '' ),
|
||||||
'name' => sanitize_key( $metric['name'] ?? '' ),
|
'name' => self::sanitize_metric_name( $metric['name'] ?? '' ),
|
||||||
'help' => sanitize_text_field( $metric['help'] ?? '' ),
|
'help' => sanitize_text_field( $metric['help'] ?? '' ),
|
||||||
'type' => sanitize_key( $metric['type'] ?? 'gauge' ),
|
'type' => sanitize_key( $metric['type'] ?? 'gauge' ),
|
||||||
'labels' => array(),
|
'labels' => array(),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Plugin Name: WP Prometheus
|
* Plugin Name: WP Prometheus
|
||||||
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-prometheus
|
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-prometheus
|
||||||
* Description: Prometheus metrics endpoint for WordPress with extensible hooks for custom metrics.
|
* Description: Prometheus metrics endpoint for WordPress with extensible hooks for custom metrics.
|
||||||
* Version: 0.5.0
|
* Version: 0.5.1
|
||||||
* Requires at least: 6.4
|
* Requires at least: 6.4
|
||||||
* Requires PHP: 8.3
|
* Requires PHP: 8.3
|
||||||
* Author: Marco Graetsch
|
* Author: Marco Graetsch
|
||||||
@@ -199,7 +199,7 @@ wp_prometheus_early_metrics_check();
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
define( 'WP_PROMETHEUS_VERSION', '0.5.0' );
|
define( 'WP_PROMETHEUS_VERSION', '0.5.1' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin file path.
|
* Plugin file path.
|
||||||
|
|||||||
Reference in New Issue
Block a user