butschster / prometheus-parser
Prometheus parser written on PHP 8
Requires
- php: >=8.1
- phplrt/runtime: ^3.2
Requires (Dev)
- mockery/mockery: ^1.5
- phplrt/phplrt: ^3.2
- phpunit/phpunit: ^9.5
- vimeo/psalm: ^4.9
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-22 11:51:10 UTC
README
Welcome to the Prometheus Metrics Parser! This package makes it easy to extract valuable information from metrics in the Prometheus text-based format and the OpenMetrics 2.0 format. Whether you're looking to analyze your metrics data, integrate it into other systems, or just want a better way to visualize it, this package has you covered.
With just a few lines of code, you can easily extract valuable insights from your Prometheus metrics.
Requirements
- PHP 8.1 and above
Quick start
To install the package, run the following command from the root directory of your project:
composer require butschster/prometheus-parser
That's it!
Usage
To get started, simply pass a string containing your Prometheus metric data to the parse() method. The method will return a schema object with metric objects, each of which contains the following properties:
use Butschster\Prometheus\ParserFactory; $parser = ParserFactory::create(); $schema = $parser->parse(<<<'SCHEMA' # HELP http_requests_total The total number of HTTP requests. # TYPE http_requests_total counter http_requests_total{method="post",code="200"} 1027 1395066363000 http_requests_total{method="post",code="400"} 3 1395066363000 # Escaping in label values: msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9 # Minimalistic line: metric_without_timestamp_and_labels 12.47 # A weird metric from before the epoch: something_weird{problem="division by zero"} +Inf -3982045 # A histogram, which has a pretty complex representation in the text format: # HELP http_request_duration_seconds A histogram of the request duration. # TYPE http_request_duration_seconds histogram http_request_duration_seconds_bucket{le="0.05"} 24054 http_request_duration_seconds_bucket{le="0.1"} 33444 http_request_duration_seconds_bucket{le="0.2"} 100392 http_request_duration_seconds_bucket{le="0.5"} 129389 http_request_duration_seconds_bucket{le="1"} 133988 http_request_duration_seconds_bucket{le="+Inf"} 144320 http_request_duration_seconds_sum 53423 http_request_duration_seconds_count 144320 # Finally a summary, which has a complex representation, too: # HELP rpc_duration_seconds A summary of the RPC duration in seconds. # TYPE rpc_duration_seconds summary rpc_duration_seconds{quantile="0.01"} 3102 rpc_duration_seconds{quantile="0.05"} 3272 rpc_duration_seconds{quantile="0.5"} 4773 rpc_duration_seconds{quantile="0.9"} 9001 rpc_duration_seconds{quantile="0.99"} 76656 rpc_duration_seconds_sum 1.7560473e+07 rpc_duration_seconds_count 2693 SCHEMA );
Schema data
$metrics = $schema->getMetrics(); // array of MetricDataNode $metrics['http_requests_total']->description; // The total number of HTTP requests. $metrics['http_requests_total']->type; // counter $metrics['http_requests_total']->name; // http_requests_total $metrics['http_requests_total']->unit; // null (if not set) foreach ($metrics['go_gc_duration_seconds'] as $metric) { $metric->name; // go_gc_duration_seconds $metric->value; // Value $metric->timestamp; // Timestamp $metric->startTimestamp; // Start timestamp (st@... syntax, OpenMetrics) $metric->labels; // Array of LabelNode objects $metric->exemplars; // Array of ExemplarNode objects (OpenMetrics) }
OpenMetrics features
# EOF marker
OpenMetrics expositions must end with # EOF. The SchemaNode::$eof property is true when the marker is present and null for plain Prometheus format.
$schema->eof; // true or null
# UNIT directive
# TYPE http_request_duration_seconds gauge
# UNIT http_request_duration_seconds seconds
$metrics['http_request_duration_seconds']->unit; // "seconds"
Exemplars
OpenMetrics allows optional exemplars attached to each sample — typically used to carry a trace ID that corresponds to the measurement.
http_requests_total{code="200"} 1027 1395066363.000 # {trace_id="abc123"} 1.0 1395066363.000
$metric->exemplars; // array of ExemplarNode $metric->exemplars[0]->value; // 1.0 $metric->exemplars[0]->timestamp; // 1395066363.000 or null $metric->exemplars[0]->labels; // array of LabelNode $metric->exemplars[0]->labels[0]->name; // "trace_id" $metric->exemplars[0]->labels[0]->value; // "abc123"
Sub-metric grouping
Counter, histogram, summary and gaugehistogram families have well-known suffixes (_total, _created, _sum, _count, _bucket, _gsum, _gcount). MetricDataNode exposes convenience accessors:
// counter $metrics['requests']->getTotal(); // MetricNode for requests_total $metrics['requests']->getCreated(); // MetricNode for requests_created (optional) // histogram / summary $metrics['http_request_duration']->getSum(); // MetricNode for _sum $metrics['http_request_duration']->getCount(); // MetricNode for _count $metrics['http_request_duration']->getBuckets(); // MetricNode[] for _bucket $metrics['http_request_duration']->getCreated(); // MetricNode for _created // gaugehistogram $metrics['http_request_size']->getGSum(); // MetricNode for _gsum $metrics['http_request_size']->getGCount(); // MetricNode for _gcount
Unicode metric and label names
OpenMetrics 2.0 supports quoted identifiers for metric and label names containing characters not allowed in plain Prometheus format (dots, hyphens, etc.):
# TYPE "my.metric.name" gauge
# HELP "my.metric.name" A metric with dots in its name.
# UNIT "my.metric.name" seconds
{"my.metric.name"} 0.5
# Quoted label names:
my_metric{"unicode.label"="value", regular_label="other"} 1
Headerless (bare) metric blocks
Metrics without a # TYPE / # HELP header are valid — they are parsed with type = "unknown" and grouped into a family per metric name:
bare_metric{code="200"} 50
bare_metric{code="400"} 5
other_bare_metric 1
$metrics['bare_metric']->type; // "unknown" $metrics['bare_metric']->metrics; // both samples $metrics['other_bare_metric']; // its own family
A family may also be declared by more than one block. The blocks are merged: samples are appended in the order they appear, and each of description, type and unit is taken from the first block that declares it.
Extended metric types
In addition to the standard gauge, counter, summary and histogram types, the following OpenMetrics-specific types are supported:
gaugehistogramstatesetinfounknown
For backwards compatibility with Prometheus, untyped remains supported.
Start timestamps (st@)
foo_total 17.0 1520879607.789 st@1520430000.123
$metric->startTimestamp; // 1520430000.123
CompositeValue
In OpenMetrics 2.0, summary, histogram and gaugehistogram use a CompositeValue instead of a number for metric values.
# TYPE foo summary
foo {count:0,sum:0.0,quantile:[0.95:123.7,0.99:150]} st@1520430000.123
$metric->value->count; // 0 $metric->value->sum; // 0.0 $metric->value->quantile; // ["0.95" => 123.7, "0.99" => 150]
# TYPE foo histogram
foo {count:17,sum:324789.3,bucket:[0.0:0,1e-05:0,0.0001:5,0.1:8,1.0:10,10.0:11,100000.0:11,1e+06:15,1e+23:16,1.1e+23:17,+Inf:17]} st@1520430000.123
$metric->value->count; // 17 $metric->value->sum; // 324789.3 $metric->value->bucket; // ["0.0" => 0, "1e-05" => 0, "0.0001" => 5, /* ... */ "+Inf" => 17]
# TYPE foo gaugehistogram
foo {gcount:42,gsum:3289.3,bucket:[0.01:20,0.1:25,1:34,+Inf:42]}
$metric->value->gcount; // 42 $metric->value->gsum; // 3289.3 $metric->value->bucket; // ["0.01" => 20, "0.1" => 25, 1 => 34, "+Inf" => 42] // note: PHP casts integral numeric string keys to int
Native (sparse) histograms carry their buckets as spans instead. Span offsets are
deltas, so the same offset may appear more than once and the order is significant:
spans are exposed as a list of [offset, length] pairs.
# TYPE foo histogram
foo {count:59,sum:1.2e2,schema:7,zero_threshold:1e-4,zero_count:0,negative_spans:[1:2],negative_buckets:[5,7],positive_spans:[-1:2,3:4],positive_buckets:[5,7,10,9,8,8]}
$metric->value->schema; // 7 $metric->value->zero_threshold; // 0.0001 $metric->value->zero_count; // 0 $metric->value->negative_spans; // [[1, 2]] $metric->value->negative_buckets; // [5, 7] $metric->value->positive_spans; // [[-1, 2], [3, 4]] $metric->value->positive_buckets; // [5, 7, 10, 9, 8, 8]
On the classic {count,sum,bucket} form the native fields (schema, zero_threshold,
zero_count, the spans and their buckets) are null, and on the native form bucket
is null unless both forms are present.
Validation layer (OpenMetrics strict mode)
Attach one or more validators to the parser to enforce OpenMetrics correctness rules. Validators are called after each successful parse and throw a ValidationException subclass on violation.
use Butschster\Prometheus\ParserFactory; use Butschster\Prometheus\Validation\InfoTypeValidator; use Butschster\Prometheus\Validation\StateSetTypeValidator; use Butschster\Prometheus\Validation\UnitSuffixValidator; $parser = ParserFactory::create(); $parser->addValidator(new InfoTypeValidator()); // info families must end in _info and have value=1 $parser->addValidator(new StateSetTypeValidator()); // stateset samples must have value 0 or 1 $parser->addValidator(new UnitSuffixValidator()); // family name must end with _<unit>
Available validators
| Validator | Rule |
|---|---|
InfoTypeValidator |
Family name must end with _info; all sample values must be 1. |
StateSetTypeValidator |
Each sample must have a label named after the family; sample value must be 0 or 1. |
UnitSuffixValidator |
When # UNIT is declared, the family name must end with _<unit>. |
Implementing a custom validator
use Butschster\Prometheus\Ast\SchemaNode; use Butschster\Prometheus\Exceptions\ValidationException; use Butschster\Prometheus\Validation\ValidatorInterface; final class MyValidator implements ValidatorInterface { public function validate(SchemaNode $schema): void { foreach ($schema->getMetrics() as $family) { // ... your rules ... // throw new ValidationException('...') on violation } } }
Structured exceptions
| Exception | When |
|---|---|
ParseException |
Base class for all parse failures. |
UnexpectedTokenException |
Parser encountered an unexpected token (extends ParseException). |
ValidationException |
Base class for all validation failures. |
InvalidMetricValueException |
A metric value violates a rule (extends ValidationException). |
InvalidLabelValueException |
A label value violates a rule (extends ValidationException). |
InvalidUnitSuffixException |
Family name does not end with declared unit (extends ValidationException). |
Enjoy!
License
The MIT License (MIT). Please see LICENSE for more information.
