Revision: Tue, 29 Oct 2024 10:57:42 GMT

Advanced — Application Metrics

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.

Grafana dashboard

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.

Installation

At first, you need to install the spiral/roadrunner-bridge package.

Note
The spiral/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:

php
app/src/Application/Kernel.php
public function defineBootloaders(): array
{
    return [
        // ...
        \Spiral\RoadRunnerBridge\Bootloader\MetricsBootloader::class,
        // ...
    ];
}

Read more about bootloaders in the Framework — Bootloaders section.

Configuration

The metrics service does not require configuration in the application. However, you must activate the service in .rr.yaml:

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

Usage

Application metrics declaration

There are two ways to declare application-specific metrics in Spiral application:

Using the .rr.yaml file:

yaml
.rr.yaml
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:

php
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.

Tagged metrics

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:

  • Increased granularity: You can attach multiple labels to a metric, allowing you to slice and dice the data in various ways.
  • Better organization: Labels can help you group and organize your metrics, making it easier to find and understand the data you are looking for.
  • Simplified querying: You can use labels to filter and aggregate your metric data, making it easier to extract meaningful insights from the data.

Using the .rr.yaml file:

yaml
.rr.yaml
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.

php
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']);
    }
}

Decorators

Retries to send metrics

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:

php
$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,
));

Suppressing exceptions

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:

php
$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:

php
$metrics = new \Spiral\RoadRunner\Metrics\SuppressExceptionsMetrics(
    $container->get(\Spiral\RoadRunner\Metrics\MetricsInterface::class),
));

Registering decorators in container

Instead of defining decorators directly in your application code, it's cleaner to set them up in the container.

Here's a simple guide:

php
app/src/Application/Bootloader/MetricsBootloader.php
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 register MetricsBootloader in application kernel.


Example Application

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!