Telemetry
A feather-weight metrics façade that lets any MonkeysLegion project emit counters, gauges, or histograms without coupling itself to a specific backend.
Out-of-the-box adapters:
Adapter | Backend | Package needed |
---|---|---|
PrometheusMetrics | promphp/prometheus_client_php | Pulled in by default |
StatsDMetrics | DogStatsD / Telegraf StatsD | php-statsd-client/php-statsd-client (suggest) |
NullMetrics | No-op (prod fallback / tests) | none |
All adapters implement one contract:
interface MetricsInterface
{
public function counter(string $name, float $delta = 1, array $labels = []): void;
public function gauge(string $name, float $value, array $labels = []): void;
public function histogram(string $name, float $value, array $labels = []): void;
}
1 · Installation
composer require monkeyscloud/monkeyslegion-telemetry
If you plan to ship to Prometheus you’re done—its client library is already a hard dependency.
For StatsD add:
composer require php-statsd-client/php-statsd-client
2 · Bootstrapping
use MonkeysLegion\Telemetry\{PrometheusMetrics, NullMetrics};
use Prometheus\Storage\InMemory; // any adapter from promphp
return [
MetricsInterface::class => $_ENV['APP_METRICS'] === 'off'
? new NullMetrics()
: new PrometheusMetrics(
namespace: 'myapp',
registry: new \Prometheus\CollectorRegistry(new InMemory())
),
];
Swap the storage driver for APCu, Redis, or custom as needed.
3 · Recording Metrics
/** @var MetricsInterface $m */
$m->counter('http_requests_total');
$m->counter('http_requests_total', labels: ['method' => 'GET','route' => '/posts']);
$m->gauge('queue_size', 42);
$m->histogram('sql_query_seconds', 0.032, ['statement' => 'SELECT']);
Under the hood, each adapter maps calls to its backend’s preferred API:
Prometheus: counters + histograms registered once, then inc() / observe().
StatsD: sends |c, |g, or timing buckets over UDP.
4 · Middleware Example (request latency)
final class LatencyMiddleware implements MiddlewareInterface
{
public function __construct(private MetricsInterface $m) {}
public function process(ServerRequestInterface $r, RequestHandlerInterface $h): ResponseInterface
{
$start = microtime(true);
$res = $h->handle($r);
$this->m->histogram(
'http_latency_seconds',
microtime(true) - $start,
[
'method' => $r->getMethod(),
'route' => $r->getAttribute('route')['name'] ?? 'unknown',
'code' => $res->getStatusCode(),
]
);
return $res;
}
}
Register early in the stack to monitor every request.
5 · Exposing Prometheus Metrics
#[Route('GET', '/metrics')]
public function metrics(\Prometheus\RendererInterface $renderer): ResponseInterface
{
return new Response(
200,
['Content-Type' => \Prometheus\RenderTextFormat::MIME_TYPE],
$renderer->render()
);
}
Scrape /metrics from your Prometheus server at 15-30 s intervals.
6 · Testing & Local Development
Use NullMetrics in unit tests to avoid network I/O.
For quick local graphs, switch Prometheus storage to InMemory and hit /metrics with PromLens or cURL.
7 · Extending
Custom backends – implement MetricsInterface, register via DI.
Sampling – wrap an adapter with a decorator that drops X % of calls.
Global tags – create a subclass that merges environment labels (service, instance) into every call.
License
MIT © 2025 MonkeysCloud