diff --git a/doc/manual/src/SUMMARY.md b/doc/manual/src/SUMMARY.md index f0dc77a4..586c09aa 100644 --- a/doc/manual/src/SUMMARY.md +++ b/doc/manual/src/SUMMARY.md @@ -4,6 +4,7 @@ - [Installation](installation.md) - [Creating and Managing Projects](projects.md) - [Using the external API](api.md) +- [Monitoring Hydra](./monitoring/README.md) ----------- [About](about.md) [Hacking](hacking.md) diff --git a/doc/manual/src/monitoring/README.md b/doc/manual/src/monitoring/README.md new file mode 100644 index 00000000..65872352 --- /dev/null +++ b/doc/manual/src/monitoring/README.md @@ -0,0 +1,15 @@ +# Monitoring Hydra + +## Webserver + +The webserver exposes Prometheus metrics for the webserver itself at `/metrics`. + +## Queue Runner + +The queue runner's status is exposed at `/queue-runner-status`: + +```console +$ curl --header "Accept: application/json" http://localhost:63333/queue-runner-status +... JSON payload ... +``` + diff --git a/flake.nix b/flake.nix index 017e0b20..f53ed808 100644 --- a/flake.nix +++ b/flake.nix @@ -70,6 +70,21 @@ }; }; + CatalystPluginPrometheusTiny = final.buildPerlPackage { + pname = "Catalyst-Plugin-PrometheusTiny"; + version = "0.005"; + src = final.fetchurl { + url = "mirror://cpan/authors/id/S/SY/SYSPETE/Catalyst-Plugin-PrometheusTiny-0.005.tar.gz"; + sha256 = "a42ef09efdc3053899ae007c41220d3ed7207582cc86e491b4f534539c992c5a"; + }; + buildInputs = with final.perlPackages; [ HTTPMessage Plack SubOverride TestDeep ]; + propagatedBuildInputs = with final.perlPackages; [ CatalystRuntime Moose PrometheusTiny PrometheusTinyShared ]; + meta = { + description = "Prometheus metrics for Catalyst"; + license = with final.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + CryptArgon2 = final.perlPackages.buildPerlModule { pname = "Crypt-Argon2"; version = "0.010"; @@ -111,6 +126,20 @@ }; }; + DataRandom = final.buildPerlPackage { + pname = "Data-Random"; + version = "0.13"; + src = final.fetchurl { + url = "mirror://cpan/authors/id/B/BA/BAREFOOT/Data-Random-0.13.tar.gz"; + sha256 = "eb590184a8db28a7e49eab09e25f8650c33f1f668b6a472829de74a53256bfc0"; + }; + buildInputs = with final.perlPackages; [ FileShareDirInstall TestMockTime ]; + meta = { + description = "Perl module to generate random data"; + license = with final.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + DirSelf = final.buildPerlPackage { pname = "Dir-Self"; version = "0.11"; @@ -125,6 +154,51 @@ }; }; + HashSharedMem = final.perlPackages.buildPerlModule { + pname = "Hash-SharedMem"; + version = "0.005"; + src = final.fetchurl { + url = "mirror://cpan/authors/id/Z/ZE/ZEFRAM/Hash-SharedMem-0.005.tar.gz"; + sha256 = "324776808602f7bdc44adaa937895365454029a926fa611f321c9bf6b940bb5e"; + }; + buildInputs = with final.perlPackages; [ ScalarString ]; + meta = { + description = "Efficient shared mutable hash"; + license = with final.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + + PrometheusTiny = final.buildPerlPackage { + pname = "Prometheus-Tiny"; + version = "0.007"; + src = final.fetchurl { + url = "mirror://cpan/authors/id/R/RO/ROBN/Prometheus-Tiny-0.007.tar.gz"; + sha256 = "0ef8b226a2025cdde4df80129dd319aa29e884e653c17dc96f4823d985c028ec"; + }; + buildInputs = with final.perlPackages; [ HTTPMessage Plack TestException ]; + meta = { + homepage = "https://github.com/robn/Prometheus-Tiny"; + description = "A tiny Prometheus client"; + license = with final.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + + PrometheusTinyShared = final.buildPerlPackage { + pname = "Prometheus-Tiny-Shared"; + version = "0.023"; + src = final.fetchurl { + url = "mirror://cpan/authors/id/R/RO/ROBN/Prometheus-Tiny-Shared-0.023.tar.gz"; + sha256 = "7c2c72397be5d8e4839d1bf4033c1800f467f2509689673c6419df48794f2abe"; + }; + buildInputs = with final.perlPackages; [ DataRandom HTTPMessage Plack TestDifferences TestException ]; + propagatedBuildInputs = with final.perlPackages; [ HashSharedMem JSONXS PrometheusTiny ]; + meta = { + homepage = "https://github.com/robn/Prometheus-Tiny-Shared"; + description = "A tiny Prometheus client with a shared database behind it"; + license = with final.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + TieHashMethod = final.buildPerlPackage { pname = "Tie-Hash-Method"; version = "0.02"; @@ -299,6 +373,7 @@ CatalystPluginAccessLog CatalystPluginAuthorizationRoles CatalystPluginCaptcha + CatalystPluginPrometheusTiny CatalystPluginSessionStateCookie CatalystPluginSessionStoreFastMmap CatalystPluginStackTrace @@ -332,6 +407,7 @@ NetPrometheus NetStatsd PadWalker + PrometheusTinyShared Readonly SQLSplitStatement SetScalar diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm index 0ff86f11..d77c3dba 100644 --- a/src/lib/Hydra.pm +++ b/src/lib/Hydra.pm @@ -16,7 +16,8 @@ use Catalyst qw/ConfigLoader Session Session::Store::FastMmap Session::State::Cookie - Captcha/, + Captcha + PrometheusTiny/, '-Log=warn,fatal,error'; use CatalystX::RoleApplicator; use YAML qw(LoadFile); @@ -47,6 +48,9 @@ __PACKAGE__->config( file($ENV{'HYDRA_LDAP_CONFIG'}) ) : undef }, + 'Plugin::PrometheusTiny' => { + include_action_labels => 1, + }, 'Plugin::Static::Simple' => { send_etag => 1, expires => 3600 diff --git a/t/Controller/metrics.t b/t/Controller/metrics.t new file mode 100644 index 00000000..8915104d --- /dev/null +++ b/t/Controller/metrics.t @@ -0,0 +1,30 @@ +use feature 'unicode_strings'; +use strict; +use Setup; +use JSON qw(decode_json encode_json); + +my %ctx = test_init(); + +require Hydra::Schema; +require Hydra::Model::DB; +require Hydra::Helper::Nix; +use HTTP::Request::Common; + +use Test2::V0; +require Catalyst::Test; +Catalyst::Test->import('Hydra'); + +my $db = Hydra::Model::DB->new; +hydra_setup($db); + +request(GET '/'); +my $metrics = request(GET '/metrics'); +ok($metrics->is_success); + +like( + $metrics->content, + qr/http_requests_total\{action="index",code="200",controller="Hydra::Controller::Root",method="GET"\} 1/, + "Metrics are collected" +); + +done_testing;