PHPackages                             rasuvaeff/yii3-metrics - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. rasuvaeff/yii3-metrics

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

rasuvaeff/yii3-metrics
======================

Vendor-neutral metrics facade (counters, gauges, histograms) for Yii3

v1.1.1(1mo ago)0473[1 issues](https://github.com/rasuvaeff/yii3-metrics/issues)[1 PRs](https://github.com/rasuvaeff/yii3-metrics/pulls)2BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Jul 10Pushed 4d agoCompare

[ Source](https://github.com/rasuvaeff/yii3-metrics)[ Packagist](https://packagist.org/packages/rasuvaeff/yii3-metrics)[ Docs](https://github.com/rasuvaeff/yii3-metrics)[ RSS](/packages/rasuvaeff-yii3-metrics/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (4)Dependencies (32)Versions (5)Used By (2)

rasuvaeff/yii3-metrics
======================

[](#rasuvaeffyii3-metrics)

[![Stable Version](https://camo.githubusercontent.com/2ef5f269dd675f8d8cb5b22a615f6f3a608a784e0cc0167ba89167e15f88c2b2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7261737576616566662f796969332d6d6574726963732e737667)](https://packagist.org/packages/rasuvaeff/yii3-metrics)[![Total Downloads](https://camo.githubusercontent.com/540535cb244b37fb75dd4a3dd2a117563e0e656dd470ca0edbd381eb06a2b6f9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7261737576616566662f796969332d6d6574726963732e737667)](https://packagist.org/packages/rasuvaeff/yii3-metrics)[![Build](https://camo.githubusercontent.com/1fd47fc50622cb2bef48fafa76e162f5b7a5ef7425d59e79f00c5f7e2b632b37/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7261737576616566662f796969332d6d6574726963732f6275696c642e796d6c3f6272616e63683d6d6173746572)](https://github.com/rasuvaeff/yii3-metrics/actions)[![Static Analysis](https://camo.githubusercontent.com/b70696ed5e3fa35108e71fbf76955176e4e2cdbcade708f222debe3528fc7fe6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7261737576616566662f796969332d6d6574726963732f7374617469632d616e616c797369732e796d6c3f6272616e63683d6d6173746572)](https://github.com/rasuvaeff/yii3-metrics/actions)[![Psalm Level](https://camo.githubusercontent.com/d25902d8bf9b8db9c2cd6f7f7b9d3cb3a09bbaa7a52670dea980720c133af3f5/68747470733a2f2f73686570686572642e6465762f6769746875622f7261737576616566662f796969332d6d6574726963732f6c6576656c2e737667)](https://shepherd.dev/github/rasuvaeff/yii3-metrics)[![PHP](https://camo.githubusercontent.com/67009259b53a18e9584934dd930826d74fb83ea63036510e34cc8a29dddc5ad3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f796969332d6d6574726963732f706870)](https://packagist.org/packages/rasuvaeff/yii3-metrics)[![License](https://camo.githubusercontent.com/f31faf417c1bba6d8d6e0e5c3457adf205cabe8e4fa48ae248eadde23933cd74/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7261737576616566662f796969332d6d6574726963732e737667)](https://github.com/rasuvaeff/yii3-metrics/blob/master/LICENSE.md)[Русская версия](README.ru.md)

Vendor-neutral metrics for Yii3: a `MetricRegistry` facade over counters, gauges, and histograms, plus a PSR-15 RED middleware. The exporter is a swappable backend (Prometheus today; the swappable provider key leaves room for others).

> Using an AI coding assistant? [llms.txt](llms.txt) has a compact API reference you can pass as context. Projects using the [llm/skills](https://github.com/roxblnfk/skills) Composer plugin also get this package's agent skill synced into `.agents/skills/`automatically on install.

Requirements
------------

[](#requirements)

- PHP 8.3+
- PSR-7/PSR-15 interfaces (for the RED middleware)

Installation
------------

[](#installation)

```
composer require rasuvaeff/yii3-metrics
```

For real export, add the backend: `rasuvaeff/yii3-metrics-prometheus`. Without one, bind `MeterProviderInterface => NullMeterProvider` (see [Wiring](#wiring-yiisoftconfig)).

Usage
-----

[](#usage)

### Record metrics

[](#record-metrics)

```
use Rasuvaeff\Yii3Metrics\LabelSet;
use Rasuvaeff\Yii3Metrics\MetricRegistry;

/** @var MetricRegistry $registry (injected) */
$orders = $registry->counter('orders_total', 'Orders placed', ['channel']);
$orders->inc(1.0, new LabelSet(['channel' => 'web']));

$inflight = $registry->upDownCounter('inflight_jobs', 'Jobs in flight');
$inflight->add(1.0);   // job started
$inflight->add(-1.0);  // job finished

$temperature = $registry->gauge('room_temperature', 'Measured value');
$temperature->set(21.5);

$latency = $registry->histogram('db_query_seconds', 'Query time', ['op'], [0.001, 0.01, 0.1]);
$latency->observe(0.023, new LabelSet(['op' => 'select']));
```

Instruments record into per-name accumulating state — asking for `counter('orders_total')` again returns an instrument over the same series. A counter rejects a negative increment.

**Gauge vs up-down counter.** A gauge is for a *measured absolute value*(`set()` — temperature, disk usage); an up-down counter is for *counted ups and downs* (`add(±δ)` — in-flight requests, pool size). Prefer the up-down counter for counted values: each process contributes deltas, so it aggregates correctly across short-lived php-fpm workers, where a gauge's `inc()`/`dec()` (kept for single-process convenience) would restart from the process-local value.

### Naming &amp; labels

[](#naming--labels)

- Metric names follow the **Prometheus** grammar `^[a-zA-Z_:][a-zA-Z0-9_:]*$`(underscores, no dots) — the lowest common denominator both backends render.
- `LabelSet` validates label names (`^[a-zA-Z_]\w*$`) and stores them in canonical order, so equality is order-independent.
- `LabelSet::key()` is the aggregation key. Every name and value is length-prefixed (`:`), so distinct label sets always get distinct keys even when values contain `=` or `,`. The exact string is an internal detail — compare label sets with `equals()`, not with `key()` strings you stored elsewhere.
- **Recorded amounts must be finite.** `counter->inc()`, `histogram->observe()`, `upDownCounter->add()` and `gauge->inc()/dec()` reject `NAN` and `±INF` with `Exception\InvalidArgumentException`: `NAN` is absorbing, so one such recording would poison a series for as long as the backend storage lives. `gauge->set()`is an absolute write, so it accepts `±INF` (the exposition has `+Inf`/`-Inf`tokens) but still rejects `NAN`, which promphp coerces to an invalid token while raising a PHP warning.

### RED middleware

[](#red-middleware)

`RedMetricsMiddleware` (PSR-15) records, for every request, a `http_server_requests_total` counter and a `http_server_request_duration_seconds`histogram, labelled by `method`, `route`, and `status` (`500` when the handler throws).

```
use Rasuvaeff\Yii3Metrics\RedMetricsMiddleware;

$middleware = new RedMetricsMiddleware($registry); // add to your PSR-15 stack

// Latency profile doesn't fit the Prometheus defaults (0.005s…10s)?
// Override the histogram bounds (seconds, strictly increasing; +Inf appended):
$middleware = new RedMetricsMiddleware($registry, durationBuckets: [0.1, 1.0, 10.0, 60.0]);

// Skip scrape/probe endpoints (exact paths) — their self-traffic is noise:
$middleware = new RedMetricsMiddleware($registry, excludedPaths: ['/metrics', '/health']);
```

With `yiisoft/config` wiring, both come from the package params instead:

```
// config/common/params.php (app override)
'rasuvaeff/yii3-metrics' => [
    'red' => [
        'duration_buckets' => [0.1, 1.0, 10.0],
        'excluded_paths' => ['/metrics', '/health'],
    ],
],
```

#### The `route` label is opt-in

[](#the-route-label-is-opt-in)

> **The shipped default never reads the request URI.** Without configuration the `route` label is the constant `(unset)` (`ConstantRouteResolver`). Rate, errors and duration are still broken down by `method` and `status`; only the per-route breakdown has to be chosen.

A raw path cannot be a safe default, because it is attacker-controlled:

RiskWhat happens with a raw-path `route`Cardinalityone series per scanned URL (`/wp-admin/...`, `/.env`, `/users/123`). In a shared promphp storage those series live until a flush: the APCu segment fills, Redis memory and scrape time grow.Disclosure`/reset-password/` reaches `/metrics` verbatim, so everyone who can scrape the endpoint reads the token.Pick one of three resolvers, most to least safe:

```
use Rasuvaeff\Yii3Metrics\{BoundedRouteResolver, CurrentRouteResolver, PathRouteResolver, RouteResolverInterface};

// 1. Matched router pattern ('/users/{id}'), low-cardinality by construction.
//    Unmatched requests (404, scanners) collapse to '(unmatched)'. Preferred.
RouteResolverInterface::class => CurrentRouteResolver::class,

// 2. Raw paths with a hard cap: the first N distinct values pass, the rest
//    become '(other)'. Bounds the series count; does NOT hide path tokens.
RouteResolverInterface::class => static fn (): RouteResolverInterface
    => new BoundedRouteResolver(new PathRouteResolver(), limit: 100),

// 3. Raw paths, unbounded — only where the path space is small and secret-free.
RouteResolverInterface::class => PathRouteResolver::class,
```

Option 2's cap is **per resolver instance**, and an instance lives in one process: on php-fpm every worker learns its own set of distinct paths, so the worst case is `limit × workers` series. That is a converging bound, not a deployment-wide cardinality guarantee.

The Prometheus backend additionally ships `SanitizingRouteResolver`, which collapses numeric ids and UUIDs in a raw path. It narrows the id case only — arbitrary scanner paths and non-UUID tokens stay unique, so treat it as a refinement of option 3, not a replacement for options 1–2.

`CurrentRouteResolver` needs `yiisoft/router`. Place `RedMetricsMiddleware`**before** the router middleware — the label is resolved after the handler ran, when `CurrentRoute` is populated.

### Inspecting metrics in tests

[](#inspecting-metrics-in-tests)

```
use Rasuvaeff\Yii3Metrics\InMemoryMeterProvider;
use Rasuvaeff\Yii3Metrics\MetricRegistry;

$provider = new InMemoryMeterProvider();
$registry = new MetricRegistry($provider);
$registry->counter('c')->inc();

$snapshots = $provider->snapshots(); // list, no timestamp
```

### API surface

[](#api-surface)

TypeRole`MetricRegistry`facade: `counter/gauge/upDownCounter/histogram(name, help, labelNames, buckets)``MeterProviderInterface` / `MeterInterface`swappable backend entry point; a meter creates and memoizes instruments`CounterInterface` / `GaugeInterface` / `UpDownCounterInterface` / `HistogramInterface`instrument contracts`LabelSet` / `MetricKind`validated label pairs / instrument kind enum (`Counter`, `Gauge`, `UpDownCounter`, `Histogram`)`MetricSnapshot` / `MetricSample`collected state: a metric (name, kind, help) and its per-label-set samples`NullMeterProvider`, `NullMeter`, `NullCounter`, `NullGauge`, `NullUpDownCounter`, `NullHistogram`no-op backend (config-only default; still validates structure)`InMemoryMeterProvider`, `InMemoryMeter`, `InMemoryCounter`, `InMemoryGauge`, `InMemoryUpDownCounter`, `InMemoryHistogram`single-process dev/test backend with `snapshots()``RedMetricsMiddleware`, `RouteResolverInterface`PSR-15 RED instrumentation`ConstantRouteResolver`safe default `route` label: a constant, never derived from the request`PathRouteResolver`, `BoundedRouteResolver`opt-in raw-path label; the bounded decorator caps how many distinct values are ever emitted`CurrentRouteResolver`route label from the matched `yiisoft/router` pattern (optional dep)`Buckets`shared histogram bucket layouts (`Buckets::PROMETHEUS_DEFAULTS`, seconds, no trailing `+Inf`)Wiring (`yiisoft/config`)
-------------------------

[](#wiring-yiisoftconfig)

The core `config/di.php` binds the facade (`MetricRegistry`) and the default `RouteResolverInterface` (`ConstantRouteResolver` — see "The `route` label is opt-in"). It never binds `MeterProviderInterface` — that swappable key is owned by exactly one source:

```
// config/common/di.php — with no backend installed
use Rasuvaeff\Yii3Metrics\MeterProviderInterface;
use Rasuvaeff\Yii3Metrics\NullMeterProvider;

return [
    MeterProviderInterface::class => NullMeterProvider::class,
];
```

Installing a backend provides the real binding — binding it in two vendor packages is a deliberate `yiisoft/config` `Duplicate key` error.

Security
--------

[](#security)

- Label names are validated; label **values** are arbitrary — keep high-cardinality or sensitive values (ids, tokens) out of labels.
- The RED `route` label is **opt-in**: the shipped default is the constant `(unset)`, precisely so an attacker-controlled path cannot mint series or carry a single-use token into `/metrics`. See "The `route` label is opt-in" before enabling a path-derived label.
- The exposition endpoint has no access control of its own — close `/metrics` at the edge/router.

Examples
--------

[](#examples)

Runnable, server-independent scripts in [`examples/`](examples/). See [`examples/README.md`](examples/README.md).

Development
-----------

[](#development)

```
docker run --rm -v "$PWD":/app -w /app composer:2 composer build
```

Runs validate → normalize → require-checker → cs → psalm → tests (incl. property tests). See [AGENTS.md](AGENTS.md).

License
-------

[](#license)

BSD-3-Clause. See [LICENSE.md](LICENSE.md).

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance96

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 75% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~7 days

Total

3

Last Release

33d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (24 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (8 commits)")

---

Tags

countersgaugeshistogramsmetricsobservabilityphppsr-15red-metricsyii3Metricscountergaugeobservabilityyii3histogram

###  Code Quality

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-yii3-metrics/health.svg)

```
[![Health](https://phpackages.com/badges/rasuvaeff-yii3-metrics/health.svg)](https://phpackages.com/packages/rasuvaeff-yii3-metrics)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.9k20.0M1.9k](/packages/cakephp-cakephp)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[cakephp/authentication

Authentication plugin for CakePHP

1214.3M120](/packages/cakephp-authentication)[typo3/cms-core

TYPO3 CMS Core

3313.6M5.7k](/packages/typo3-cms-core)[typo3/cms-adminpanel

TYPO3 CMS Admin Panel - The Admin Panel displays information about your site in the frontend and contains a range of metrics including debug and caching information.

115.8M71](/packages/typo3-cms-adminpanel)[eliashaeussler/typo3-warming

Warming - Warms up Frontend caches based on an XML sitemap. Cache warmup can be triggered via TYPO3 backend or using a console command. Supports multiple languages and custom crawler implementations.

22272.4k](/packages/eliashaeussler-typo3-warming)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
