As a professional, you know the importance of keeping track of key metrics for your application. With Prometheus, you can collect and store time series data such as application metrics, and use its powerful query language to analyze and visualize that data in real-time using a tool like Grafana saving you the time and effort of building your own dashboard from scratch.
Spiral and RoadRunner metrics plugin provide the ability to collect application metrics and expose them to Prometheus.
Note
Here you can find out more about Prometheus metrics.
At first, you need to install the spiral/roadrunner-bridge package.
Note
Thespiral/roadrunner-bridge
package allows you to use RoadRunner metrics plugin with Spiral. This package provides RPC api for metrics and a bootloader for your application.
Once the package is installed, you can add the Spiral\RoadRunnerBridge\Bootloader\MetricsBootloader
to the list of
bootloaders:
public function defineBootloaders(): array
{
return [
// ...
\Spiral\RoadRunnerBridge\Bootloader\MetricsBootloader::class,
// ...
];
}
Read more about bootloaders in the Framework — Bootloaders section.
The metrics service does not require configuration in the application. However, you must activate the service
in .rr.yaml
:
rpc:
listen: tcp://127.0.0.1:6001
# ...
metrics:
# prometheus client address (path /metrics added automatically)
address: 127.0.0.1:2112
Note
You can view defaults metrics on http://127.0.0.1:2112
There are two ways to declare application-specific metrics in Spiral application:
Using the .rr.yaml
file:
metrics:
address: 127.0.0.1:2112
collect:
registered_users:
type: counter
help: "Total registered users counter."
To populate a metric from the application, use Spiral\RoadRunner\Metrics\MetricsInterface
:
use Spiral\RoadRunner\Metrics\MetricsInterface;
class UserRegistrationHandler
{
public function __construct(
private readonly MetricsInterface $metrics
) {
}
public function handle(User $user): void
{
// Store user in database
$this->metrics->add('registered_users', 1);
}
}
See more
Supported types: gauge, counter, summary, histogram. Read more about metrics types in the official Prometheus documentation.
Using tagged (also known as labeled) metrics allows you to attach additional metadata to your metrics, which can be useful for filtering, grouping, and aggregating the data.
Some benefits of using labeled metrics include:
Using the .rr.yaml
file:
metrics:
address: 127.0.0.1:2112
collect:
registered_users:
type: histogram
help: "Total registered users counter."
labels: [ "type" ]
In the example, the registered_users
metric is declared with a label called type
. When adding data to the
metric, you can specify the value for the type label, such as customer
, admin
, etc. This allows you to differentiate
between different types of users when analyzing the metric data.
use Spiral\RoadRunner\Metrics\MetricsInterface;
class UserRegistrationHandler
{
public function __construct(
private readonly MetricsInterface $metrics
) {
}
public function handle(User $user): void
{
// Store user in database
$this->metrics->add('registered_users', 1, ['customer']);
// or
$this->metrics->add('registered_users', 1, ['admin']);
}
}
Sometimes, you might face issues sending metrics to the RoadRunner metrics plugin. The connection might drop or the plugin could be temporarily down. For such scenarios, you can make use of a retry decorator. It allows you to specify how many times and how frequently you'd like to retry.
Here is an example of how to use the retry decorator:
$factory = new \Spiral\RoadRunner\Metrics\MetricsFactory();
$rpc = $container->get(\Spiral\Goridge\RPC\RPCInterface::class);
$metrics = $factory->create($rpc, new \Spiral\RoadRunner\Metrics\MetricsOptions(
retryAttempts: 3,
retrySleepMicroseconds: 50,
));
Sometimes, you might not want errors to stop your application when sending metrics. For instance, if you're processing payments, a metric error shouldn't disrupt the transaction. You can suppress these errors.
Here is an example of how to suppress exceptions:
$factory = new \Spiral\RoadRunner\Metrics\MetricsFactory();
$rpc = $container->get(\Spiral\Goridge\RPC\RPCInterface::class);
$metrics = $factory->create(
$rpc,
new \Spiral\RoadRunner\Metrics\MetricsOptions(
suppressExceptions: true,
),
));
Alternatively, use the Spiral\RoadRunner\Metrics\SuppressExceptionsMetrics
decorator:
$metrics = new \Spiral\RoadRunner\Metrics\SuppressExceptionsMetrics(
$container->get(\Spiral\RoadRunner\Metrics\MetricsInterface::class),
));
Instead of defining decorators directly in your application code, it's cleaner to set them up in the container.
Here's a simple guide:
use Spiral\RoadRunner\Metrics\MetricsInterface;
use Spiral\RoadRunner\Metrics\Collector;
use Spiral\Goridge\RPC\RPCInterface;
class MetricsBootloader extends Bootloader
{
const SINGLETONS = [
MetricsInterface::class => [self::class, 'createMetrics'],
];
private function createMetrics(RPCInterface $rpc): MetricsInterface
{
$factory = new \Spiral\RoadRunner\Metrics\MetricsFactory();
return $factory->create(
$rpc,
new \Spiral\RoadRunner\Metrics\MetricsOptions(
suppressExceptions: true,
retryAttempts: 3,
retrySleepMicroseconds: 50,
),
));
}
}
Note
Don't forget to registerMetricsBootloader
in application kernel.
There is a good example Demo ticket booking system application built on Spiral Framework, that follows the principles of microservices and allows developers to create reusable, independent, and easy-to-maintain components.
In this demo application, you can find an example of using RoadRunner's metrics plugin.
Overall, our demo ticket booking system is a great example of how Spiral and other tools can be used to build a modern and efficient application. We hope you have a blast using it and learning more about the capabilities of our framework and the other tools we've used.
Happy (fake) ticket shopping!