#  You may distribute under the terms of either the GNU General Public License
#  or the Artistic License (the same terms as Perl itself)
#
#  (C) Paul Evans, 2016 -- leonerd@leonerd.org.uk

package Net::Prometheus;

use strict;
use warnings;

our $VERSION = '0.01';

use Carp;

use Net::Prometheus::Gauge;
use Net::Prometheus::Counter;
use Net::Prometheus::Summary;

=head1 NAME

C<Net::Prometheus> - export monitoring metrics for F<prometheus>

=head1 SYNOPSIS

   use Net::Prometheus;

   my $client = Net::Prometheus->new;

   my $counter = $client->new_counter(
      name => "requests",
      help => "Number of received requests",
   );

   sub handle_request
   {
      $counter->inc;
      ...
   }

   use Plack::Builder;

   builder {
      mount "/metrics" => $client->psgi_app;
      ...
   }

=head1 DESCRIPTION

This module provides the ability for a program to collect monitoring metrics
and export them to the F<prometheus.io> monitoring server.

As C<prometheus> will expect to collect the metrics by making an HTTP request,
facilities are provided to yield a L<PSGI> application that the containing
program can embed in its own structure to provide the results, or the
application can generate a plain-text result directly and serve them by its
own means.

=cut

=head1 CONSTRUCTOR

=cut

=head2 new

   $prometheus = Net::Prometheus->new;

Returns a new C<Net::Prometheus> instance.

=cut

sub new
{
   my $class = shift;

   return bless {
      collectors => {},
   }, $class;
}

=head1 METHODS

=cut

sub register
{
   my $self = shift;
   my ( $collector ) = @_;

   my $fullname = $collector->fullname;
   croak "Already have a collector called '$fullname'"
      if defined $self->{collectors}{$fullname};

   return $self->{collectors}{$fullname} = $collector;
}

sub unregister
{
   my $self = shift;
   my ( $collector ) = @_;

   my $fullname = $collector->fullname;
   delete $self->{collectors}{$fullname} or
      croak "No collector called '$fullname'";
}

=head2 new_gauge

   $gauge = $prometheus->new_gauge( %args )

Constructs a new L<Net::Prometheus::Gauge> using the arguments given and
registers it with the exporter. The newly-constructed gauge is returned.

=cut

sub new_gauge
{
   my $self = shift;
   my %args = @_;

   return $self->register( Net::Prometheus::Gauge->new( %args ) );
}

=head2 new_counter

   $counter = $prometheus->new_counter( %args )

Constructs a new L<Net::Prometheus::Counter> using the arguments given and
registers it with the exporter. The newly-constructed counter is returned.

=cut

sub new_counter
{
   my $self = shift;
   my %args = @_;

   return $self->register( Net::Prometheus::Counter->new( %args ) );
}

=head2 new_summary

   $summary = $prometheus->new_summary( %args )

Constructs a new L<Net::Prometheus::Summary> using the arguments given
and registers it with the exporter. The newly-constructed summary is returned.

=cut

sub new_summary
{
   my $self = shift;
   my %args = @_;

   return $self->register( Net::Prometheus::Summary->new( %args ) );
}

=head2 render

   $str = $prometheus->render

Returns a string in the Prometheus text exposition format containing the
current values of all the registered metrics.

=cut

sub render
{
   my $self = shift;

   my $collectors = $self->{collectors};

   return join "", map { $collectors->{$_}->render } sort keys %$collectors;
}

=head2 psgi_app

   $app = $prometheus->psgi_app

Returns a new L<PSGI> application as a C<CODE> reference. This application
will render the metrics in the Prometheus text exposition format, suitable for
scraping by the Prometheus collector.

This application will respond to any C<GET> request, and reject requests for
any other method.

=cut

sub psgi_app
{
   my $self = shift;

   return sub {
      my $env = shift;
      my $method = $env->{REQUEST_METHOD};

      $method eq "GET" or return [
         405,
         [ "Content-Type" => "text/plain" ],
         [ "Method $method not supported" ],
      ];

      return [
         200,
         [ "Content-Type" => "text/plain" ],
         [ $self->render ],
      ];
   };
}

=head1 AUTHOR

Paul Evans <leonerd@leonerd.org.uk>

=cut

0x55AA;
