Skip to content

Commit

Permalink
Handle metrics for databases
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbowers committed Jul 26, 2022
1 parent 9ba3ff3 commit fab3409
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 15 deletions.
9 changes: 9 additions & 0 deletions config/metrics.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
<?php

return [
// Allowed values: 'redis', 'apc', 'apcng', 'memory' (default)
'adapter' => env('METRICS_ADAPTER', 'memory'),

// What metrics do we want to report on?
'handle' => [
'database' => env('METRICS_HANDLE_DATABASE', true),
'http' => env('METRICS_HANDLE_HTTP', true),
],

// The route configuration controls which path the metrics data is served on
'route' => [
'enabled' => env('ENABLE_METRICS', true),
Expand Down
71 changes: 56 additions & 15 deletions src/CloudNativeServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,32 @@

use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use Jobilla\CloudNative\Laravel\Exceptions\UnsupportedAdapterException;
use Jobilla\CloudNative\Laravel\Http\Controllers\ServeMetrics;
use Jobilla\CloudNative\Laravel\Http\Middleware\RecordPrometheusMetrics;
use Prometheus\CollectorRegistry;
use Prometheus\Storage\APC;
use Prometheus\Storage\APCng;
use Prometheus\Storage\InMemory;
use Prometheus\Storage\Redis;

class CloudNativeServiceProvider extends ServiceProvider
{
protected CollectorRegistry $registry;

public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/metrics.php', 'metrics');

$this->app->singleton(CollectorRegistry::class, function () {
return new CollectorRegistry(new APC());
});
}

public function boot(Repository $config, Kernel $kernel)
{
$this->publishes([
__DIR__.'/../config/logging.php' => config_path('logging.php'),
__DIR__.'/../config/metrics.php' => config_path('metrics.php'),
], 'cloud-native-config');

$this->publishes([
__DIR__.'/../config/logging.php' => config_path('logging.php'),
], 'cloud-native-logging');

$this->publishes([
__DIR__.'/../config/metrics.php' => config_path('metrics.php'),
], 'cloud-native-metrics');
Expand All @@ -41,11 +38,55 @@ public function boot(Repository $config, Kernel $kernel)
__DIR__.'/../assets/Dockerfile' => base_path('Dockerfile'),
], 'dockerfile');

if ($config->get('metrics.route.enabled')) {
$this->app->singleton(CollectorRegistry::class, function () use ($config) {
$adapter = $config->get('metrics.adapter', 'memory');

switch ($adapter) {
case 'redis':
return new CollectorRegistry(new Redis);
break;
case 'apc':
if (filter_var(ini_get('apcu.enable_cli'), FILTER_VALIDATE_BOOLEAN) === false && App::runningInConsole()) {
Log::warning('Metrics adapter temporarily turned to "memory" because apc is disabled in CLI. See: https://www.php.net/manual/en/apcu.configuration.php');
return new CollectorRegistry(new InMemory);
}

return new CollectorRegistry(new APC);
break;
case 'apcng':
return new CollectorRegistry(new APCng);
break;
case 'memory':
return new CollectorRegistry(new InMemory);
break;
default:
throw new UnsupportedAdapterException("Adapter `{$adapter}` is not supported.");
break;
}
});

$this->registry = $this->app->make(CollectorRegistry::class);

if ($this->registry && $config->get('metrics.handle.database')) {
DB::listen(function(QueryExecuted $query) use ($config) {
$query_labels = ['query' => $query->sql, 'mode' => strtok($query->sql, ' ')];

$query_counter = $this->registry->getOrRegisterCounter('db', 'query_total', 'Counter of total queries', array_keys($query_labels));
$query_counter->inc(array_values($query_labels));

$query_duration = $this->registry->getOrRegisterHistogram('db', 'query_duration_ms', 'Duration of query', array_keys($query_labels), $config->get('metrics.buckets'));
$query_duration->observe($query->time, array_values($query_labels));
});
}

if ($this->registry && $config->get('metrics.handle.http')) {
$kernel->prependMiddleware(RecordPrometheusMetrics::class);
}

if ($this->registry && $config->get('metrics.route.enabled')) {
/** @var Router $router */
$router = $this->app['router'];
$router->get($config->get('metrics.route.path'), ServeMetrics::class);
$kernel->prependMiddleware(RecordPrometheusMetrics::class);
}
}
}
8 changes: 8 additions & 0 deletions src/Exceptions/UnsupportedAdapterException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Jobilla\CloudNative\Laravel\Exceptions;

class UnsupportedAdapterException extends \Exception
{

}

0 comments on commit fab3409

Please sign in to comment.