#!/usr/bin/perl

use 5.010;

use strict;
use warnings;

my $logfile = '/var/log/httpd/access_log';

my $q = CGI->new();
say $q->header();
say $q->start_html( -title => "HTTP Log report" );

my ( %hour_requests, %status_requests, %type_requests, %url_requests );

my $bytes = analyze($logfile);
report($q, $bytes);

sub analyze
{
    my ($logfile) = @_;

    open my $log, '<', $logfile or die "Could not open log '$logfile': $!\n";

    my $bytes = 0;

    while ( my $line = <$log> )
    {
        my @fields = split /\s/, $line;

        # Make /about/ and /about/index.html the same URL.
        $fields[6] =~ s{/$}{/index.html};

        # Log successful requests by file type.  URLs without an extension
        # are assumed to be text files.
        if ( $fields[8] eq '200' ) {
            if ( $fields[6] =~ /\.([a-z]+)$/i ) {
                $type_requests{$1}++;
            }
            else {
                $type_requests{'txt'}++;
            }
        }

        # Log the hour of this request
        $fields[3] =~ /:(\d{2}):/;
        $hour_requests{$1}++;

        # Log the URL requested
        $url_requests{ $fields[6] }++;

        # Log status code
        $status_requests{ $fields[8] }++;

        # Log bytes, but only for results where byte count is non-zero
        $bytes += $fields[9] unless $fields[9] eq "-";
    }

    close $log;
    return $bytes;
}

sub report
{
    my $q = shift;

    for my $type ( $q->param('report') ) {
        my @report_args;

        given ($type) {
            when ('url')    { @report_args = ( "URL requests", %url_requests ) }
            when ('status') { @report_args = ( "Status code requests", %status_requests ) }
            when ('hour')   { @report_args = ( "Requests by hour", %hour_requests ) }
            when ('type')   { @report_args = ( "Requests by file type", %type_requests ) }
        }

        report_section( $q, @report_args );
    }
}

sub report_section {
    my $q                  = shift;
    return unless @_;

    my ( $header, %types ) = @_;
    my $number             = $q->param( 'number ');
    my @type_keys;

    if ($number eq 'ALL') {
        @type_keys = sort keys %types;
    }
    else {
        @type_keys = sort { $type{$b} <=> $type{$a} } keys %type;

        # truncate the list if we have too many results
        splice @type_keys, $number if @type_keys > $number;
    }

    # Begin a HTML table
    say "<table>";

    # Print a table row containing a header for the table
    say '<tr><th colspan="2">', $header, '</th></tr>';

    # Print a table row containing each item and its value
    for my $key (@type_keys) {
        say "<tr><td>$key</td><td>$type{$key}</td></tr>\n";
    }

    # Finish the table
    say '</table>';
}
