Repository: LKaemmerling/laravel-horizon-prometheus-exporter
Branch: master
Commit: 9574e946bb41
Files: 30
Total size: 28.1 KB
Directory structure:
gitextract_ur9a7upc/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── .styleci.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── composer.json
├── config/
│ ├── .gitkeep
│ └── config.php
├── phpunit.xml.dist
├── routes/
│ └── api.php
├── src/
│ ├── Contracts/
│ │ └── Exporter.php
│ ├── Exporter/
│ │ ├── CurrentMasterSupervisors.php
│ │ ├── CurrentProcessesPerQueue.php
│ │ ├── CurrentWorkload.php
│ │ ├── FailedJobsPerHour.php
│ │ ├── HorizonStatus.php
│ │ ├── JobsPerMinute.php
│ │ └── RecentJobs.php
│ ├── HorizonPrometheusExporterServiceProvider.php
│ ├── Http/
│ │ ├── Controller/
│ │ │ └── HorizonPrometheusExporterController.php
│ │ └── Middleware/
│ │ └── IPWhitelistingMiddleware.php
│ └── Repository/
│ └── ExporterRepository.php
└── tests/
├── Http/
│ ├── Controller/
│ │ └── HorizonPrometheusExporterControllerTest.php
│ └── Middleware/
│ └── IPWhitelistingMiddlewareTest.php
├── Repository/
│ └── ExporterRepositoryTest.php
├── TestCase.php
└── Util/
└── NoopExporter.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
; This file is for unifying the coding style for different editors and IDEs.
; More information at https://editorconfig.org
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = space
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2
================================================
FILE: .gitattributes
================================================
# Path-based git attributes
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
# Ignore all test and documentation with "export-ignore".
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/.scrutinizer.yml export-ignore
/tests export-ignore
/.editorconfig export-ignore
================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests
on:
- push
- pull_request
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
dependency-version: [prefer-stable]
redis-version: [6]
name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.composer/cache/files
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- name: Install dependencies
run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
- name: Execute tests
run: vendor/bin/phpunit
================================================
FILE: .gitignore
================================================
build
composer.lock
docs
vendor
coverage
.phpunit.result.cache
================================================
FILE: .styleci.yml
================================================
preset: laravel
disabled:
- single_class_element_per_statement
- self_accessor
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Contributions are **welcome** and will be fully **credited**.
Please read and understand the contribution guide before creating an issue or pull request.
## Etiquette
This project is open source, and as such, the maintainers give their free time to build and maintain the source code
held within. They make the code freely available in the hope that it will be of use to other developers. It would be
extremely unfair for them to suffer abuse or anger for their hard work.
Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
world that developers are civilized and selfless people.
It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
## Viability
When requesting or submitting new features, first consider whether it might be useful to others. Open
source projects are used by many developers, who may have entirely different needs to your own. Think about
whether or not your feature is likely to be used by other users of the project.
## Procedure
Before filing an issue:
- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
- Check to make sure your feature suggestion isn't already present within the project.
- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
- Check the pull requests tab to ensure that the feature isn't already in progress.
Before submitting a pull request:
- Check the codebase to ensure that your feature doesn't already exist.
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
## Requirements
If the project maintainer has any additional requirements, you will find them listed here.
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
**Happy coding**!
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) LK-Development <info@lk-development.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# Laravel Horizon Prometheus Exporter
[](https://packagist.org/packages/lkaemmerling/laravel-horizon-prometheus-exporter)
[](https://github.com/lkaemmerling/laravel-horizon-prometheus-exporter/actions)
[](https://packagist.org/packages/lkaemmerling/laravel-horizon-prometheus-exporter)
This package allows an easy way to expose the Laravel Horizon Metrics to Prometheus.
## Prom... What?
Prometheus is a scraping service which allows you to easily store and scrape information from your application, server or even from your router!
Prometheus itself does not know about your application, so you need a exporter on your app. This small package is exactly this, an exporter which allows Prometheus to understand some information
from your application. With Prometheus and a visualisation tool called `Grafana` you can build something like this beautiful Dashboard:

## Installation
You can install the package via composer:
```bash
composer require lkaemmerling/laravel-horizon-prometheus-exporter
```
## Configuration
```bash
php artisan vendor:publish --provider=LKDevelopment\\HorizonPrometheusExporter\\HorizonPrometheusExporterServiceProvider
```
You can configure this package by changing the values in `config/horizon-exporter.php`.
## Custom Metrics
You can also use this package easily to expose custom metrics. You just need to implement the `LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter` interface and then add your implementation to your `config/horizon-exporter.php` like we do it for the Horizon exporters: https://github.com/LKaemmerling/laravel-horizon-prometheus-exporter/blob/master/config/config.php#L17
## Dashboard
You can find a sample dashboard using this metrics on the [Grafana Marketplace](https://grafana.com/grafana/dashboards/11034).
### Testing
``` bash
composer test
```
### Changelog
Please see [Releases](https://github.com/LKaemmerling/laravel-horizon-prometheus-exporter/releases) for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
### Security
If you discover any security related issues, please email kontakt@lukas-kaemmerling.de instead of using the issue tracker.
## Credits
- [Lukas Kämmerling](https://github.com/LKaemmerling)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
================================================
FILE: composer.json
================================================
{
"name": "lkaemmerling/laravel-horizon-prometheus-exporter",
"description": "A small package to gain and export long time information from Laravel & Horizon for Prometheus.",
"keywords": [
"laravel-horizon-prometheus-exporter",
"horizon",
"laravel",
"prometheus",
"exporter"
],
"homepage": "https://github.com/lkaemmerling/laravel-horizon-prometheus-exporter",
"license": "MIT",
"authors": [
{
"name": "Lukas Kämmerling",
"email": "kontakt@lukas-kaemmerling.de",
"homepage": "https://lukas-kaemmerling.de",
"role": "Developer"
}
],
"require": {
"php": "^7.1|^8.0",
"promphp/prometheus_client_php": "^1.0.3|^2.0.0",
"illuminate/routing": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/config": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"laravel/horizon": "^4.0|^5.0"
},
"require-dev": {
"orchestra/testbench": "^v4.9|^5.3|^6.3|^7.0|^8.0|^9.0|^10.0|^11.0",
"phpunit/phpunit": "^8.2|^9.0|^10.5|^11.5.3|^12.5.12",
"symfony/var-dumper": "^4.3|^5.1|^7.2"
},
"autoload": {
"psr-4": {
"LKDevelopment\\HorizonPrometheusExporter\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"LKDevelopment\\HorizonPrometheusExporter\\Tests\\": "tests"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
},
"config": {
"sort-packages": true
},
"extra": {
"laravel": {
"providers": [
"LKDevelopment\\HorizonPrometheusExporter\\HorizonPrometheusExporterServiceProvider"
]
}
}
}
================================================
FILE: config/.gitkeep
================================================
================================================
FILE: config/config.php
================================================
<?php
return [
"namespace" => 'app',
"enabled" => env('HORIZON_PROMETHEUS_EXPORTER_ENABLED', true),
/**
* You can change the default endpoint to something other than metrics.
* Keep in mind that the change needs to be reflected in your Prometheus configuration as well.
*/
"url" => 'metrics',
/**
* You can enable or disable or even create own exporters by simply implementing the LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter Contract.
* If you want to disable oder enable a Exporter just comment the specific line out.
* If you want to add your own Exporter just add the Class Name to this array
*/
"exporters" => [
\LKDevelopment\HorizonPrometheusExporter\Exporter\CurrentMasterSupervisors::class,
\LKDevelopment\HorizonPrometheusExporter\Exporter\JobsPerMinute::class,
\LKDevelopment\HorizonPrometheusExporter\Exporter\CurrentWorkload::class,
\LKDevelopment\HorizonPrometheusExporter\Exporter\CurrentProcessesPerQueue::class,
\LKDevelopment\HorizonPrometheusExporter\Exporter\FailedJobsPerHour::class,
\LKDevelopment\HorizonPrometheusExporter\Exporter\HorizonStatus::class,
\LKDevelopment\HorizonPrometheusExporter\Exporter\RecentJobs::class
],
/**
* IP Whitelisting, you may don't want to expose your metrics on the internet so you can add the IP addresses of your Prometheus Server here.
*/
"ip_whitelist" => [
// Keep empty to allow all IP addresses
],
/**
* You can change the Middleware which is used for the IP whitelisting. You can add your own, like a token based authentication.
*/
"middleware" => \LKDevelopment\HorizonPrometheusExporter\Http\Middleware\IPWhitelistingMiddleware::class,
/**
* Allow storage to be wiped after a render of data in metrics controller
*/
"wipe_storage_after_render" => false,
];
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Spatie Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<logging>
<log type="tap" target="build/report.tap"/>
<log type="junit" target="build/report.junit.xml"/>
<log type="coverage-html" target="build/coverage"/>
<log type="coverage-text" target="build/coverage.txt"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
</phpunit>
================================================
FILE: routes/api.php
================================================
<?php
Route::get(config('horizon-exporter.url'), \LKDevelopment\HorizonPrometheusExporter\Http\Controller\HorizonPrometheusExporterController::class.'@metrics');
================================================
FILE: src/Contracts/Exporter.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Contracts;
use Prometheus\CollectorRegistry;
/**
* Interface Exporter
* Describes an metric exporter that generate the metrics
* @package LKDevelopment\HorizonPrometheusExporter\Contracts
*/
interface Exporter
{
/**
* The metrics method is used to register/describe your metrics to the exporter.
* @param CollectorRegistry $collectorRegistry
* @void
*/
public function metrics(CollectorRegistry $collectorRegistry): void;
/**
* The collect method is called from the Exporter when he collects the data.
* You don't need to call it manually. You should perform the whole data
* collection within this method.
* @void
*/
public function collect(): void;
}
================================================
FILE: src/Exporter/CurrentMasterSupervisors.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\MasterSupervisorRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class CurrentMasterSupervisors implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_current_mastersupervisors',
'Number of mastersupervisors'
);
}
public function collect(): void
{
$number = count(app(MasterSupervisorRepository::class)->all());
$this->gauge->set($number);
}
}
================================================
FILE: src/Exporter/CurrentProcessesPerQueue.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\WorkloadRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class CurrentProcessesPerQueue implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_current_processes',
'Current processes of all queues',
['queue']
);
}
public function collect(): void
{
$workloadRepository = app(WorkloadRepository::class);
$workloads = collect($workloadRepository->get())->sortBy('name')->values();
$workloads->each(function ($workload) {
$this->gauge->set($workload['processes'], [$workload['name']]);
});
}
}
================================================
FILE: src/Exporter/CurrentWorkload.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\WorkloadRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class CurrentWorkload implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_current_workload',
'Current workload of all queues',
['queue']
);
}
public function collect(): void
{
$workloadRepository = app(WorkloadRepository::class);
$workloads = collect($workloadRepository->get())->sortBy('name')->values();
$workloads->each(function ($workload) {
if (isset($workload['split_queues']) && $workload['split_queues']) {
$workload['split_queues']->each(function ($queue) {
$this->gauge->set($queue['length'], [$queue['name']]);
});
return;
}
$this->gauge->set($workload['length'], [$workload['name']]);
});
}
}
================================================
FILE: src/Exporter/FailedJobsPerHour.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\JobRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class FailedJobsPerHour implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_failed_jobs',
'The number of recently failed jobs'
);
}
public function collect(): void
{
$this->gauge->set(app(JobRepository::class)->countRecentlyFailed());
}
}
================================================
FILE: src/Exporter/HorizonStatus.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\MasterSupervisorRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class HorizonStatus implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_status',
'The status of Horizon, -1 = inactive, 0 = paused, 1 = running'
);
}
public function collect(): void
{
$status = -1;
if ($masters = app(MasterSupervisorRepository::class)->all()) {
$status = collect($masters)->contains(function ($master) {
return $master->status === 'paused';
}) ? 0 : 1;
}
$this->gauge->set($status);
}
}
================================================
FILE: src/Exporter/JobsPerMinute.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\MetricsRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class JobsPerMinute implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_jobs_per_minute',
'The number of jobs per minute'
);
}
public function collect(): void
{
$this->gauge->set(app(MetricsRepository::class)->jobsProcessedPerMinute());
}
}
================================================
FILE: src/Exporter/RecentJobs.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Exporter;
use Laravel\Horizon\Contracts\JobRepository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Gauge;
class RecentJobs implements Exporter
{
protected Gauge $gauge;
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->gauge = $collectorRegistry->getOrRegisterGauge(
config('horizon-exporter.namespace'),
'horizon_recent_jobs',
'The number of recent jobs'
);
}
public function collect(): void
{
$this->gauge->set(app(JobRepository::class)->countRecent());
}
}
================================================
FILE: src/HorizonPrometheusExporterServiceProvider.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class HorizonPrometheusExporterServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*/
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__ . '/../config/config.php' => config_path('horizon-exporter.php'),
], 'config');
}
$this->app->booted(function () {
$this->routes();
});
}
/**
* Register the application services.
*/
public function register(): void
{
$this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'horizon-exporter');
}
protected function routes(): void
{
if ($this->app->routesAreCached()) {
return;
}
if (config('horizon-exporter.enabled')) {
Route::middleware(config('horizon-exporter.middleware'))->group(
__DIR__ . '/../routes/api.php'
);
}
}
}
================================================
FILE: src/Http/Controller/HorizonPrometheusExporterController.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Http\Controller;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use LKDevelopment\HorizonPrometheusExporter\Repository\ExporterRepository;
use Prometheus\RenderTextFormat;
class HorizonPrometheusExporterController extends Controller
{
public function metrics()
{
ExporterRepository::load();
$renderer = new RenderTextFormat();
$result = $renderer->render(ExporterRepository::getRegistry()->getMetricFamilySamples());
if(config('horizon-exporter.wipe_storage_after_render', false)) {
ExporterRepository::getRegistry()->wipeStorage();
}
return new Response($result, Response::HTTP_OK, ["Content-Type" => RenderTextFormat::MIME_TYPE]);
}
}
================================================
FILE: src/Http/Middleware/IPWhitelistingMiddleware.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Http\Middleware;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\IpUtils;
class IPWhitelistingMiddleware
{
public function handle(Request $request, \Closure $next)
{
if (!empty(config('horizon-exporter.ip_whitelist'))) {
$clientIp = $request->ip();
if (IpUtils::checkIp($clientIp, config('horizon-exporter.ip_whitelist'))) {
return $next($request);
} else {
abort(403);
}
} else {
return $next($request);
}
}
}
================================================
FILE: src/Repository/ExporterRepository.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Repository;
use LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter;
use Prometheus\CollectorRegistry;
use Prometheus\Storage\InMemory;
/**
* Class ExporterRepository
* @package LKDevelopment\HorizonPrometheusExporter\Repository
*/
class ExporterRepository
{
/**
* @var CollectorRegistry
*/
protected static $registry;
/**
* @param array $exporters
*/
public static function load(array $exporters = []): void
{
$_exporters = empty($exporters) ? config('horizon-exporter.exporters') : $exporters;
if (self::getRegistry() === null) {
self::setRegistry(new CollectorRegistry(new InMemory()));
}
foreach ($_exporters as $exporter) {
$_exporter = new $exporter();
/**
* @var Exporter $_exporter
*/
$_exporter->metrics(self::$registry);
$_exporter->collect();
}
}
/**
* @param CollectorRegistry $collectorRegistry
*/
public static function setRegistry(CollectorRegistry $collectorRegistry): void
{
self::$registry = $collectorRegistry;
}
/**
* @return CollectorRegistry
*/
public static function getRegistry()
{
return self::$registry;
}
}
================================================
FILE: tests/Http/Controller/HorizonPrometheusExporterControllerTest.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Tests\Http\Controller;
use LKDevelopment\HorizonPrometheusExporter\Http\Controller\HorizonPrometheusExporterController;
use LKDevelopment\HorizonPrometheusExporter\Repository\ExporterRepository;
use LKDevelopment\HorizonPrometheusExporter\Tests\Util\NoopExporter;
use LKDevelopment\HorizonPrometheusExporter\Tests\TestCase;
class HorizonPrometheusExporterControllerTest extends TestCase
{
public function testMetrics()
{
$ctrl = new HorizonPrometheusExporterController();
$resp = $ctrl->metrics();
$expected = <<<EOF
# HELP app_noop_metric noop metric
# TYPE app_noop_metric counter
app_noop_metric{op="noop"} 1
EOF;
self::assertStringContainsString($expected, $resp);
}
}
================================================
FILE: tests/Http/Middleware/IPWhitelistingMiddlewareTest.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Tests\Http\Middleware;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use LKDevelopment\HorizonPrometheusExporter\Http\Middleware\IPWhitelistingMiddleware;
use LKDevelopment\HorizonPrometheusExporter\Tests\TestCase;
use Symfony\Component\HttpKernel\Exception\HttpException;
class IPWhitelistingMiddlewareTest extends TestCase
{
/**
* @dataProvider testCases
*/
public function testHandle($requestingIP, $expectedStatusCode)
{
$middleware = new IPWhitelistingMiddleware();
$statusCode = null;
try {
$statusCode = $middleware->handle(new Request([], [], [], [], [], ['REMOTE_ADDR' => $requestingIP]), \Closure::fromCallable(function ($next) {
return new Response();
}))->getStatusCode();
} catch (HttpException $httpException) {
$statusCode = $httpException->getStatusCode();
}
self::assertEquals($expectedStatusCode, $statusCode);
}
public function testCases()
{
return [
[
"127.0.0.1", // Requesting IP
Response::HTTP_OK // Expected Status Code
],
[
"127.0.0.2",
Response::HTTP_FORBIDDEN
],
[
"10.0.0.1",
Response::HTTP_OK
],
[
"10.0.1.1",
Response::HTTP_FORBIDDEN
]
];
}
}
================================================
FILE: tests/Repository/ExporterRepositoryTest.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Tests\Repository;
use LKDevelopment\HorizonPrometheusExporter\Repository\ExporterRepository;
use LKDevelopment\HorizonPrometheusExporter\Tests\Util\NoopExporter;
use LKDevelopment\HorizonPrometheusExporter\Tests\TestCase;
class ExporterRepositoryTest extends TestCase
{
public function testLoad()
{
ExporterRepository::load([NoopExporter::class]);
self::assertNotNull(ExporterRepository::getRegistry());
$counter = ExporterRepository::getRegistry()->getCounter("app", "noop_metric");
self::assertNotNull($counter);
}
}
================================================
FILE: tests/TestCase.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Tests;
use LKDevelopment\HorizonPrometheusExporter\Tests\Util\NoopExporter;
class TestCase extends \Orchestra\Testbench\TestCase
{
protected function getEnvironmentSetUp($app)
{
$app['config']->set('horizon-exporter.exporters', [NoopExporter::class]);
$app['config']->set('horizon-exporter.ip_whitelist', ["127.0.0.1", "10.0.0.0/24"]);
}
}
================================================
FILE: tests/Util/NoopExporter.php
================================================
<?php
namespace LKDevelopment\HorizonPrometheusExporter\Tests\Util;
use Prometheus\CollectorRegistry;
use Prometheus\Counter;
/**
* Class NoopExporter
* @package LKDevelopment\HorizonPrometheusExporter\Tests\Util
*/
class NoopExporter implements \LKDevelopment\HorizonPrometheusExporter\Contracts\Exporter
{
/**
* @var Counter
*/
protected Counter $counter;
/**
* @inheritDoc
*/
public function metrics(CollectorRegistry $collectorRegistry): void
{
$this->counter = $collectorRegistry->getOrRegisterCounter(
"app",
'noop_metric',
'noop metric',
["op"]
);
}
/**
* @inheritDoc
*/
public function collect(): void
{
$this->counter->inc(["op" => "noop"]);
}
}
gitextract_ur9a7upc/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ └── tests.yml
├── .gitignore
├── .styleci.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── composer.json
├── config/
│ ├── .gitkeep
│ └── config.php
├── phpunit.xml.dist
├── routes/
│ └── api.php
├── src/
│ ├── Contracts/
│ │ └── Exporter.php
│ ├── Exporter/
│ │ ├── CurrentMasterSupervisors.php
│ │ ├── CurrentProcessesPerQueue.php
│ │ ├── CurrentWorkload.php
│ │ ├── FailedJobsPerHour.php
│ │ ├── HorizonStatus.php
│ │ ├── JobsPerMinute.php
│ │ └── RecentJobs.php
│ ├── HorizonPrometheusExporterServiceProvider.php
│ ├── Http/
│ │ ├── Controller/
│ │ │ └── HorizonPrometheusExporterController.php
│ │ └── Middleware/
│ │ └── IPWhitelistingMiddleware.php
│ └── Repository/
│ └── ExporterRepository.php
└── tests/
├── Http/
│ ├── Controller/
│ │ └── HorizonPrometheusExporterControllerTest.php
│ └── Middleware/
│ └── IPWhitelistingMiddlewareTest.php
├── Repository/
│ └── ExporterRepositoryTest.php
├── TestCase.php
└── Util/
└── NoopExporter.php
SYMBOL INDEX (48 symbols across 17 files)
FILE: src/Contracts/Exporter.php
type Exporter (line 13) | interface Exporter
method metrics (line 20) | public function metrics(CollectorRegistry $collectorRegistry): void;
method collect (line 28) | public function collect(): void;
FILE: src/Exporter/CurrentMasterSupervisors.php
class CurrentMasterSupervisors (line 12) | class CurrentMasterSupervisors implements Exporter
method metrics (line 16) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 25) | public function collect(): void
FILE: src/Exporter/CurrentProcessesPerQueue.php
class CurrentProcessesPerQueue (line 11) | class CurrentProcessesPerQueue implements Exporter
method metrics (line 15) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 25) | public function collect(): void
FILE: src/Exporter/CurrentWorkload.php
class CurrentWorkload (line 12) | class CurrentWorkload implements Exporter
method metrics (line 16) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 26) | public function collect(): void
FILE: src/Exporter/FailedJobsPerHour.php
class FailedJobsPerHour (line 12) | class FailedJobsPerHour implements Exporter
method metrics (line 16) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 25) | public function collect(): void
FILE: src/Exporter/HorizonStatus.php
class HorizonStatus (line 12) | class HorizonStatus implements Exporter
method metrics (line 16) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 25) | public function collect(): void
FILE: src/Exporter/JobsPerMinute.php
class JobsPerMinute (line 12) | class JobsPerMinute implements Exporter
method metrics (line 16) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 25) | public function collect(): void
FILE: src/Exporter/RecentJobs.php
class RecentJobs (line 12) | class RecentJobs implements Exporter
method metrics (line 16) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 25) | public function collect(): void
FILE: src/HorizonPrometheusExporterServiceProvider.php
class HorizonPrometheusExporterServiceProvider (line 8) | class HorizonPrometheusExporterServiceProvider extends ServiceProvider
method boot (line 13) | public function boot(): void
method register (line 28) | public function register(): void
method routes (line 33) | protected function routes(): void
FILE: src/Http/Controller/HorizonPrometheusExporterController.php
class HorizonPrometheusExporterController (line 12) | class HorizonPrometheusExporterController extends Controller
method metrics (line 14) | public function metrics()
FILE: src/Http/Middleware/IPWhitelistingMiddleware.php
class IPWhitelistingMiddleware (line 10) | class IPWhitelistingMiddleware
method handle (line 12) | public function handle(Request $request, \Closure $next)
FILE: src/Repository/ExporterRepository.php
class ExporterRepository (line 15) | class ExporterRepository
method load (line 25) | public static function load(array $exporters = []): void
method setRegistry (line 45) | public static function setRegistry(CollectorRegistry $collectorRegistr...
method getRegistry (line 53) | public static function getRegistry()
FILE: tests/Http/Controller/HorizonPrometheusExporterControllerTest.php
class HorizonPrometheusExporterControllerTest (line 10) | class HorizonPrometheusExporterControllerTest extends TestCase
method testMetrics (line 13) | public function testMetrics()
FILE: tests/Http/Middleware/IPWhitelistingMiddlewareTest.php
class IPWhitelistingMiddlewareTest (line 11) | class IPWhitelistingMiddlewareTest extends TestCase
method testHandle (line 16) | public function testHandle($requestingIP, $expectedStatusCode)
method testCases (line 31) | public function testCases()
FILE: tests/Repository/ExporterRepositoryTest.php
class ExporterRepositoryTest (line 9) | class ExporterRepositoryTest extends TestCase
method testLoad (line 12) | public function testLoad()
FILE: tests/TestCase.php
class TestCase (line 6) | class TestCase extends \Orchestra\Testbench\TestCase
method getEnvironmentSetUp (line 8) | protected function getEnvironmentSetUp($app)
FILE: tests/Util/NoopExporter.php
class NoopExporter (line 14) | class NoopExporter implements \LKDevelopment\HorizonPrometheusExporter\C...
method metrics (line 23) | public function metrics(CollectorRegistry $collectorRegistry): void
method collect (line 36) | public function collect(): void
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (32K chars).
[
{
"path": ".editorconfig",
"chars": 338,
"preview": "; This file is for unifying the coding style for different editors and IDEs.\n; More information at https://editorconfig."
},
{
"path": ".gitattributes",
"chars": 395,
"preview": "# Path-based git attributes\n# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html\n\n# Ignore all test and"
},
{
"path": ".github/workflows/tests.yml",
"chars": 1003,
"preview": "name: Tests\n\non:\n - push\n - pull_request\n\njobs:\n test:\n runs-on: ${{ matrix.os }}\n\n strategy:\n fail-fast: "
},
{
"path": ".gitignore",
"chars": 63,
"preview": "build\ncomposer.lock\ndocs\nvendor\ncoverage\n.phpunit.result.cache\n"
},
{
"path": ".styleci.yml",
"chars": 84,
"preview": "preset: laravel\n\ndisabled:\n - single_class_element_per_statement\n - self_accessor\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 2972,
"preview": "# Contributing\n\nContributions are **welcome** and will be fully **credited**.\n\nPlease read and understand the contributi"
},
{
"path": "LICENSE.md",
"chars": 1101,
"preview": "The MIT License (MIT)\n\nCopyright (c) LK-Development <info@lk-development.de>\n\nPermission is hereby granted, free of char"
},
{
"path": "README.md",
"chars": 2908,
"preview": "# Laravel Horizon Prometheus Exporter\n\n[,\n\n /**\n "
},
{
"path": "phpunit.xml.dist",
"chars": 1002,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit bootstrap=\"vendor/autoload.php\"\n backupGlobals=\"false\"\n "
},
{
"path": "routes/api.php",
"chars": 163,
"preview": "<?php\nRoute::get(config('horizon-exporter.url'), \\LKDevelopment\\HorizonPrometheusExporter\\Http\\Controller\\HorizonPrometh"
},
{
"path": "src/Contracts/Exporter.php",
"chars": 779,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Contracts;\n\nuse Prometheus\\CollectorRegistry;\n\n/**\n * Interfac"
},
{
"path": "src/Exporter/CurrentMasterSupervisors.php",
"chars": 739,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\n\nuse Laravel\\Horizon\\Contracts\\MasterSupervisorRepo"
},
{
"path": "src/Exporter/CurrentProcessesPerQueue.php",
"chars": 974,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\nuse Laravel\\Horizon\\Contracts\\WorkloadRepository;\nu"
},
{
"path": "src/Exporter/CurrentWorkload.php",
"chars": 1245,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\n\nuse Laravel\\Horizon\\Contracts\\WorkloadRepository;\n"
},
{
"path": "src/Exporter/FailedJobsPerHour.php",
"chars": 731,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\n\nuse Laravel\\Horizon\\Contracts\\JobRepository;\nuse L"
},
{
"path": "src/Exporter/HorizonStatus.php",
"chars": 973,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\n\nuse Laravel\\Horizon\\Contracts\\MasterSupervisorRepo"
},
{
"path": "src/Exporter/JobsPerMinute.php",
"chars": 737,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\n\nuse Laravel\\Horizon\\Contracts\\MetricsRepository;\nu"
},
{
"path": "src/Exporter/RecentJobs.php",
"chars": 707,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Exporter;\n\n\nuse Laravel\\Horizon\\Contracts\\JobRepository;\nuse L"
},
{
"path": "src/HorizonPrometheusExporterServiceProvider.php",
"chars": 1123,
"preview": "<?php\n\nnamespace LKDevelopment\\HorizonPrometheusExporter;\n\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\"
},
{
"path": "src/Http/Controller/HorizonPrometheusExporterController.php",
"chars": 790,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Http\\Controller;\n\n\nuse Illuminate\\Http\\Response;\nuse Illuminat"
},
{
"path": "src/Http/Middleware/IPWhitelistingMiddleware.php",
"chars": 647,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Http\\Middleware;\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\"
},
{
"path": "src/Repository/ExporterRepository.php",
"chars": 1347,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Repository;\n\n\nuse LKDevelopment\\HorizonPrometheusExporter\\Cont"
},
{
"path": "tests/Http/Controller/HorizonPrometheusExporterControllerTest.php",
"chars": 779,
"preview": "<?php\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Tests\\Http\\Controller;\n\nuse LKDevelopment\\HorizonPrometheusExpo"
},
{
"path": "tests/Http/Middleware/IPWhitelistingMiddlewareTest.php",
"chars": 1525,
"preview": "<?php\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Tests\\Http\\Middleware;\n\nuse Illuminate\\Http\\Request;\nuse Illumi"
},
{
"path": "tests/Repository/ExporterRepositoryTest.php",
"chars": 624,
"preview": "<?php\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Tests\\Repository;\n\nuse LKDevelopment\\HorizonPrometheusExporter\\"
},
{
"path": "tests/TestCase.php",
"chars": 426,
"preview": "<?php\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Tests;\n\nuse LKDevelopment\\HorizonPrometheusExporter\\Tests\\Util\\N"
},
{
"path": "tests/Util/NoopExporter.php",
"chars": 806,
"preview": "<?php\n\n\nnamespace LKDevelopment\\HorizonPrometheusExporter\\Tests\\Util;\n\n\nuse Prometheus\\CollectorRegistry;\nuse Prometheus"
}
]
About this extraction
This page contains the full source code of the LKaemmerling/laravel-horizon-prometheus-exporter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (28.1 KB), approximately 7.6k tokens, and a symbol index with 48 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.