You've already forked wp-prometheus
feat: Add custom metric builder, export/import, and Grafana dashboards (v0.3.0)
All checks were successful
Create Release Package / build-release (push) Successful in 59s
All checks were successful
Create Release Package / build-release (push) Successful in 59s
- Custom Metrics Builder with admin UI for gauge metrics - Support for static values and WordPress option-based values - JSON-based export/import with skip/overwrite/rename modes - Three Grafana dashboard templates (overview, runtime, WooCommerce) - New tabs: Custom Metrics and Dashboards - Reset runtime metrics button Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
151
src/Admin/DashboardProvider.php
Normal file
151
src/Admin/DashboardProvider.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* Dashboard provider class.
|
||||
*
|
||||
* @package WP_Prometheus
|
||||
*/
|
||||
|
||||
namespace Magdev\WpPrometheus\Admin;
|
||||
|
||||
// Prevent direct file access.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* DashboardProvider class.
|
||||
*
|
||||
* Provides Grafana dashboard templates for download.
|
||||
*/
|
||||
class DashboardProvider {
|
||||
|
||||
/**
|
||||
* Dashboard directory path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $dashboard_dir;
|
||||
|
||||
/**
|
||||
* Available dashboard definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $dashboards = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->dashboard_dir = WP_PROMETHEUS_PATH . 'assets/dashboards/';
|
||||
|
||||
$this->dashboards = array(
|
||||
'wordpress-overview' => array(
|
||||
'title' => __( 'WordPress Overview', 'wp-prometheus' ),
|
||||
'description' => __( 'General WordPress metrics including users, posts, comments, and plugins.', 'wp-prometheus' ),
|
||||
'file' => 'wordpress-overview.json',
|
||||
'icon' => 'dashicons-wordpress',
|
||||
),
|
||||
'wordpress-runtime' => array(
|
||||
'title' => __( 'Runtime Performance', 'wp-prometheus' ),
|
||||
'description' => __( 'HTTP request metrics, database query performance, and response times.', 'wp-prometheus' ),
|
||||
'file' => 'wordpress-runtime.json',
|
||||
'icon' => 'dashicons-performance',
|
||||
),
|
||||
'wordpress-woocommerce' => array(
|
||||
'title' => __( 'WooCommerce Store', 'wp-prometheus' ),
|
||||
'description' => __( 'WooCommerce metrics including products, orders, revenue, and customers.', 'wp-prometheus' ),
|
||||
'file' => 'wordpress-woocommerce.json',
|
||||
'icon' => 'dashicons-cart',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available dashboards.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_available(): array {
|
||||
$available = array();
|
||||
|
||||
foreach ( $this->dashboards as $slug => $dashboard ) {
|
||||
$file_path = $this->dashboard_dir . $dashboard['file'];
|
||||
if ( file_exists( $file_path ) ) {
|
||||
$available[ $slug ] = $dashboard;
|
||||
}
|
||||
}
|
||||
|
||||
return $available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dashboard content by slug.
|
||||
*
|
||||
* @param string $slug Dashboard slug.
|
||||
* @return string|null JSON content or null if not found.
|
||||
*/
|
||||
public function get_dashboard( string $slug ): ?string {
|
||||
// Validate slug to prevent directory traversal.
|
||||
$slug = sanitize_file_name( $slug );
|
||||
|
||||
if ( ! isset( $this->dashboards[ $slug ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$file_path = $this->dashboard_dir . $this->dashboards[ $slug ]['file'];
|
||||
|
||||
// Security: Ensure file is within dashboard directory.
|
||||
$real_path = realpath( $file_path );
|
||||
$real_dir = realpath( $this->dashboard_dir );
|
||||
|
||||
if ( false === $real_path || false === $real_dir || strpos( $real_path, $real_dir ) !== 0 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! file_exists( $file_path ) || ! is_readable( $file_path ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
|
||||
$content = file_get_contents( $file_path );
|
||||
|
||||
if ( false === $content ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dashboard metadata by slug.
|
||||
*
|
||||
* @param string $slug Dashboard slug.
|
||||
* @return array|null Dashboard metadata or null if not found.
|
||||
*/
|
||||
public function get_metadata( string $slug ): ?array {
|
||||
$slug = sanitize_file_name( $slug );
|
||||
|
||||
if ( ! isset( $this->dashboards[ $slug ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->dashboards[ $slug ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename for download.
|
||||
*
|
||||
* @param string $slug Dashboard slug.
|
||||
* @return string|null Filename or null if not found.
|
||||
*/
|
||||
public function get_filename( string $slug ): ?string {
|
||||
$slug = sanitize_file_name( $slug );
|
||||
|
||||
if ( ! isset( $this->dashboards[ $slug ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->dashboards[ $slug ]['file'];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,7 @@ final class Installer {
|
||||
'wp_prometheus_enable_default_metrics',
|
||||
'wp_prometheus_enabled_metrics',
|
||||
'wp_prometheus_runtime_metrics',
|
||||
'wp_prometheus_custom_metrics',
|
||||
);
|
||||
|
||||
foreach ( $options as $option ) {
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Magdev\WpPrometheus\Metrics;
|
||||
use Prometheus\CollectorRegistry;
|
||||
use Prometheus\Storage\InMemory;
|
||||
use Prometheus\RenderTextFormat;
|
||||
use Magdev\WpPrometheus\Metrics\CustomMetricBuilder;
|
||||
|
||||
// Prevent direct file access.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
@@ -113,6 +114,10 @@ class Collector {
|
||||
// Collect runtime metrics (HTTP requests, DB queries).
|
||||
$this->collect_runtime_metrics( $enabled_metrics );
|
||||
|
||||
// Collect custom user-defined metrics.
|
||||
$custom_builder = new CustomMetricBuilder();
|
||||
$custom_builder->register_with_collector( $this );
|
||||
|
||||
/**
|
||||
* Fires after default metrics are collected.
|
||||
*
|
||||
|
||||
496
src/Metrics/CustomMetricBuilder.php
Normal file
496
src/Metrics/CustomMetricBuilder.php
Normal file
@@ -0,0 +1,496 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom metric builder class.
|
||||
*
|
||||
* @package WP_Prometheus
|
||||
*/
|
||||
|
||||
namespace Magdev\WpPrometheus\Metrics;
|
||||
|
||||
// Prevent direct file access.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* CustomMetricBuilder class.
|
||||
*
|
||||
* Manages custom user-defined Prometheus metrics.
|
||||
*/
|
||||
class CustomMetricBuilder {
|
||||
|
||||
/**
|
||||
* Option name for storing custom metrics.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_NAME = 'wp_prometheus_custom_metrics';
|
||||
|
||||
/**
|
||||
* Export format version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EXPORT_VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* Maximum number of labels per metric.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_LABELS = 5;
|
||||
|
||||
/**
|
||||
* Maximum number of label value combinations per metric.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_LABEL_VALUES = 50;
|
||||
|
||||
/**
|
||||
* Get all custom metrics.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_all(): array {
|
||||
$metrics = get_option( self::OPTION_NAME, array() );
|
||||
return is_array( $metrics ) ? $metrics : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single metric by ID.
|
||||
*
|
||||
* @param string $id Metric ID.
|
||||
* @return array|null
|
||||
*/
|
||||
public function get( string $id ): ?array {
|
||||
$metrics = $this->get_all();
|
||||
return $metrics[ $id ] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a metric (create or update).
|
||||
*
|
||||
* @param array $metric Metric data.
|
||||
* @return string Metric ID.
|
||||
* @throws \InvalidArgumentException If validation fails.
|
||||
*/
|
||||
public function save( array $metric ): string {
|
||||
$errors = $this->validate( $metric );
|
||||
if ( ! empty( $errors ) ) {
|
||||
throw new \InvalidArgumentException( implode( ', ', $errors ) );
|
||||
}
|
||||
|
||||
$metrics = $this->get_all();
|
||||
|
||||
// Generate ID if not provided.
|
||||
if ( empty( $metric['id'] ) ) {
|
||||
$metric['id'] = wp_generate_uuid4();
|
||||
$metric['created_at'] = time();
|
||||
}
|
||||
|
||||
$metric['updated_at'] = time();
|
||||
|
||||
// Sanitize and normalize the metric data.
|
||||
$metric = $this->sanitize_metric( $metric );
|
||||
|
||||
$metrics[ $metric['id'] ] = $metric;
|
||||
update_option( self::OPTION_NAME, $metrics );
|
||||
|
||||
return $metric['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a metric.
|
||||
*
|
||||
* @param string $id Metric ID.
|
||||
* @return bool True if deleted, false if not found.
|
||||
*/
|
||||
public function delete( string $id ): bool {
|
||||
$metrics = $this->get_all();
|
||||
|
||||
if ( ! isset( $metrics[ $id ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset( $metrics[ $id ] );
|
||||
update_option( self::OPTION_NAME, $metrics );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a Prometheus metric name.
|
||||
*
|
||||
* @param string $name Metric name.
|
||||
* @return bool True if valid.
|
||||
*/
|
||||
public function validate_name( string $name ): bool {
|
||||
// Prometheus metric names must match: [a-zA-Z_:][a-zA-Z0-9_:]*
|
||||
return (bool) preg_match( '/^[a-zA-Z_:][a-zA-Z0-9_:]*$/', $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a Prometheus label name.
|
||||
*
|
||||
* @param string $name Label name.
|
||||
* @return bool True if valid.
|
||||
*/
|
||||
public function validate_label_name( string $name ): bool {
|
||||
// Prometheus label names must match: [a-zA-Z_][a-zA-Z0-9_]*
|
||||
// Labels starting with __ are reserved.
|
||||
if ( strpos( $name, '__' ) === 0 ) {
|
||||
return false;
|
||||
}
|
||||
return (bool) preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a complete metric definition.
|
||||
*
|
||||
* @param array $metric Metric data.
|
||||
* @return array Array of error messages (empty if valid).
|
||||
*/
|
||||
public function validate( array $metric ): array {
|
||||
$errors = array();
|
||||
|
||||
// Name is required.
|
||||
if ( empty( $metric['name'] ) ) {
|
||||
$errors[] = __( 'Metric name is required.', 'wp-prometheus' );
|
||||
} elseif ( ! $this->validate_name( $metric['name'] ) ) {
|
||||
$errors[] = __( 'Metric name must start with a letter, underscore, or colon, and contain only letters, numbers, underscores, and colons.', 'wp-prometheus' );
|
||||
}
|
||||
|
||||
// Check for reserved prefixes.
|
||||
if ( ! empty( $metric['name'] ) ) {
|
||||
$reserved_prefixes = array( 'wordpress_', 'go_', 'process_', 'promhttp_' );
|
||||
foreach ( $reserved_prefixes as $prefix ) {
|
||||
if ( strpos( $metric['name'], $prefix ) === 0 ) {
|
||||
$errors[] = sprintf(
|
||||
/* translators: %s: Reserved prefix */
|
||||
__( 'Metric name cannot start with reserved prefix "%s".', 'wp-prometheus' ),
|
||||
$prefix
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for duplicate names (excluding current metric if editing).
|
||||
if ( ! empty( $metric['name'] ) ) {
|
||||
$existing = $this->get_all();
|
||||
foreach ( $existing as $id => $existing_metric ) {
|
||||
if ( $existing_metric['name'] === $metric['name'] && ( empty( $metric['id'] ) || $metric['id'] !== $id ) ) {
|
||||
$errors[] = __( 'A metric with this name already exists.', 'wp-prometheus' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Help text is required.
|
||||
if ( empty( $metric['help'] ) ) {
|
||||
$errors[] = __( 'Help text is required.', 'wp-prometheus' );
|
||||
}
|
||||
|
||||
// Validate type.
|
||||
$valid_types = array( 'gauge' );
|
||||
if ( empty( $metric['type'] ) || ! in_array( $metric['type'], $valid_types, true ) ) {
|
||||
$errors[] = __( 'Invalid metric type. Only gauge is supported.', 'wp-prometheus' );
|
||||
}
|
||||
|
||||
// Validate labels.
|
||||
if ( ! empty( $metric['labels'] ) ) {
|
||||
if ( ! is_array( $metric['labels'] ) ) {
|
||||
$errors[] = __( 'Labels must be an array.', 'wp-prometheus' );
|
||||
} elseif ( count( $metric['labels'] ) > self::MAX_LABELS ) {
|
||||
$errors[] = sprintf(
|
||||
/* translators: %d: Maximum labels */
|
||||
__( 'Maximum %d labels allowed per metric.', 'wp-prometheus' ),
|
||||
self::MAX_LABELS
|
||||
);
|
||||
} else {
|
||||
foreach ( $metric['labels'] as $label ) {
|
||||
if ( ! $this->validate_label_name( $label ) ) {
|
||||
$errors[] = sprintf(
|
||||
/* translators: %s: Label name */
|
||||
__( 'Invalid label name: %s', 'wp-prometheus' ),
|
||||
$label
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate value type.
|
||||
$valid_value_types = array( 'static', 'option' );
|
||||
if ( empty( $metric['value_type'] ) || ! in_array( $metric['value_type'], $valid_value_types, true ) ) {
|
||||
$errors[] = __( 'Invalid value type. Must be "static" or "option".', 'wp-prometheus' );
|
||||
}
|
||||
|
||||
// Validate value config based on type.
|
||||
if ( ! empty( $metric['value_type'] ) ) {
|
||||
if ( 'static' === $metric['value_type'] ) {
|
||||
// Static values validated in label_values.
|
||||
} elseif ( 'option' === $metric['value_type'] ) {
|
||||
if ( empty( $metric['value_config']['option_name'] ) ) {
|
||||
$errors[] = __( 'Option name is required for option-based metrics.', 'wp-prometheus' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate label values count.
|
||||
if ( ! empty( $metric['label_values'] ) && is_array( $metric['label_values'] ) ) {
|
||||
if ( count( $metric['label_values'] ) > self::MAX_LABEL_VALUES ) {
|
||||
$errors[] = sprintf(
|
||||
/* translators: %d: Maximum label combinations */
|
||||
__( 'Maximum %d label value combinations allowed.', 'wp-prometheus' ),
|
||||
self::MAX_LABEL_VALUES
|
||||
);
|
||||
}
|
||||
|
||||
// Validate each row has correct number of values.
|
||||
$label_count = count( $metric['labels'] ?? array() );
|
||||
foreach ( $metric['label_values'] as $row ) {
|
||||
if ( is_array( $row ) && count( $row ) !== $label_count + 1 ) { // +1 for value.
|
||||
$errors[] = __( 'Each label value row must have values for all labels plus a metric value.', 'wp-prometheus' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize metric data.
|
||||
*
|
||||
* @param array $metric Raw metric data.
|
||||
* @return array Sanitized metric data.
|
||||
*/
|
||||
private function sanitize_metric( array $metric ): array {
|
||||
$sanitized = array(
|
||||
'id' => sanitize_key( $metric['id'] ?? '' ),
|
||||
'name' => sanitize_key( $metric['name'] ?? '' ),
|
||||
'help' => sanitize_text_field( $metric['help'] ?? '' ),
|
||||
'type' => sanitize_key( $metric['type'] ?? 'gauge' ),
|
||||
'labels' => array(),
|
||||
'value_type' => sanitize_key( $metric['value_type'] ?? 'static' ),
|
||||
'value_config' => array(),
|
||||
'label_values' => array(),
|
||||
'enabled' => ! empty( $metric['enabled'] ),
|
||||
'created_at' => absint( $metric['created_at'] ?? time() ),
|
||||
'updated_at' => absint( $metric['updated_at'] ?? time() ),
|
||||
);
|
||||
|
||||
// Sanitize labels.
|
||||
if ( ! empty( $metric['labels'] ) && is_array( $metric['labels'] ) ) {
|
||||
foreach ( $metric['labels'] as $label ) {
|
||||
$sanitized['labels'][] = sanitize_key( $label );
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize value config.
|
||||
if ( 'static' === $sanitized['value_type'] ) {
|
||||
$sanitized['value_config'] = array();
|
||||
} elseif ( 'option' === $sanitized['value_type'] ) {
|
||||
$sanitized['value_config'] = array(
|
||||
'option_name' => sanitize_key( $metric['value_config']['option_name'] ?? '' ),
|
||||
'default' => floatval( $metric['value_config']['default'] ?? 0 ),
|
||||
);
|
||||
}
|
||||
|
||||
// Sanitize label values.
|
||||
if ( ! empty( $metric['label_values'] ) && is_array( $metric['label_values'] ) ) {
|
||||
foreach ( $metric['label_values'] as $row ) {
|
||||
if ( is_array( $row ) ) {
|
||||
$sanitized_row = array();
|
||||
foreach ( $row as $index => $value ) {
|
||||
// Last value is the metric value (numeric).
|
||||
if ( $index === count( $row ) - 1 ) {
|
||||
$sanitized_row[] = floatval( $value );
|
||||
} else {
|
||||
$sanitized_row[] = sanitize_text_field( $value );
|
||||
}
|
||||
}
|
||||
$sanitized['label_values'][] = $sanitized_row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all metrics to JSON.
|
||||
*
|
||||
* @return string JSON string.
|
||||
*/
|
||||
public function export(): string {
|
||||
$metrics = $this->get_all();
|
||||
|
||||
$export_data = array(
|
||||
'version' => self::EXPORT_VERSION,
|
||||
'plugin_version' => WP_PROMETHEUS_VERSION,
|
||||
'exported_at' => gmdate( 'c' ),
|
||||
'site_url' => home_url(),
|
||||
'metrics' => array_values( $metrics ),
|
||||
);
|
||||
|
||||
return wp_json_encode( $export_data, JSON_PRETTY_PRINT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import metrics from JSON.
|
||||
*
|
||||
* @param string $json JSON string.
|
||||
* @param string $mode Import mode: 'skip', 'overwrite', or 'rename'.
|
||||
* @return array Result with 'imported', 'skipped', 'errors' counts.
|
||||
* @throws \InvalidArgumentException If JSON is invalid.
|
||||
*/
|
||||
public function import( string $json, string $mode = 'skip' ): array {
|
||||
$data = json_decode( $json, true );
|
||||
|
||||
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||||
throw new \InvalidArgumentException( __( 'Invalid JSON format.', 'wp-prometheus' ) );
|
||||
}
|
||||
|
||||
if ( empty( $data['metrics'] ) || ! is_array( $data['metrics'] ) ) {
|
||||
throw new \InvalidArgumentException( __( 'No metrics found in import file.', 'wp-prometheus' ) );
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'imported' => 0,
|
||||
'skipped' => 0,
|
||||
'errors' => 0,
|
||||
'messages' => array(),
|
||||
);
|
||||
|
||||
$existing_metrics = $this->get_all();
|
||||
$existing_names = array_column( $existing_metrics, 'name', 'id' );
|
||||
|
||||
foreach ( $data['metrics'] as $metric ) {
|
||||
if ( empty( $metric['name'] ) ) {
|
||||
$result['errors']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for name collision.
|
||||
$name_exists = in_array( $metric['name'], $existing_names, true );
|
||||
|
||||
if ( $name_exists ) {
|
||||
if ( 'skip' === $mode ) {
|
||||
$result['skipped']++;
|
||||
$result['messages'][] = sprintf(
|
||||
/* translators: %s: Metric name */
|
||||
__( 'Skipped "%s" (already exists).', 'wp-prometheus' ),
|
||||
$metric['name']
|
||||
);
|
||||
continue;
|
||||
} elseif ( 'rename' === $mode ) {
|
||||
// Generate unique name.
|
||||
$base_name = $metric['name'];
|
||||
$counter = 1;
|
||||
while ( in_array( $metric['name'], $existing_names, true ) ) {
|
||||
$metric['name'] = $base_name . '_imported_' . $counter;
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
// 'overwrite' mode: continue with same name, will overwrite below.
|
||||
}
|
||||
|
||||
// Clear ID to create new metric (unless overwriting).
|
||||
if ( 'overwrite' === $mode && $name_exists ) {
|
||||
// Find existing ID by name.
|
||||
$metric['id'] = array_search( $metric['name'], $existing_names, true );
|
||||
} else {
|
||||
unset( $metric['id'] );
|
||||
}
|
||||
|
||||
try {
|
||||
$this->save( $metric );
|
||||
$result['imported']++;
|
||||
|
||||
// Update existing names for subsequent collision checks.
|
||||
$existing_names = array_column( $this->get_all(), 'name', 'id' );
|
||||
} catch ( \InvalidArgumentException $e ) {
|
||||
$result['errors']++;
|
||||
$result['messages'][] = sprintf(
|
||||
/* translators: 1: Metric name, 2: Error message */
|
||||
__( 'Error importing "%1$s": %2$s', 'wp-prometheus' ),
|
||||
$metric['name'],
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom metrics with the Collector.
|
||||
*
|
||||
* @param Collector $collector The metrics collector instance.
|
||||
* @return void
|
||||
*/
|
||||
public function register_with_collector( Collector $collector ): void {
|
||||
$metrics = $this->get_all();
|
||||
|
||||
foreach ( $metrics as $metric ) {
|
||||
if ( empty( $metric['enabled'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$gauge = $collector->register_gauge(
|
||||
$metric['name'],
|
||||
$metric['help'],
|
||||
$metric['labels'] ?? array()
|
||||
);
|
||||
|
||||
// Set values based on value type.
|
||||
if ( 'option' === $metric['value_type'] ) {
|
||||
// Option-based metric: read from WordPress option.
|
||||
$option_name = $metric['value_config']['option_name'] ?? '';
|
||||
$default = $metric['value_config']['default'] ?? 0;
|
||||
|
||||
if ( ! empty( $option_name ) ) {
|
||||
$value = get_option( $option_name, $default );
|
||||
$value = is_numeric( $value ) ? floatval( $value ) : $default;
|
||||
|
||||
// For option-based, use empty labels if no labels defined.
|
||||
$label_values = array();
|
||||
if ( ! empty( $metric['labels'] ) && ! empty( $metric['label_values'][0] ) ) {
|
||||
// Use first row of labels (without the value).
|
||||
$label_values = array_slice( $metric['label_values'][0], 0, count( $metric['labels'] ) );
|
||||
}
|
||||
|
||||
$gauge->set( $value, $label_values );
|
||||
}
|
||||
} elseif ( 'static' === $metric['value_type'] ) {
|
||||
// Static metric: use predefined label values.
|
||||
if ( ! empty( $metric['label_values'] ) ) {
|
||||
foreach ( $metric['label_values'] as $row ) {
|
||||
if ( ! is_array( $row ) || count( $row ) < 1 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Last element is the value.
|
||||
$value = array_pop( $row );
|
||||
|
||||
// Remaining elements are label values.
|
||||
$gauge->set( floatval( $value ), $row );
|
||||
}
|
||||
} else {
|
||||
// No labels, single value.
|
||||
$gauge->set( 0, array() );
|
||||
}
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// Log error but don't break metric collection.
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
error_log( sprintf( 'WP Prometheus: Failed to register custom metric "%s": %s', $metric['name'], $e->getMessage() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user