diff --git a/app/Providers/PrometheusServiceProvider.php b/app/Providers/PrometheusServiceProvider.php new file mode 100644 index 000000000..2cb9f1d66 --- /dev/null +++ b/app/Providers/PrometheusServiceProvider.php @@ -0,0 +1,104 @@ +helpText("How many users are registered on the website?") + ->value(function() { + return User::count(); + }); + + Prometheus::addGauge('Status count') + ->helpText("How many statuses are posted on the website?") + ->value(function() { + return Status::count(); + }); + + Prometheus::addGauge('Hafas Trips count') + ->helpText("How many hafas trips are posted grouped by operator and mode of transport?") + ->labels(["operator", "category"]) + ->value(function() { + return HafasTrip::groupBy("operator_id", "category") + ->selectRaw("count(*) AS total, operator_id, category") + ->with("operator") + ->get() + ->map(fn($item) => [$item->total, [$item->operator->name, $item->category]]) + ->toArray(); + }); + + Prometheus::addGauge('Polylines count') + ->helpText("How many polylines are saved grouped by source?") + ->labels(["source"]) + ->value(function() { + return PolyLine::groupBy("source") + ->selectRaw("count(*) AS total, source") + ->get() + ->map(fn($item) => [$item->total, [$item->source]]) + ->toArray(); + }); + + Prometheus::addGauge("queue_size") + ->helpText("How many items are currently in the job queue?") + ->label("job_name") + ->value(function() { + if (config("queue.default") === "database") { + return $this->getJobsByDisplayName("jobs"); + } + + return [Queue::size(), ["all"]]; + }); + + Prometheus::addGauge("failed_jobs_count") + ->helpText("How many jobs have failed?") + ->label("job_name") + ->value(function() { + return $this->getJobsByDisplayName("failed_jobs"); + }); + + Prometheus::addGauge("completed_jobs_count") + ->helpText("How many jobs are done? Old items from queue monitor table are deleted after 7 days.") + ->labels(["job_name", "status"]) + ->value(function() { + return DB::table("queue_monitor") + ->groupBy("name", "status") + ->selectRaw("count(*) AS total, name, status") + ->get() + ->map(fn($item) => [$item->total, [$item->name, MonitorStatus::toNamedArray()[$item->status]]]) + ->toArray(); + }); + + Prometheus::addGauge("profile_image_count") + ->helpText("How many profile images are stored?") + ->value(function() { + $iter = new \FilesystemIterator(public_path("uploads/avatars")); + return iterator_count($iter); + }); + } + + + private function getJobsByDisplayName($table_name): array { + return DB::table($table_name) + ->get("payload") + ->map(fn($row) => json_decode($row->payload)) + ->countBy(fn($payload) => $payload->displayName) + ->mapWithKeys(fn($total, $key) => [$total, [$key]]) + ->toArray(); + } +} diff --git a/composer.json b/composer.json index d82a5a916..75ba99405 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "revolution/laravel-mastodon-api": "^3.0", "romanzipp/laravel-queue-monitor": "^4.0", "spatie/icalendar-generator": "^2.0", + "spatie/laravel-prometheus": "^1.0", "spatie/laravel-sitemap": "^6.0", "spatie/laravel-validation-rules": "^3.2", "spatie/laravel-webhook-server": "^3.4", @@ -55,7 +56,10 @@ "config": { "optimize-autoloader": true, "preferred-install": "dist", - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "stephank/composer-plugin-nixify": true + } }, "extra": { "laravel": { diff --git a/composer.lock b/composer.lock index ae54a0c97..845ca2cb9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e413208c26e2c1403f53c55f981aa849", + "content-hash": "0dd19e6bc0052a6a333069272cd20fc8", "packages": [ { "name": "barryvdh/laravel-dompdf", @@ -4671,6 +4671,73 @@ ], "time": "2023-07-09T15:24:48+00:00" }, + { + "name": "promphp/prometheus_client_php", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/PromPHP/prometheus_client_php.git", + "reference": "82e42b5a491ee94127110548ee835052b9765a78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/82e42b5a491ee94127110548ee835052b9765a78", + "reference": "82e42b5a491ee94127110548ee835052b9765a78", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.2|^8.0" + }, + "replace": { + "endclothing/prometheus_client_php": "*", + "jimdo/prometheus_client_php": "*", + "lkaemmerling/prometheus_client_php": "*" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.3|^7.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5.4", + "phpstan/phpstan-phpunit": "^1.1.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "phpunit/phpunit": "^9.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/polyfill-apcu": "^1.6" + }, + "suggest": { + "ext-apc": "Required if using APCu.", + "ext-redis": "Required if using Redis.", + "promphp/prometheus_push_gateway_php": "An easy client for using Prometheus PushGateway.", + "symfony/polyfill-apcu": "Required if you use APCu on PHP8.0+" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Prometheus\\": "src/Prometheus/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Lukas Kämmerling", + "email": "kontakt@lukas-kaemmerling.de" + } + ], + "description": "Prometheus instrumentation library for PHP applications.", + "support": { + "issues": "https://github.com/PromPHP/prometheus_client_php/issues", + "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.7.1" + }, + "time": "2023-04-24T18:11:52+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -6070,6 +6137,81 @@ ], "time": "2023-08-23T09:04:39+00:00" }, + { + "name": "spatie/laravel-prometheus", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-prometheus.git", + "reference": "4fbe7aa88f643abfdc721a66b7a3e375ecd57cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-prometheus/zipball/4fbe7aa88f643abfdc721a66b7a3e375ecd57cda", + "reference": "4fbe7aa88f643abfdc721a66b7a3e375ecd57cda", + "shasum": "" + }, + "require": { + "illuminate/config": "*", + "illuminate/contracts": "^10.0", + "illuminate/support": "*", + "php": "^8.1", + "promphp/prometheus_client_php": "^2.7.1", + "spatie/laravel-package-tools": "^1.15.0" + }, + "require-dev": { + "laravel/horizon": "^5.16.1", + "laravel/pint": "^1.10", + "nunomaduro/collision": "^7", + "nunomaduro/larastan": "^2.6.0", + "orchestra/testbench": "^8.5.5", + "pestphp/pest": "^2.6.2", + "pestphp/pest-plugin-arch": "^2.2", + "pestphp/pest-plugin-laravel": "^2.0", + "spatie/laravel-ray": "^1.32.4", + "spatie/pest-plugin-snapshots": "^2.0.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Prometheus\\PrometheusServiceProvider" + ], + "aliases": { + "Prometheus": "Spatie\\Prometheus\\Facades\\Prometheus" + } + } + }, + "autoload": { + "psr-4": { + "Spatie\\Prometheus\\": "src/", + "Spatie\\Prometheus\\Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Export Laravel metrics to Prometheus", + "homepage": "https://github.com/spatie/laravel-prometheus", + "keywords": [ + "laravel", + "laravel-prometheus", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-prometheus/issues", + "source": "https://github.com/spatie/laravel-prometheus/tree/1.0.4" + }, + "time": "2023-07-26T10:17:55+00:00" + }, { "name": "spatie/laravel-sitemap", "version": "6.3.1", diff --git a/config/app.php b/config/app.php index 6aaa90830..fa400666b 100644 --- a/config/app.php +++ b/config/app.php @@ -191,6 +191,7 @@ App\Providers\AuthServiceProvider::class, Laravel\Passport\PassportServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, + App\Providers\PrometheusServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, diff --git a/config/prometheus.php b/config/prometheus.php new file mode 100644 index 000000000..8049db523 --- /dev/null +++ b/config/prometheus.php @@ -0,0 +1,43 @@ + true, + + /* + * The urls that will return metrics. + */ + 'urls' => [ + 'default' => 'prometheus', + ], + + /* + * Only these IP's will be allowed to visit the above urls. + * All IP's are allowed when empty. + */ + 'allowed_ips' => [ + '127.0.0.1', + '::1' + // '1.2.3.4', + ], + + /* + * This is the default namespace that will be + * used by all metrics + */ + 'default_namespace' => 'trwl', + + /* + * The middleware that will be applied to the urls above + */ + 'middleware' => [ + Spatie\Prometheus\Http\Middleware\AllowIps::class, + ], + + /* + * You can override these classes to customize low-level behaviour of the package. + * In most cases, you can just use the defaults. + */ + 'actions' => [ + 'render_collectors' => Spatie\Prometheus\Actions\RenderCollectorsAction::class, + ], +];