Repository
Munin (contrib)
Last change
2018-08-02
Keywords
Language
Perl

apache_pipelogger

# Log vhost port method response_bytes response_time status <IfModule mod_log_config.c> CustomLog “|/usr/share/munin/apache_pipelogger” “%v %p %m %B %D %s” </IfModule>

#!/usr/bin/perl

=head1

# Log vhost port method response_bytes response_time status
<IfModule mod_log_config.c>
  CustomLog "|/usr/share/munin/apache_pipelogger" "%v %p %m %B %D %s"
</IfModule>

=cut
# write every n seconds to shared memory
local $nsec=7;
local $debug=undef;

use Storable qw(freeze thaw);
use List::Util qw(min max);
use IPC::ShareLite ':lock';
require Data::Dumper if $debug;
use Munin::Plugin;


local $share = IPC::ShareLite->new(
	-key     => 'mapl',
	-create  => 1,
	-destroy => 1,
	-exclusive => 0,
	-mode => '0666'
) or die $!;


local $SIG{'ALRM'}=\&periodic_write;
alarm $nsec;


# drop stored data on reload
local %temp=();

while (<STDIN>) {
 	my ($vhost,$port,$method,$bytes,$time,$status)=split(/\s/,$_);

	# sanity check
	next unless m/^([\d\w\.\-_]+\s){5}([\d\w\.\-_]+$)/;       # escaped "." and "-"

	# sitename to munin fieldname
	my $vpm=clean_fieldname($vhost);
	$temp{$vpm}{'label'}=$vhost;
	$temp{$vpm}{'label'}=~s/www\.//;

	# count all requests
	$temp{$vpm}{'requests'}++;

	if ($bytes) {
	 $bytes=~s/-/0/;
 	 # bytes transmitted
	 $temp{$vpm}{'bytes'}+=$bytes;

	 # max bytes
	 $temp{$vpm}{'max_bytes'}=max($temp{$vpm}{'max_bytes'},$bytes);

         # average bytes
         $temp{$vpm}{'avg_bytes'}=$temp{$vpm}{'bytes'}/$temp{$vpm}{'requests'} || 0 if ($bytes);
	}

	# count by status / error code
	$temp{$vpm}{"status"}{$status}++ if $status;

	if ($time) {
  	  # min/max execution time
  	  $temp{$vpm}{'max_time'}=max($temp{$vpm}{'max_time'},$time);

  	  # cumulative and average execution time
  	  $temp{$vpm}{'cml_time'}+=$time;

          # average time
          $temp{$vpm}{'avg_time'}=$temp{$vpm}{'cml_time'}/$temp{$vpm}{'requests'} || 0 if ($time);
        }
};

sub periodic_write {
        # begin transaction
        $share->lock(LOCK_EX);

        # get data (may be updated by other loggers too)
        my %old=eval{%{thaw($share->fetch)}};             # using eval to suppress thaw error on empty string at the first run

        foreach my $vpm (keys %temp){
                # merge values
                $old{$vpm}{'bytes'}+=$temp{$vpm}{'bytes'} if $temp{$vpm}{'bytes'};
                $old{$vpm}{'requests'}+=$temp{$vpm}{'requests'} if $temp{$vpm}{'requests'};
                $old{$vpm}{'time'}+=$temp{$vpm}{'time'} if $temp{$vpm}{'time'};
                $old{$vpm}{'label'}=$temp{$vpm}{'label'};
                $old{$vpm}{'avg_time'}=sprintf("%d",(($old{$vpm}{'avg_time'}+$temp{$vpm}{'avg_time'})/2)/1000);
                $old{$vpm}{'max_time'}=max($old{$vpm}{'max_time'},$temp{$vpm}{'max_time'})/1000;
                $old{$vpm}{'max_bytes'}=max($temp{$vpm}{'max_bytes'},$temp{$vpm}{'max_bytes'});
                $old{$vpm}{'avg_bytes'}=sprintf("%d",($old{$vpm}{'avg_bytes'}+$temp{$vpm}{'avg_bytes'})/2);

                # reset local counters
                foreach my $check (qw(requests bytes time cml_time max_bytes avg_bytes max_time avg_time)) {
                        $temp{$vpm}{$check}=0;
                }

                # reset status counts
                foreach my $val (keys %{$temp{$vpm}{'status'}}) {
                        $old{$vpm}{'status'}{$val}+=$temp{$vpm}{'status'}{$val};
                        $temp{$vpm}{'status'}{$val}=0;
                }

        };

        # save to shm
#        print Data::Dumper::Dumper(%old) if $debug;
        $share->store( freeze \%old );

        # end transaction
        $share->unlock;

        # parse/write every n seconds
        alarm $nsec;
}