cpsit / monitoring
Generic monitoring solution for web applications
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0
- ext-json: *
- guzzlehttp/guzzle: ^6.5 || ^7.0
- guzzlehttp/psr7: ^1.9 || ^2.0
- psr/http-message: ^1.0 || ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- ext-iconv: *
- armin/editorconfig-cli: ^1.7 || ^2.0
- cpsit/php-cs-fixer-config: ^1.1
- ergebnis/composer-normalize: ^2.39
- friendsofphp/php-cs-fixer: ^3.57
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^10.5 || ^11.0
- rector/rector: ^2.0
- symfony/config: ^5.4 || ^6.0 || ^7.0
- symfony/dependency-injection: ^5.4 || ^6.0 || ^7.0
- symfony/yaml: ^5.4 || ^6.0 || ^7.0
Suggests
- symfony/config: ^5.4 || ^6.0 || ^7.0
- symfony/dependency-injection: ^5.4 || ^6.0 || ^7.0
- symfony/yaml: ^5.4 || ^6.0 || ^7.0
This package is auto-updated.
Last update: 2024-12-23 23:59:49 UTC
README
Monitoring
This package provides a generic monitoring solution for web applications. It can be used to monitor various parts of your application by implementing a set of monitoring providers and exposing their health state by using a dedicated monitoring route. The package is highly customizable in terms of implementing custom monitoring providers and authorization solutions.
🔥 Installation
composer require cpsit/monitoring
⚡ Usage
Monitoring service
The package ships a Monitoring
class which can be used to check health
of various services.
Services can be defined using the MonitoringProvider
interface. Each provider can reveal the health state of the underlain service using the isHealthy()
method.
namespace My\Vendor\Monitoring\Provider; use CPSIT\Monitoring\Provider\MonitoringProvider; use My\Vendor\Service\ApiService; final class ApiMonitoringProvider implements MonitoringProvider { public function __construct( private readonly ApiService $apiService, ) {} public function getName(): string { return 'api'; } public function isHealthy(): bool { try { $response = $this->apiService->request('/health', 'HEAD'); return $response->getStatusCode() < 400; } catch (\Exception) { return false; } } }
Report errors
In addition to the normal health state, providers might also be able to report errors which
occurred during health check. For this, an
ExceptionAwareMonitoringProvider
interface exists. It allows to fetch errors using the getLastException()
method.
namespace My\Vendor\Monitoring\Provider; -use CPSIT\Monitoring\Provider\MonitoringProvider; +use CPSIT\Monitoring\Provider\ExceptionAwareMonitoringProvider; use My\Vendor\Service\ApiService; -final class ApiMonitoringProvider implements MonitoringProvider +final class ApiMonitoringProvider implements ExceptionAwareMonitoringProvider { + private ?\Throwable $lastException = null; + public function __construct( private readonly ApiService $apiService, ) {} public function getName(): string { return 'api'; } public function isHealthy(): bool { try { $response = $this->apiService->request('/health', 'HEAD'); return $response->getStatusCode() < 400; - } catch (\Exception) { + } catch (\Exception $exception) { + $this->lastException = $exception; return false; } } + + public function getLastException(): ?\Throwable + { + return $this->lastException; + } }
Provide individual status information
Each provider can be extended to return individual status information. This is possible once the
StatusInformationAwareMonitoringProvider
interface is implemented within a concrete provider.
namespace My\Vendor\Monitoring\Provider; -use CPSIT\Monitoring\Provider\MonitoringProvider; +use CPSIT\Monitoring\Provider\StatusInformationAwareMonitoringProvider; use My\Vendor\Service\ApiService; -final class ApiMonitoringProvider implements MonitoringProvider +final class ApiMonitoringProvider implements StatusInformationAwareMonitoringProvider { public function __construct( private readonly ApiService $apiService, ) {} public function getName(): string { return 'api'; } public function isHealthy(): bool { try { $response = $this->apiService->request('/health', 'HEAD'); return $response->getStatusCode() < 400; } catch (\Exception) { return false; } } + + /** + * @return array<string, string> + */ + public function getStatusInformation(): array + { + $lastProcessDate = $this->apiService->getLastProcessDate(); + + return [ + 'last_process_date' => $lastProcessDate->format(\DateTimeInterface::RFC2822), + ]; + } }
Middleware
Additionally, a middleware MonitoringMiddleware
exists.
It can be used to make health checks available using the middleware stack of a web application. A set
of monitoring providers needs to be provided when constructing the middleware. This can be best
achieved with a PSR-11 service container, e.g. within a Symfony or Symfony-based application.
In case the monitoring result is healthy, a 200 OK
response will be returned, otherwise the response
is 424 Failed Dependency
. All responses are in JSON format and contain the serialized monitoring result.
If an internal error occurs (such as failed JSON encoding), the response is 500 Internal Server Error
and the response body contains the error message in JSON format.
Validators
The middleware acts only on valid requests. The decision as to whether a request is valid is in the hands
of so called validators. Each validator must implement the
Validator
interface.
The package already provides the RouteValidator
. It can be
configured to allow only requests to a given route, which is /monitor/health
by default. In case this
route is matched, the request is valid and therefore the monitoring process will be triggered.
Authorizers
Requests handled by the provided middleware can be secured using a list of authorizers. Authorizers
are classes which implement the Authorizer
interface.
Each authorizer must implement the isAuthorized()
method. In case any authorizer returns true
, the
request will be processed as is. If no authorizer is able to give the appropriate authorization, a
401 Unauthorized
response will be returned.
Authorizers can be prioritized by defining an explicit priority using the getPriority()
method.
Authorizers with higher priority will be executed first.
Dependency injection
The package already provides a ready-made container configuration for dependency injection based on Symfony dependency injection. This is particularly helpful if several monitoring providers are to be automatically configured on the middleware.
For this purpose, it is necessary to install the following packages via Composer:
composer require symfony/config symfony/dependency-injection symfony/yaml
Note
The dependency injection configuration is an optional component of this package. Therefore, required Composer packages are not explicitly required, but only suggested. You must install them by your own.
The next step is to load the configuration into the container. For this, the package provides a
helper class ServiceConfigurator
:
use CPSIT\Monitoring\DependencyInjection\ServiceConfigurator; use Symfony\Component\DependencyInjection\ContainerBuilder; return static function (ContainerBuilder $container): void { ServiceConfigurator::configure($container); };
All classes that are configured in the service container and implement the
MonitoringProvider
interface are now automatically tagged with monitoring.provider
. The
MonitoringProviderCompilerPass
then takes care of the autoconfiguration of all tagged monitoring providers at the
MonitoringMiddleware
class.
🧑💻 Contributing
Please have a look at CONTRIBUTING.md
.
⭐ License
This project is licensed under GNU General Public License 3.0 (or later).