Apache::ParseLog - Object-oriented Perl extension for parsing Apache log files


Apache-ParseLog documentation Contained in the Apache-ParseLog distribution.

Index


Code Index:

NAME

Top

Apache::ParseLog - Object-oriented Perl extension for parsing Apache log files

SYNOPSIS

Top

    use Apache::ParseLog;
    $base = new Apache::ParseLog();
    $transferlog = $base->getTransferLog();
    %dailytransferredbytes = $transferlog->bytebydate();
    ...

DESCRIPTION

Top

Apache::ParseLog provides an easy way to parse the Apache log files, using object-oriented constructs. The data obtained using this module are generic enough that it is flexible to use the data for your own applications, such as CGI, simple text-only report generater, feeding RDBMS, data for Perl/Tk-based GUI application, etc.

FEATURES

Top

1

Easy and Portable Log-Parsing Methods

Because all of the work (parsing logs, constructing regex, matching and assigning to variables, etc.) is done inside this module, you can easily create log reports (unless your logs need intense scrutiny). Read on this manpage as well as the "EXAMPLES" section to see how easy it is to create log reports with this module.

Also, this module does not require C compiler, and it can (should) run on any platforms supported by Perl.

2

Support for LogFormat/CustomLog

The Apache Web Server 1.3.x's new LogForamt/CustomLog feature (with mod_log_config) is supported.

The log format specified with Apache's LogFormat directive in the httpd.conf file will be parsed and the regular expressions will be created dynamically inside this module, so re-writing your existing code will be minimal when the log format is changed.

3

Reports on Unique Visitor Counts

Tranditionally, the hit count is calculated based on the number of files requested by visitors (the simplest is the the total number of lines of the log file calculated as the "total hit").

As such, the hit count obviously can be misleading in the sense of "how many visitors have actually visited my site?", especially if the pages of your site contain many images (because each image is counted as one hit).

Apache::ParseLog provides the methods to obtain such traditional data, because those data also are very important for monitoring your web site's activities. However, this module also provides the methods to obtain the unique visitor counts, i.e., the actual number of "people" (well, IP or hostname) who visited your site, by date, time, and date and time.

See the "LOG OBJECT METHODS" for details about those methods.

4

Pre-Compiled Regex

The new pre-compiled regex feature introduced by Perl 5.005 is used (if you have the version installed on your machine).

For the pre-compiled regex and the new quote-like assignment operator (qr), see perlop(1) and perlre(1) manpages.

CONSTRUCTOR

Top

To construct an Apache::ParseLog object,new() method is available just like other modules.

The new() constructor returns an Apache::ParseLog base object with which to obtain basic server information as well as to construct log objects.

New Method

new([$path_to_httpd_conf[, $virtual_host]]);

With the new() method, an Apache::ParseLog object can be created in three different ways.

1

$base = new Apache::ParseLog();

This first method creates an empty object, which means that the fields of the object are undefined (undef); i.e., the object does not know what the server name is, where the log files are, etc. It is useful when you need to parse log files that are not created on the local Apache server (e.g., the log files FTP'd from elsewhere).

You have to use the config() method (see below) to call any other methods.

2

$base = new Apache::ParseLog($httpd_conf);

This is the second way to create an object with necessary information extracted from the $httpd_conf. $httpd_conf is a scalar string containing the absolute path to the httpd.conf file; e.g.,

    $httpd_conf = "/usr/local/httpd/conf/httpd.conf";

This method tries to extract the information from $httpd_conf, specified by the following Apache directives: ServerName, Port, ServerAdmin, TransferLog, ErrorLog, AgentLog, RefererLog, and any user-defined CustomLog along with LogFormat.

If any of the directives cannot be found or commented out in the $httpd_conf, then the field(s) for that directive(s) will be empty (undef), and corresponding methods that use the particular fields return an empty string when called, or error out (for log object methods, refer to the section below).

3

$base = new Apache::ParseLog($httpd_conf, $virtual_host);

This method creates an object just like the second method, but for the VirtualHost specified by $virtual_host only. The Apache directives and rules not specified within the <VitualHost xxx> and </VirtualHost> tags are parsed from the "regular" server section in the httpd.conf file.

Note that the $httpd_conf must be specified in order to create an object for the $virtual_host.

BASE OBJECT METHODS

Top

This section describes the methods available for the base object created by the new() construct described above.

Unless the object is created with an empty argument, the Apache::ParseLog module parses the basic information configured in the httpd.conf file (as passed as the first argument). The object uses the information to construct the log object.

The available methods are (return values are in parentheses):

    $base->config([%fields]); # (object)
    $base->version(); # (scalar)
    $base->serverroot(); # (scalar)
    $base->servername(); # (scalar)
    $base->httpport(); # (scalar)
    $base->serveradmin(); # (scalar)
    $base->transferlog(); # (scalar)
    $base->errorlog(); # (scalar)
    $base->agentlog(); # (scalar)
    $base->refererlog(); # (scalar)
    $base->customlog(); # (array)
    $base->customlogLocation($name); # (scalar)
    $base->customlogExists($name); # (scalar boolean, 1 or 0)
    $base->customlogFormat($name); # (scalar)
    $base->getTransferLog(); # (object)
    $base->getErrorLog(); # (object)
    $base->getRefererLog(); # (object)
    $base->getAgentLog(); # (object)
    $base->getCustomLog(); # (object)

LOG OBJECT METHODS

Top

This section describes the methods available for the log object created by any of the following base object methods: getTransferLog(), getErrorLog(), getRereferLog(), getAgentLog(), and getCustomLog($log_nickname).

This section is devided into six subsections, each of which describes the available methods for a certain log object.

Note that all the methods for TransferLog, RefererLog, and AgentLog can be used for the object created with getCustomLog($name).

TransferLog/CustomLog Methods

The following methods are available for the TransferLog object (created by getTransferLog() method), as well as the CustomLog object that logs appropriate arguments to the corresponding LogFormat.

ErrorLog Methods

Until the Apache version 1.2.x, each error log entry was just an error, meaning that there was no distinction between "real" errors (e.g., File Not Found, malfunctioning CGI, etc.) and non-significant errors (e.g., kill -1 the httpd processes, etc.).

Starting from the version 1.3.x, the Apache httpd logs the "type" of each error log entry, namely "error", "notice" and "warn".

If you use Apache 1.2.x, the errorbyxxx(), noticebyxxx(), and warnbyxxx() should not be used, because those methods for that are for 1.3.x only will merely return an empty hash. The allbyxxx() methods will return desired results.

The following methods are available for the ErrorLog object (created by getErrorLog() method).

RefererLog/CustomLog Methods

The following methods are available for the RefererLog object (created by getRefererLog() method), as well as the CustomLog object that logs %{Referer}i to the corresponding LogFormat.

AgentLog/CustomLog Methods

This subsection describes the methods available for the AgentLog object (created by getAgentLog() method), as well as the CustomLog object that logs %{User-agent}i to the corresponding LogFormat.

CustomLog Methods

This subsection describes the methods available only for the CustomLog object. See each method for what Apache directive is used for each returned result.

Special Method

The special method described below, getMethods(), can be used with any of the log objects to extract the methods available for the calling object.

MISCELLANEOUS

Top

This section describes some miscellaneous methods that might be useful.

Exported Methods

This subsection describes exported methods provided by the Apache::ParseLog module. (For the information about exported methods, see Exporter(3).)

Note that those exported modules can be used (called) just like local (main package) subroutines.

EXAMPLES

Top

The most basic, easiest way to create reports is presented as an example in the getMethods() section above, but the format of the output is pretty crude and less user-friendly.

Shown below are some other examples to use Apache::ParseLog.

Example 1: Basic Report

The example code below checks the TransferLog and ErrorLog generated by the Apache 1.2.x, and prints the reports to STDOUT. (To run this code, all you have to do is to change the $conf value.)

    #!/usr/local/bin/perl
    $|++;
    use Apache::ParseLog;

    $conf = "/usr/local/httpd/conf/httpd.conf"; 
    $base = new Apache::ParseLog($conf);

    print "TransferLog Report\n\n";
    $transferlog = $base->getTransferLog();

    %hit = $transferlog->hit();
    %hitbydate = $transferlog->hitbydate();
    print "Total Hit Counts: ", $hit{'Total'}, "\n";
    foreach (sort keys %hitbydate) {
        print "$_:\t$hitbydate{$_}\n"; # <date>: <hit counts>
    }
    $hitaverage = int($hit{'Total'} / scalar(keys %hitbydate));
    print "Average Daily Hits: $hitaverage\n\n";

    %byte = $transferlog->byte();
    %bytebydate = $transferlog->bytebydate();
    print "Total Bytes Transferred: ", $byte{'Total'}, "\n";
    foreach (sort keys %bytebydate) {
        print "$_:\t$bytebydate{$_}\n"; # <date>: <bytes transferred>
    }
    $byteaverage = int($byte{'Total'} / scalar(keys %bytebydate));
    print "Average Daily Bytes Transferred: $byteaverage\n\n";

    %visitorbydate = $transferlog->visitorbydate();
    %host = $transferlog->host();
    print "Total Unique Visitors: ", scalar(keys %host), "\n";
    foreach (sort keys %visitorbydate) {
        print "$_:\t$visitorbydate{$_}\n"; # <date: <visitor counts>
    }
    $visitoraverage = int(scalar(keys %host) / scalar(keys %visitorbydate));
    print "Average Daily Unique Visitors: $visitoraverage\n\n";

    print "ErrorLog Report\n\n";
    $errorlog = $base->getErrorLog();

    %count = $errorlog->count();
    %allbydate = $errorlog->allbydate();
    print "Total Errors: ", $count{'Total'}, "\n";
    foreach (sort keys %allbydate) {
        print "$_:\t$allbydate{$_}\n"; # <date>: <error counts>
    }
    $erroraverage = int($count{'Total'} / scalar(keys %allbydate));
    print "Average Daily Errors: $erroraverage\n\n";

    exit;

Example 2: Referer Report

The RefererLog (or CustomLog with referer logged) contains the referer for every single file requested. It means that everytime a page that contains 10 images is requested, 11 lines are added to the RefererLog, one line for the actual referer (where the visitor comes from), and the other 10 lines for the images with the just refererd page containing the 10 images as the referer, which is probably a little too much more than what you want to know.

The example code below checks the CustomLog that contains referer, (among other things), and reports the names of the referer sites that are not the local server itself.

    #!/usr/local/bin/perl
    $|++;
    use Apache::ParseLog;

    $conf = "/usr/local/httpd/conf/httpd.conf"; 
    $base = new Apache::ParseLog($conf);

    $localserver = $base->servername();

    $log = $base->getCustomLog("combined");
    %referer = $log->referer();
    @sortedkeys = sortHashByValue(%referer);

    print "External Referers Report\n";
    foreach (@sortedkeys) {
        print "$_:\t$referer{$_}\n" unless m/$localserver/i or m/^\-/;
    }

    exit;

Example 3: Access-Controlled User Report

Let's suppose that you have a directory tree on your site that is access-controlled by .htaccess or the like, and you want to check how frequently the section is used by the users.

    #!/usr/local/bin/perl
    $|++;
    use Apache::ParseLog;

    $conf = "/usr/local/httpd/conf/httpd.conf";
    $base = new Apache::ParseLog($conf);

    $log = $base->getCustomLog("common");
    %user = $log->user();

    print "Users Report\n";
    foreach (sort keys %user) {
        print "$_:\t$user{$_}\n" unless m/^-$/;
    }

    exit;

SEE ALSO

Top

perl(1), perlop(1), perlre(1), Exporter(3)

BUGS

Top

The reports on lesser-known browsers returned from the AgentLog methods are not always informative.

The data returned from the referer() method for RefererLog may be irrelvant if the referred files are not accessed via HTTP (i.e., the referer does not start with "http://" string).

If the base object is created with the $virtualhost specified, unless the ServerAdmin and ServerName are specified within the <VirtualHost xxx> ... </VirtualHost>, those values specified in the global section of the httpd.conf are not shared with the $virtualhost.

TO DO

Top

Increase the performance (speed).

VERSION

Top

Apache::ParseLog 1.01 (10/01/1998).

AUTHOR

Top

Apache::ParseLog was written and is maintained by Akira Hangai (akira@discover-net.net)

For the bug reports, comments, suggestions, etc., please email me.

COPYRIGHT

Top

DISCLAIMER

Top

This package is distributed in the hope that it will be useful for many web administrators/webmasters who are too busy to write their own programs to analyze the Apache log files. However, this package is so distributed WITHOUT ANY WARRANTY in that any use of the data generated by this package must be used at the user's own discretion, and the author shall not be held accountable for any results from the use of this package.


Apache-ParseLog documentation Contained in the Apache-ParseLog distribution.
#######################################################################
# Apache::ParseLog
#
#    Module to parse and process the Apache log files
#
#    See the manpage for the usage
#    
#    Written by Akira Hangai
#    For comments, suggestions, opinions, etc., 
#    email to: akira@discover-net.net
#
#    Copyright 1998 by Akira Hangai. All rights reserved. 
#    This program is free software; You can redistribute it and/or 
#    modify it under the same terms as Perl itself. 
#######################################################################
package Apache::ParseLog;

require 5.004;
use Carp;
use vars qw($VERSION);
$VERSION = "1.02";

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(countryByCode statusByCode sortHashByValue);
@EXPORT_OK = qw(countryByCode statusByCode sortHashByValue);

#######################################################################
# Local variables
local($HOST, $LOGIN, $DATETIME, $REQUEST, $OSTATUS, $LSTATUS, $BYTE, $FILENAME, $ADDR, $PORT, $PROC, $SEC, $URL, $HOSTNAME, $REFERER, $UAGENT);

my(%COUNTRY_BY_CODE) = map { 
    chomp; 
    my(@line) = split(/:/); 
    $line[0] => $line[1] 
} <DATA>;

my(%STATUS_BY_CODE) = (
    100 => "Continue",
    101 => "Switching Protocols",
    200 => "OK",
    201 => "Created",
    202 => "Accepted",
    203 => "Non-Authoritative Information",
    204 => "No Content",
    205 => "Reset Content",
    206 => "Partial Content",
    300 => "Multiple Choices",
    301 => "Moved Permanently",
    302 => "Moved Temporarily",
    303 => "See Other",
    304 => "Not Modified",
    305 => "Use Proxy",
    400 => "Bad Request",
    401 => "Unauthorized",
    402 => "Payment Required",
    403 => "Forbidden",
    404 => "Not Found",
    405 => "Method Not Allowed",
    406 => "Not Acceptable",
    407 => "Proxy Authentication Required",
    408 => "Request Time-out",
    409 => "Conflict",
    410 => "Gone",
    411 => "Length Required",
    412 => "Precondition Failed",
    413 => "Request Entity Too Large",
    414 => "Request-URI Too Large",
    415 => "Unsupported Media Type",
    500 => "Internal Server Error",
    501 => "Not Implemented",
    502 => "Bad Gateway",
    503 => "Service Unavailable",
    504 => "Gateway Time-out",
    505 => "HTTP Version not supported",
);

my(%M2N) = (
    'Jan' => '01',
    'Feb' => '02',
    'Mar' => '03',
    'Apr' => '04',
    'May' => '05',
    'Jun' => '06',
    'Jul' => '07',
    'Aug' => '08',
    'Sep' => '09',
    'Oct' => '10',
    'Nov' => '11',
    'Dec' => '12',
);

#######################################################################
# Constructor
#######################################################################
#######################################################################
# new([$path_to_httpd.conf]); returns ParseLog object
sub new {
    my($package) = shift;
    my($httpd_conf) = shift;
    my($virtual_host) = shift;
    my(%ARG);
    my($METHOD) = "Apache::ParseLog::new";
    if (defined($httpd_conf)) { 
        unless (-e $httpd_conf) {
            croak "$METHOD: $httpd_conf does not exist. Exiting...";
        } else {
            %ARG = populate($httpd_conf, $virtual_host);
        }
    }
    return config($package, %ARG);
}

#######################################################################
# BASE OBJECT METHODS
#######################################################################

#######################################################################
# config($ParseLog, %arg); returns ParseLog object
sub config {
    my($this, %arg) = @_;
    my($root, $servername, $port, $admin, $transfer, $error, $agent, $referer, %customlog);
    $serverroot = $arg{'serverroot'} || $this->{'serverroot'};
    $servername = $arg{'servername'} || $this->{'servername'};
    $httpport = $arg{'httpport'} || $this->{'httpport'};
    $serveradmin = $arg{'serveradmin'} || $this->{'serveradmin'};
    $transferlog = $arg{'transferlog'} || $this->{'transferlog'};
    $errorlog = $arg{'errorlog'} || $this->{'errorlog'};
    $agentlog = $arg{'agentlog'} || $this->{'agentlog'};
    $refererlog = $arg{'refererlog'} || $this->{'refererlog'};
    $customlog = $arg{'customlog'} || $this->{'customlog'};
    return bless {
        'serverroot'  => $serverroot,
        'servername'  => $servername,
        'httpport'    => $httpport,
        'serveradmin' => $serveradmin,
        'transferlog' => $transferlog,
        'errorlog'    => $errorlog,
        'agentlog'    => $agentlog,
        'refererlog'  => $refererlog,
        'customlog'   => $customlog,
    }
}

#######################################################################
# populate($path_to_httpd.conf[, $virtualHost]); return %arg; private
sub populate {
    my($conf) = shift;
    my($virtualhost) = shift;
    my($VIRTUAL) = ($virtualhost ? 1 : 0);
    my(%arg, $fh, $line, $serverroot, $servername, $httpport, $serveradmin, $transferlog, $errorlog, $agentlog, $refererlog, $customlog, @nickname, %location, %format);
    $fh = openFile($conf);
    while(defined($line = <$fh>)) {
        chomp($line);
        if ($line =~ /^ServerRoot\s+(.+)$/) { 
            $serverroot = $1;
        } elsif ($line =~ /^Port\s+(.+)$/) { 
            $httpport = $1;
        } elsif ($line =~ /^LogFormat\s+"(.+)"\s+(\w+)$/) {
            $format{$2} = $1;
        }
        unless ($VIRTUAL) {        # check only when $VIRTUAL == 0
            if ($line =~ /^ServerName\s+(.+)$/) { 
                $servername = $1;
            } elsif ($line =~ /^ServerAdmin\s+(.+)$/) { 
                   $serveradmin = $1;
            } elsif ($line =~ /^TransferLog\s+(.+)$/) {
                $transferlog = $1; 
            } elsif ($line =~ /^ErrorLog\s+(.+)$/) {
                $errorlog =  $1;
            } elsif ($line =~ /^AgentLog\s+(.+)$/) {
                $agentlog = $1;
            } elsif ($line =~ /^RefererLog\s+(.+)$/) {
                $refererlog = $1;
            } elsif ($line =~ /^CustomLog\s+(.+)\s+(\w+)$/) {
                my($loc) = $1;
                push(@nickname, $2);
                if ($loc =~ m#\|#) { undef $loc }
                elsif ($loc !~ m#^/#) { $loc = "$serverroot/$loc" }
                $location{$2} = $loc;
            } 
        }
        if (defined($virtualhost)) {
            if ($line =~ m#^<VirtualHost\s+(.+)?>#) {
                if ($1 =~ /$virtualhost/i) {   # if matches, 0
                    $VIRTUAL = 0;
                }
            } elsif ($line =~ m#^</VirtualHost>#) {
                $VIRTUAL = 1;                  # if matches, 0
            }
        } else {
            if ($line =~ m#^<VirtualHost#) {
                $VIRTUAL = 1;
            } elsif ($line =~ m#^</VirtualHost>#) {
                $VIRTUAL = 0;
            }
        }
    }
    close($fh);
    $arg{'serverroot'} = $serverroot || undef;
    $arg{'servername'} = $servername || undef;
    $arg{'httpport'} = $httpport || undef;
    $arg{'serveradmin'} = $serveradmin || undef;
    if ($transferlog) {
        if ($transferlog !~ m#^/#) { 
            $transferlog = "$serverroot/$transferlog" 
        } elsif ($transferlog =~ m#\|#) { 
            undef $transferlog 
        }
    }
    $arg{'transferlog'} = $transferlog;
    if ($errorlog) {
        if ($errorlog !~ m#^/#) { 
            $errorlog = "$serverroot/$errorlog" 
        } elsif ($errorlog =~ m#\|#) { 
            undef $errorlog 
        }
    }
    $arg{'errorlog'} = $errorlog;
    if ($agentlog) {
        if ($agentlog !~ m#^/#) { 
            $agentlog = "$serverroot/$agentlog" 
        } elsif ($agentlog =~ m#\|#) { 
            undef $agentlog 
        }
    }
    $arg{'agentlog'} = $agentlog;
    if ($refererlog) {
        if ($refererlog !~ m#^/#) { 
            $refererlog = "$serverroot/$refererlog" 
        } elsif ($refererlog =~ m#\|#) { 
            undef $refererlog 
        }
    }
    $arg{'refererlog'} = $refererlog;
    $customlog = {
        'nickname' => [ @nickname ],
        'format'   => { %format },
        'location' => { %location },
    };
    $arg{'customlog'} = $customlog;
    return %arg;
}

#######################################################################
# serverroot(); returns $serverroot
sub serverroot {
    my($this) = shift;
    return $this->{'serverroot'} || undef;
}

#######################################################################
# servername(); returns $servername
sub servername {
    my($this) = shift;
    return $this->{'servername'} || undef;
}

#######################################################################
# httpport(); returns $port
sub httpport {
    my($this) = shift;
    return $this->{'httpport'} || undef;
}

#######################################################################
# serveradmin(); returns $serveradmin
sub serveradmin {
    my($this) = shift;
    return $this->{'serveradmin'} || undef;
}

#######################################################################
# transferlog(); returns $transferlog
sub transferlog {
    my($this) = shift;
    return $this->{'transferlog'} || undef;
}

#######################################################################
# errorlog(); returns $errorlog
sub errorlog {
    my($this) = shift;
    return $this->{'errorlog'} || undef;
}

#######################################################################
# agentlog(); returns $agentlog
sub agentlog {
    my($this) = shift;
    return $this->{'agentlog'} || undef;
}

#######################################################################
# refererlog(); returns $refererlog
sub refererlog {
    my($this) = shift;
    return $this->{'refererlog'} || undef;
}

#######################################################################
# customlog(); returns @customlog
sub customlog {
    my($this) = shift;
    return @{ ${$this->{'customlog'}}{'nickname'} };
}

#######################################################################
# customlogDefined($customlog_nickname); returns 1 or 0; private
sub customlogDefined {
    my($this) = shift;
    my($nickname) = shift;
    my($switch) = 0;
    my(@logs) = customlog($this);
    foreach (@logs) { 
        if ($_ eq $nickname) {
            $switch++;
            last;
        }
    }
    return $switch;
}

#######################################################################
# customlogLocation($customlog_nickname); returns path to the log
sub customlogLocation {
    my($this) = shift;
    my($nickname) = shift;
    my($root) = serverroot($this);
    if (customlogDefined($this, $nickname)) {
        return ${ ${$this->{'customlog'}}{'location'} }{$nickname};
    } else {
        return undef;
    }
}

#######################################################################
# customlogExists($customlog_nickname); returns 1 or 0
sub customlogExists {
    my($this) = shift;
    my($nickname) = shift;
    my($switch) = 0;
    $switch++ if -e customlogLocation($this, $nickname);
    return $switch;
}

#######################################################################
# customlogFormat($customlog_nickname); returns the format
sub customlogFormat {
    my($this) = shift;
    my($nickname) = shift;
    if (customlogExists($this, $nickname)) {
        return ${ ${$this->{'customlog'}}{'format'} }{$nickname};
    } else {
        return undef;
    }
}

#######################################################################
# getTransferLog(); returns a blessed ref object for TransferLog 
sub getTransferLog {
    my($this) = shift;
    my($METHOD) = "Apache::ParseLog::getTransferLog";
    my($logfile) = $this->{'transferlog'};
    croak "$METHOD: $logfile does not exist. Exiting " unless -e $logfile;
    my($FORMAT) = '(\\S+)\\s(\\S+)\\s(\\S+)\\s\\[(\\d{2}/\\w+/\\d{4}\\:\\d{2}\\:\\d{2}\\:\\d{2}\\s+.+?)\\]\\s\\"(\\w+\\s\\S+\\s\\w+\\/\\d+\\.\\d+)\\"\\s(\\d+)\\s(\\d+|-)';
    my(@elements) = qw/HOST LOGIN USER DATETIME REQUEST LSTATUS BYTE/;
    return scanLog($this, $logfile, $FORMAT, @elements);
}

#######################################################################
# getRefererLog(); returns a blessed ref object for RefererLog
sub getRefererLog {
    my($this) = shift;
    my($METHOD) = "Apache::ParseLog::getRefererLog";
    my($logfile) = $this->{'refererlog'};
    croak "$METHOD: $logfile does not exist. Exiting " unless -e $logfile;
    my($FORMAT) = '(\\S+)\\s\\-\\>\\s(\\S+)';
    my(@elements) = qw/REFERER URL/;
    return scanLog($this, $logfile, $FORMAT, @elements);
}

#######################################################################
# getAgentLog(); returns a blessed ref object for AgentLog
sub getAgentLog {
    my($this) = shift;
    my($METHOD) = "Apache::ParseLog::getAgentLog";
    my($logfile) = $this->{'agentlog'};
    croak "$METHOD: $logfile does not exist. Exiting " unless -e $logfile;
    my($FORMAT) = '(.+)';
    my(@elements) = qw/UAGENT/;
    return scanLog($this, $logfile, $FORMAT, @elements);
}

#######################################################################
# getErrorLog(); returns a blessed ref object for ErrorLog
sub getErrorLog {
    my($this) = shift;
    my($METHOD) = "Apache::ParseLog::getErrorLog";
    my($logfile) = $this->{'errorlog'};
    croak "$METHOD: $logfile does not exist. Exiting " unless -e $logfile;
    my($FORMAT) = '\\[(?:\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\S+)\\s+(\\d{4})\\]\\s+(\\[(\\w+)\\])?';
    my($regex) = $FORMAT;
    my($line, %count,  %allbydate, %allbytime, %allbydatetime, %allmessage, %errorbydate, %errorbytime, %errorbydatetime, %errormessage, %noticebydate, %noticebytime, %noticebydatetime, %noticemessage, %warnbydate, %warnbytime, %warnbydatetime, %warnmessage);
    my($fh) = openFile($logfile);
    while (defined($line = <$fh>)) {
        chomp($line);
        if ($line =~ m/^\[/) {
            $line =~ m#$regex#;
            my($m) = $M2N{$1};
            my($d) = $2;
            my($time) = $3;
            my($y) = $4;
            my($keyword) = $6;
            $d = "0" . $d if $d =~ m/^\d$/;
            my($date) = "$m/$d/$y";
            $time =~ s/:.+$//;
            my($datetime) = "$date-$time";
            $line =~ s/^.+\]\s+//;
            if ($keyword eq "error") {
                $count{'error'}++;
                $errorbydate{$date}++;
                $errorbytime{$time}++;
                $errorbydatetime{$datetime}++;
                $errormessage{$line}++;
            } elsif ($keyword eq "notice") {
                $count{'notice'}++;
                $noticebydate{$date}++;
                $noticebytime{$time}++;
                $noticebydatetime{$datetime}++;
                $noticemessage{$line}++;
            } elsif ($keyword eq "warn") {
                $count{'warn'}++;
                $warnbydate{$date}++;
                $warnbytime{$time}++;
                $warnbydatetime{$datetime}++;
                $warnmessage{$line}++;
            }
            $allbydate{$date}++;
            $allbytime{$time}++;
            $allbydatetime{$datetime}++;
            $allmessage{$line}++ if $line;
            $count{'dated'}++;
        } else {
            $count{'nodate'}++;
            $allmessage{$line}++ if $line;
        }
        $count{'Total'}++;
    }
    close($fh);
    my(@methods) = qw/count allbydate allbytime allbydatetime allmessage errorbydate errorbytime errorbydatetime errormessage noticebydate noticebytime noticebydatetime noticemessage warnbydate warnbytime warnbydatetime warnmessage/;
    return bless {
        'allbydate'             => { %allbydate },
        'allbytime'             => { %allbytime },
        'allbydatetime'         => { %allbydatetime },
        'allmessage'            => { %allmessage },
        'errorbydate'           => { %errorbydate },
        'errorbytime'           => { %errorbytime },
        'errorbydatetime'       => { %errorbydatetime },
        'errormessage'          => { %errormessage },
        'noticebydate'          => { %noticebydate },
        'noticebytime'          => { %noticebytime },
        'noticebydatetime'      => { %noticebydatetime },
        'noticemessage'         => { %noticemessage },
        'warnbydate'            => { %warnbydate },
        'warnbytime'            => { %warnbytime },
        'warnbydatetime'        => { %warnbydatetime },
        'warnmessage'           => { %warnmessage },
        'count'                 => { %count },
        'methods'               => [ @methods ],
    };
}

#######################################################################
# getCustomLog($nickname); returns $customlog object
sub getCustomLog {
    my($this) = shift;
    my($nickname) = shift;
    my($METHOD) = "Apache::ParseLog::getCustomLog";
    croak "$METHOD: $nickname does not exist, Exiting " unless customlogExists($this, $nickname);
    my($logfile) = customlogLocation($this, $nickname);
    croak "$METHOD: $logfile does not exist. Exiting " unless -e $logfile;
    # Variables preparation
    my($host_rx) = '(\\S+)';            # %h (host, visitor IP/hostname)
    my($log_rx) = '(\\S+)';             # %l (login, login name)
    my($user_rx) = '(\\S+)';            # %u (user, user name)
    my($time_rx) = '\\[(\\d{2}/\\w+/\\d{4}\\:\\d{2}\\:\\d{2}\\:\\d{2}\\s+.+?)\\]';                                     # %t (datetime, date/time)
    my($req_rx) = '(\\w+\\s\\S+\\s\\w+\\/\\d+\\.\\d+)';
                                        # %r (request, method, file, proto)
    my($ost_rx) = '(\\d+)';             # %s (ostatus, original status)
    my($lst_rx) = '(\\d+)';             # %>s (lstatus, last status)
    my($byte_rx) = '(\\d+|-)';          # %b (byte, bytes)
    my($file_rx) = '(\\S+)';            # %f (filename, filename)
    my($addr_rx) = '(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})'; 
                                        # %a (addr, IP)
    my($port_rx) = '(\\d+)';            # %p (port, port)
    my($proc_rx) = '(\\d+)';            # %p (proc, proc ID)
    my($sec_rx) = '(\\d+)';             # %T (sec, time in sec)
    my($url_rx) = '(\\S+)';             # %U (url, URL)
    my($hname_rx) = '(\\S+)';           # %v (hostname, hostname)
    my($referer_rx) = '(\\S+)';         # %{Referer}i (referer, referer)
    my($uagent_rx) = '(.+?)';           # %{User-agent}i (uagent, browser)
    my($space) = '\\s';                 # white space
    my($FORMAT) = customlogFormat($this, $nickname);
    my($temp) = $FORMAT . " "; # add the last space for the $temp regex below
    my(@regex, @elements);
    # Create the pre-compiled regex sting dynamically here
    while ((length($temp)) > 0) {
        $temp =~ s/^(\\?\")?(.+?)(\\?\")?\s+//;
        my($match) = $2;
        if ($2 eq '%h') { 
            push(@regex, $host_rx);    push(@elements, 'HOST');
        } elsif ($2 eq '%l') { 
            push(@regex, $log_rx);     push(@elements, 'LOGIN');
        } elsif ($2 eq '%u') { 
            push(@regex, $user_rx);    push(@elements, 'USER');
        } elsif ($2 eq '%t') { 
            push(@regex, $time_rx);    push(@elements, 'DATETIME');
        } elsif ($2 eq '%r') { 
            push(@regex, $req_rx);     push(@elements, 'REQUEST');
        } elsif ($2 eq '%s') { 
            push(@regex, $ost_rx);     push(@elements, 'OSTATUS');
        } elsif ($2 eq '%>s') { 
            push(@regex, $lst_rx);     push(@elements, 'LSTATUS');
        } elsif ($2 eq '%b') { 
            push(@regex, $byte_rx);    push(@elements, 'BYTE');
        } elsif ($2 eq '%f') { 
            push(@regex, $file_rx);    push(@elements, 'FILENAME');
        } elsif ($2 eq '%a') { 
            push(@regex, $addr_rx);    push(@elements, 'ADDR'); 
        } elsif ($2 eq '%p') { 
            push(@regex, $port_rx);    push(@elements, 'PORT');
        } elsif ($2 eq '%P') { 
            push(@regex, $proc_rx);    push(@elements, 'PROC');
        } elsif ($2 eq '%T') { 
            push(@regex, $sec_rx);     push(@elements, 'SEC');
        } elsif ($2 eq '%U') { 
            push(@regex, $url_rx);     push(@elements, 'URL');
        } elsif ($2 eq '%v') { 
            push(@regex, $hname_rx);   push(@elements, 'HOSTNAME');
        } elsif ($2 =~ /Referer/i) { 
            push(@regex, $referer_rx); push(@elements, 'REFERER');
        } elsif ($2 =~ /User-agent/i) { 
            push(@regex, $uagent_rx);  push(@elements, 'UAGENT'); 
        } else { 
            my($unknown) = $2; 
            $unknown =~ s|(\W)|\\$1|g;
            push(@regex, $unknown);
        }
        $FORMAT =~ s/$match/$regex[$#regex]/;
    }
    $FORMAT =~ s/\s/$space/g;
    # Parse the log finally
    return scanLog($this, $logfile, $FORMAT, @elements);
}

#######################################################################
# scanLog($path_to_logfile, $regex_format, @elments); 
# returns a blessed object; private
sub scanLog {
    my($this) = shift;    # package
    my($logfile) = shift; # path to the log
    my($FORMAT) = shift;   # regex
    my(@elements) = @_;   # array containing what's in the regex
    # create an array containing the uc name of placeholders
    my($hostswitch) = 1;    # off with host defined
    my($visitorswitch) = 0; # on with host defined
    my($visitordone) = 0;   # on with visitor added to @methods
    my($dtswitch) = 0;      # on with datetime defined
    my($dtbyteswitch) = 0;  # on with datetime or byte defined
    my($fileswitch) = 0;    # on with filename or request or url defined
    my(@METHODS) = map { 
        if ((m"^HOST") && ($hostswitch)) {
            $hostswitch--;
            $visitorswitch++;
            if (($dtswitch) && (! $visitordone)) {
                ($_, "TOPDOMAIN", "SECDOMAIN", "VISITORBYDATE", "VISITORBYTTME", "VISITORBYDATETIME")
            } else {
                ($_, "TOPDOMAIN", "SECDOMAIN")
            }
        } elsif (((m"DATETIME") || (m"BYTE")) && (! $dtbyteswitch)) {
            $dtbyteswitch++;
            if (m"DATETIME") {
                $dtswitch++; 
                if ($visitorswitch) {
                    $visitorswitch = 0;
                    $visitordone++;
                    ("VISITORBYDATE", "VISITORBYTIME", "VISITORBYDATETIME", "HITBYDATE", "HITBYTIME", "HITBYDATETIME") 
                } else {
                    ("HITBYDATE", "HITBYTIME", "HITBYDATETIME")
                }
            } else {
                $_
            }
        } elsif (((m"DATETIME") || (m"BYTE")) && ($dtbyteswitch)) {
            if (m"DATETIME") { 
                $dtswitch++;
                if ($visitorswitch) {
                    $visitorswitch = 0;
                    $visitordone++;
                    ("VISITORBYDATE", "VISITORBYTIME", "VISITORBYDATETIME", "HITBYDATE", "HITBYTIME", "HITBYDATETIME", "BYTEBYDATE", "BYTEBYTIME", "BYTEBYDATETIME") 
                } else {
                    ("HITBYDATE", "HITBYTIME", "HITBYDATETIME", "BYTEBYDATE", "BYTEBYTIME", "BYTEBYDATETIME") 
                }
            } else { 
                ($_, "BYTEBYDATE", "BYTEBYTIME", "BYTEBYDATETIME") 
            }
        } elsif (m"REQUEST") {
            $fileswitch++;
            ("METHOD", "FILE", "QUERYSTRING", "PROTO")
        } elsif ((m"FILENAME") || (m"URL")) {
            $fileswitch++;
            $_
        } elsif ((m"SEC") && ($fileswitch)) {
            $_
        } elsif (m"UAGENT") {
            ($_, "UAVERSION", "BROWSER", "PLATFORM", "BROWSERBYOS")
        } elsif (m"REFERER") {
            ($_, "REFERERDETAIL")
        } else { 
            $_ 
        }
    } @elements;
    push(@METHODS, "HIT");  # the hit => { %hit } is always there
    @METHODS = map { lc } @METHODS;
    ### reports placeholders
    my(%host);               # hosts (visitors)
    my(%topdomain);          # top domains
    my(%secdomain);          # secondary domains
    my(%login);              # logins
    my(%user);               # users
    my(%visitorbydate);      # unique visitors (hosts) by date
    my(%visitorbytime);      # unique visitors by time
    my(%visitorbydatetime);  # unique visitors by date/time
    my(%hitbydate);          # hits by date
    my(%hitbytime);          # hits by time
    my(%hitbydatetime);      # hits by date/time
    my(%method);             # methods (get, post, etc.)
    my(%file);               # files
    my(%querystring);        # Query String
    my(%proto);              # protos (HTTP/1.0, etc.)
    my(%ostatus);            # original status (..)
    my(%lstatus);            # last status (use with %STATUS_BY_CODE)
    my(%byte);               # Bytes transferred (* containts one key "total")
    my(%bytebydate);         # bytes by date
    my(%bytebytime);         # bytes by time
    my(%bytebydatetime);     # bytes by date/time
    my(%filename);           # filenames (= files)
    my(%addr);               # IPs (=~ hosts)
    my(%port);               # ports
    my(%proc);               # procs
    my(%sec);                # seconds (time in sec)
    my(%url);                # URLs (=~ files)
    my(%hostname);           # hostnames (=~ hosts)
    my(%referer);            # referer (site only)
    my(%refererdetail);      # referer (detail)
    my(%uagent);             # agents
    my(%uaversion);          # uagent w/ versions (Mozilla/4.04, Slurp/2.0)
    my(%browser);            # browsers w/ version
    my(%platform);           # platforms only
    my(%browserbyos);        # browsers w/ platforms
    my(%hit);                # Total number of hits (lines)
    ### Routine
    if ((scalar(@elements) == 1) && ($elements[0] eq "UAGENT")) { 
        $FORMAT =~ s#\?## 
    }
    my($regex) = $FORMAT;
    my($line);
    my($fh) = openFile($logfile);
    while (defined($line = <$fh>)) { 
        chomp($line);
        $line =~ m#$regex#;
        # Scan each match
        for ($i = 0; $i < scalar(@elements); $i++) {
            my($mi) = $i + 1;          # index for match; $1, $2,...
            ${$elements[$i]} = ${$mi}; # assign the back ref
        }
        my($date, $time, $method, $file, $proto);
        ### create reports ###
        { # HOST RELATED BLOCK
            # HOST
            $host{$HOST}++ if $HOST;
            # HOSTNAME
            $hostname{$HOSTNAME}++ if $HOSTNAME;
            my($domain) = ($HOST ? $HOST : $HOSTNAME);
            # (TOP|SEC)DOMAIN
            if ($domain) {
                if ($domain !~ /^\d{1,3}(?:\.\d{1,3}){3}$/) {
                    if ($domain =~ m/\.([A-Za-z0-9\-]+\.)(\w+)$/) {
                        my($secdomain) = $1;
                        my($topdomain) = $2;
                        $topdomain{$topdomain}++;
                        $secdomain = $secdomain . $topdomain;
                        $secdomain{$secdomain}++;
                    } else {
                        $topdomain{$domain}++;
                        $secdomain{$domain}++;
                    }
                } else {
                    $topdomain{'unknown'}++;
                    $secdomain{'unknown'}++;
                }
            }
        }
        # LOGIN
        $login{$LOGIN}++ if $LOGIN;
        # USER
        $user{$USER}++ if $USER;
        # DATETIME
        if ($DATETIME) {
            $DATETIME =~ m#^(\d+)/(\w+)/(\d+)\:(\d{2})\:\d{2}\:\d{2}#;
            my($d) = ($1 =~ /^\d$/ ? '0' . $1 : $1);
            my($m) = $M2N{$2};
            my($y) = $3;
            my($t) = $4;
            my($date) = "$m/$d/$y";
            $hitbydate{$date}++;
            $hitbytime{$t}++;
            my($dt) = "$date-$t";
            $hitbydatetime{$dt}++;
            if (($BYTE) && ($BYTE =~ m#^\d+$#)) {
                $bytebydate{$date} += $BYTE;
                $bytebytime{$t} += $BYTE;
                $bytebydatetime{$dt} += $BYTE;
            }
            my($visitor);
            if ($HOST) { $visitor = $HOST } 
            elsif ($HOSTNAME) { $visitor = $HOSTNAME } 
            elsif ($ADDR) { $visitor = $HOSTNAME }
            ${$date}{$visitor}++;
            ${$t}{$visitor}++;
            ${$dt}{$visitor}++;
        }
        # STATUS
        if ($OSTATUS) {
            my($key) = "$OSTATUS $STATUS_BY_CODE{$OSTATUS}";
            $ostatus{$key}++;
        }
        if ($LSTATUS) {
            my($key) = "$LSTATUS $STATUS_BY_CODE{$LSTATUS}";
            $lstatus{$key}++;
        }
        # FILENAME
        $filename{$FILENAME}++ if $FILENAME;
        # ADDR
        $addr{$ADDR}++ if $ADDR;
        # PORT
        $port{$PORT}++ if $PORT;
        # PROC
        $proc{$PROC}++ if $PROC;
        { # BEGIN FILE RELATED BLOCK
            my($FILE);
            # REQUEST
            if ($REQUEST) {
                $REQUEST =~ m#^(\w+)\s(\S+)\s(\S+)$#;
                my($method) = $1;
                my($file) = $2;
                my($proto) = $3;
                $method{$method}++;
                if ($file =~ m#\?(.+)$#) {
                    $querystring{$1}++;    # query string
                }
                $file =~ s#\?.+$##;        # trim query_string
                $file =~ s#/\./#/#g;         # same-dir duplicates
                $file =~ s#/\s+?/\.\./#/#g;  # same-upper-dir duplicates
                $file{$file}++;
                $proto{$proto}++;
                $FILE = $file;   
            }
            # URL
            if ($URL) {
                $url{$URL}++;
                $FILE = $URL;
            }
            # FILE
            $FILE = $FILENAME unless $FILE;
            # SEC
            SEC: if ($SEC) {
                last SEC unless $FILE;
                if (exists($sec{$FILE})) {
                    $sec{$FILE} = "$SEC" if $SEC > $sec{$FILE};
                } else {
                    $sec{$FILE} = "$SEC";
                }
            }
            # BYTE
            if (($BYTE) && ($BYTE =~ m#^\d+$#)) {
                $byte{'Total'} += $BYTE;
                last unless $FILE;
                $FILE =~ m#\.(\w+)$#;
                if ($1) {
                    $byte{$1} += $BYTE;
                    $hit{$1}++;
                } else {
                    $byte{'OtherTypes'} += $BYTE;
                    $hit{'OtherTypes'}++;
                }
            }
            # REFERER
            if ($REFERER) {
                my($refered);
                if ($URL) { $refered = $URL }
                elsif ($FILE) { $refered = $FILE }
                elsif ($FILENAME) { $refered = $FILENAME }
                my($ref) = (($refered) ? "$REFERER -> $refered" : $REFERER);
                $refererdetail{$ref}++;
                if ($REFERER =~ m#http://(\S+?)[/?]#) { $referer{$1}++ }
                elsif ($REFERER =~ m#^-$#) { $referer{'bookmark'}++ }
                else { $referer{'unknown'}++ }
            }
        } # END FILE RELATED BLOCK 
        # UAGENT
        if ($UAGENT) {
            $uagent{$UAGENT}++;
            $UAGENT =~ m#^(\S+)\s*(.+)?$#;
            my($parser) = $1;
            my($rest) = $2;
            $uaversion{$parser}++ if $parser;
            my($browser);
            if (($UAGENT =~ m/^Mozilla/)
            && (($rest =~ m/(Webtv.+?)[;)]/) 
            || ($rest =~ m/(AOL.+?)[;)]/) 
            || ($rest =~ m/(MSN.+?)[;)]/) 
            || ($rest =~ m/(MSIE.+?)[;)]/))) {
                $browser = $1;
                $browser{$1}++;
            } elsif (($UAGENT =~ m/Mozilla/) && ($rest =~ m/compatible\;\s+(.+?)[;)]/)) {
                $browser = $1;
                $browser{$1}++;
            } else {
                $browser = $parser;
                $browser{$browser}++;
            }
            my($plat);
            if ($rest =~ m/(Win.+?)[;)]/) {
                $plat = $1;
                $platform{$1}++;
            } elsif ($rest =~ m/(Mac.+?)[;)]/) {
                $plat = $1;
                $platform{$1}++;
            } elsif ($rest =~ m/X11\;\s+.+?\;\s+(.+?)[;)]/) {
                $plat = $1;
                $platform{$1}++;
            } else {
                $plat = $rest;
                $plat =~ s#(?:\(|\)|\;)##g;
                $platform{$plat}++;
            }
            my($bandp) = "$browser ($plat)";
            $browserbyos{$bandp}++;
        }
        # hit
        $hit{'Total'}++;
    }
    close($fh);
    # Construct %visitorxxx hashes
    %visitorbydate = map { $_ => scalar(keys %{$_}), } keys %hitbydate;
    %visitorbytime = map { $_ => scalar(keys %{$_}), } keys %hitbytime;
    %visitorbydatetime = map { $_ => scalar(keys %{$_}), } keys %hitbydatetime;
    return bless { 
        'host'               => { %host },
        'topdomain'          => { %topdomain },
        'secdomain'          => { %secdomain },
        'login'              => { %login },
        'user'               => { %user },
        'hitbydate'          => { %hitbydate },
        'hitbytime'          => { %hitbytime },
        'hitbydatetime'      => { %hitbydatetime },
        'visitorbydate'      => { %visitorbydate },
        'visitorbytime'      => { %visitorbytime },
        'visitorbydatetime'  => { %visitorbydatetime },
        'method'             => { %method },
        'file'               => { %file },
        'querystring'        => { %querystring },
        'proto'              => { %proto },
        'ostatus'            => { %ostatus },
        'lstatus'            => { %lstatus },
        'byte'               => { %byte },
        'bytebydate'         => { %bytebydate },
        'bytebytime'         => { %bytebytime },
        'bytebydatetime'     => { %bytebydatetime },
        'filename'           => { %filename },
        'addr'               => { %addr },
        'port'               => { %port },
        'proc'               => { %proc },
        'sec'                => { %sec },
        'url'                => { %url },
        'hostname'           => { %hostname },
        'referer'            => { %referer },
        'refererdetail'      => { %refererdetail },
        'uagent'             => { %uagent },
        'uaversion'          => { %uaversion },
        'browser'            => { %browser },
        'platform'           => { %platform },
        'browserbyos'        => { %browserbyos },
        'hit'                => { %hit },
        'methods'            => [ @METHODS ],
    };
}

#######################################################################
# LOG OBJECT METHODS
#######################################################################
#######################################################################
# TRANSFERLOG METHODS
######################################################################
# hit(); returns %hit
sub hit {
    my($this) = shift;
    return %{($this->{'hit'} || undef)};
}

#######################################################################
# host(); returns %host
sub host {
    my($this) = shift;
    my(%host) = %{($this->{'host'} || undef)};
    return %host;
}

#######################################################################
# topdomain(); returns %topdomain
sub topdomain {
    my($this) = shift;
    return %{($this->{'topdomain'} || undef)};
}

######################################################################
# secdomain(); returns %secdomain
sub secdomain {
    my($this) = shift;
    return %{($this->{'secdomain'} || undef)};
}

######################################################################
# login(); returns %login
sub login {
    my($this) = shift;
    return %{($this->{'login'} || undef)};
}

######################################################################
# user(); returns %user
sub user {
    my($this) = shift;
    return %{($this->{'user'} || undef)};
}

######################################################################
# hitbydate(); returns %hitbydate
sub hitbydate {
    my($this) = shift;
    return %{($this->{'hitbydate'} || undef)};
}

######################################################################
# hitbytime(); returns %hitbytime
sub hitbytime {
    my($this) = shift;
    return %{($this->{'hitbytime'} || undef)};
}

######################################################################
# hitbydatetime(); returns %hitbydatetime
sub hitbydatetime {
    my($this) = shift;
    return %{($this->{'hitbydatetime'} || undef)};
}

######################################################################
# visitorbydate(); returns %visitorbydate
sub visitorbydate {
    my($this) = shift;
    return %{($this->{'visitorbydate'} || undef)};
}

######################################################################
# visitorbytime(); returns %visitorbytime
sub visitorbytime {
    my($this) = shift;
    return %{($this->{'visitorbytime'} || undef)};
}

######################################################################
# visitorbydatetime(); returns %visitorbydatetime
sub visitorbydatetime {
    my($this) = shift;
    return %{($this->{'visitorbydatetime'} || undef)};
}

######################################################################
# method(); returns %method
sub method {
    my($this) = shift;
    return %{($this->{'method'} || undef)};
}

######################################################################
# file(); returns %file
sub file {
    my($this) = shift;
    return %{($this->{'file'} || undef)};
}

######################################################################
# querystring(); returns %querystring
sub querystring {
    my($this) = shift;
    return %{($this->{'querystring'} || undef)};
}

######################################################################
# proto(); returns %proto
sub proto {
    my($this) = shift;
    return %{($this->{'proto'} || undef)};
}

######################################################################
# lstatus(); returns %lstatus
sub lstatus {
    my($this) = shift;
    return %{($this->{'lstatus'} || undef)};
}

######################################################################
# byte(); returns %byte
sub byte {
    my($this) = shift;
    return %{($this->{'byte'} || undef)};
}

######################################################################
# bytebydate(); returns %bytebydate
sub bytebydate {
    my($this) = shift;
    return %{($this->{'bytebydate'} || undef)};
}

######################################################################
# bytebytime(); returns %bytebytime
sub bytebytime {
    my($this) = shift;
    return %{($this->{'bytebytime'} || undef)};
}

######################################################################
# bytebydatetime(); returns %bytebydatetime
sub bytebydatetime {
    my($this) = shift;
    return %{($this->{'bytebydatetime'} || undef)};
}

#######################################################################
# ERRORLOG METHODS
#######################################################################
# total(); returns $total;
sub count {
    my($this) = shift;
    return %{($this->{'count'} || undef)};
}

#######################################################################
# allbydate(); returns %allbydate;
sub allbydate {
    my($this) = shift;
    return %{($this->{'allbydate'} || undef)};
}

#######################################################################
# allbytime(); returns %allbytime;
sub allbytime {
    my($this) = shift;
    return %{($this->{'allbytime'} || undef)};
}

#######################################################################
# allbydatetime(); returns %allbydatetime;
sub allbydatetime {
    my($this) = shift;
    return %{($this->{'allbydatetime'} || undef)};
}

#######################################################################
# allmessage(); returns %allmessage;
sub allmessage {
    my($this) = shift;
    return %{($this->{'allmessage'} || undef)};
}

#######################################################################
# errorbydate(); returns %errorbydate;
sub errorbydate {
    my($this) = shift;
    return %{($this->{'errorbydate'} || undef)};
}

#######################################################################
# errorbytime(); returns %errorbytime;
sub errorbytime {
    my($this) = shift;
    return %{($this->{'errorbytime'} || undef)};
}

#######################################################################
# errorbydatetime(); returns %errorbydatetime;
sub errorbydatetime {
    my($this) = shift;
    return %{($this->{'errorbydatetime'} || undef)};
}

#######################################################################
# errormessage(); returns %errormessage;
sub errormessage {
    my($this) = shift;
    return %{($this->{'errormessage'} || undef)};
}

#######################################################################
# noticebydate(); returns %noticebydate;
sub noticebydate {
    my($this) = shift;
    return %{($this->{'noticebydate'} || undef)};
}

#######################################################################
# noticebytime(); returns %noticebytime;
sub noticebytime {
    my($this) = shift;
    return %{($this->{'noticebytime'} || undef)};
}

#######################################################################
# noticebydatetime(); returns %noticebydatetime;
sub noticebydatetime {
    my($this) = shift;
    return %{($this->{'noticebydatetime'} || undef)};
}

#######################################################################
# noticemessage(); returns %noticemessage;
sub noticemessage {
    my($this) = shift;
    return %{($this->{'noticemessage'} || undef)};
}

#######################################################################
# warnbydate(); returns %warnbydate;
sub warnbydate {
    my($this) = shift;
    return %{($this->{'warnbydate'} || undef)};
}

#######################################################################
# warnbytime(); returns %warnbytime;
sub warnbytime {
    my($this) = shift;
    return %{($this->{'warnbytime'} || undef)};
}

#######################################################################
# warnbydatetime(); returns %warnbydatetime;
sub warnbydatetime {
    my($this) = shift;
    return %{($this->{'warnbydatetime'} || undef)};
}

#######################################################################
# warnmessage(); returns %warnmessage;
sub warnmessage {
    my($this) = shift;
    return %{($this->{'warnmessage'} || undef)};
}

#######################################################################
# REFERERLOG METHODS
######################################################################
# referer(); returns %referer
sub referer {
    my($this) = shift;
    return %{($this->{'referer'} || undef)};

}

#######################################################################
# refererdetail(); returns %refererdetail
sub refererdetail {
    my($this) = shift;
    return %{($this->{'refererdetail'} || undef)};
}

#######################################################################
# AGENTLOG METHODS
######################################################################
# uagent(); returns %uagent
sub uagent {
    my($this) = shift;
    return %{($this->{'uagent'} || undef)};
}

######################################################################
# uaversion(); returns %uaversion
sub uaversion {
    my($this) = shift;
    return %{($this->{'uaversion'} || undef)};
}

######################################################################
# browser(); returns %browser
sub browser {
    my($this) = shift;
    return %{($this->{'browser'} || undef)};
}

######################################################################
# platform(); returns %platform
sub platform {
    my($this) = shift;
    return %{($this->{'platform'} || undef)};
}

######################################################################
# browserbyos(); returns %browserbyos
sub browserbyos {
    my($this) = shift;
    return %{($this->{'browserbyos'} || undef)};
}

#######################################################################
# CUSTOMLOG METHODS
######################################################################
# addr(); returns %addr
sub addr {
    my($this) = shift;
    return %{($this->{'addr'} || undef)};
}

######################################################################
# filename(); returns %filename
sub filename {
    my($this) = shift;
    return %{($this->{'filename'} || undef)};
}

######################################################################
# hostname(); returns %hostname
sub hostname {
    my($this) = shift;
    return %{($this->{'hostname'} || undef)};
}

######################################################################
# ostatus(); returns %ostatus
sub ostatus {
    my($this) = shift;
    return %{($this->{'ostatus'} || undef)};
}

######################################################################
# port(); returns %port
sub port {
    my($this) = shift;
    return %{($this->{'port'} || undef)};
}

######################################################################
# proc(); returns %proc
sub proc {
    my($this) = shift;
    return %{($this->{'proc'} || undef)};
}

######################################################################
# sec(); returns %sec
sub sec {
    my($this) = shift;
    return %{($this->{'sec'} || undef)};
}

######################################################################
# url(); returns %url
sub url {
    my($this) = shift;
    return %{($this->{'url'} || undef)};
}

#######################################################################
# SPECIAL METHOD
#######################################################################
# getMethods(); returns @methods;
sub getMethods {
    my($this) = shift;
    return @{($this->{'methods'} || undef)};
}

#######################################################################
# MISCELLANEOUS
#######################################################################
#######################################################################
# version(); returns $VERSION
sub Version { $VERSION }

#######################################################################
# EXPORTED METHODS
#######################################################################
# countryByCode(); returns %COUNTRY_BY_CODE
sub countryByCode {
    return %COUNTRY_BY_CODE;
}

#######################################################################
# statusByCode(); returns %STATUS_BY_CODE
sub statusByCode {
    return %STATUS_BY_CODE;
}

#######################################################################
# sortHashByValue(); returns @sorted
sub sortHashByValue {
    my(%hash) = @_;
    return sort { $hash{$b} <=> $hash{$a} } keys %hash;
}

#######################################################################
# PRIVATE METHODS
#######################################################################

#######################################################################
# openFile($any_file); returns a filehandle
sub openFile {
    my($file) = shift;
    my($METHOD) = "Apache::ParseLog::openFile";
    local(*FH);
    open(FH, "<$file") or croak "$METHOD: Cannot open $file. Exiting ";
    return *FH;
}

#######################################################################
# ADDITIONAL DOCUMENTATION
#######################################################################
#######################################################################
# DATA 
#######################################################################
__DATA__
ad:Andorra
ae:United Arab Emirates
af:Afghanistan
ag:Antigua and Barbuda
ai:Anguilla
al:Albania
am:Armenia
an:Netherlands Antilles
ao:Angola
aq:Antarctica
ar:Argentina
as:American Samoa
at:Austria
au:Australia
aw:Aruba
az:Azerbaijan
ba:Bosnia and Herzegovina
bb:Barbados
bd:Bangladesh
be:Belgium
bf:Burkina Faso
bg:Bulgaria
bh:Bahrain
bi:Burundi
bj:Benin
bm:Bermuda
bn:Brunei Darussalam
bo:Bolivia
br:Brazil
bs:Bahamas
bt:Bhutan
bv:Bouvet Island
bw:Botswana
by:Belarus
bz:Belize
ca:Canada
cc:Cocos (Keeling) Islands
cf:Central African Republic
cg:Congo
ch:Switzerland
ci:Cote D'Ivoire (Ivory Coast)
ck:Cook Islands
cl:Chile
cm:Cameroon
cn:China
co:Colombia
cr:Costa Rica
cs:Czechoslovakia (former)
cu:Cuba
cv:Cape Verde
cx:Christmas Island
cy:Cyprus
cz:Czech Republic
de:Germany
dj:Djibouti
dk:Denmark
dm:Dominica
do:Dominican Republic
dz:Algeria
ec:Ecuador
ee:Estonia
eg:Egypt
eh:Western Sahara
er:Eritrea
es:Spain
et:Ethiopia
fi:Finland
fj:Fiji
fk:Falkland Islands (Malvinas)
fm:Micronesia
fo:Faroe Islands
fr:France
fx:France Metropolitan,
ga:Gabon
gb:Great Britain (UK)
gd:Grenada
ge:Georgia
gf:French Guiana
gh:Ghana
gi:Gibraltar
gl:Greenland
gm:Gambia
gn:Guinea
gp:Guadeloupe
gq:Equatorial Guinea
gr:Greece
gs:S. Georgia and S. Sandwich Isls.
gt:Guatemala
gu:Guam
gw:Guinea-Bissau
gy:Guyana
hk:Hong Kong
hm:Heard and McDonald Islands
hn:Honduras
hr:Croatia (Hrvatska)
ht:Haiti
hu:Hungary
id:Indonesia
ie:Ireland
il:Israel
in:India
io:British Indian Ocean Territory
iq:Iraq
ir:Iran
is:Iceland
it:Italy
jm:Jamaica
jo:Jordan
jp:Japan
ke:Kenya
kg:Kyrgyzstan
kh:Cambodia
ki:Kiribati
km:Comoros
kn:Saint Kitts and Nevis
kp:Korea (North)
kr:Korea (South)
kw:Kuwait
ky:Cayman Islands
kz:Kazakhstan
la:Laos
lb:Lebanon
lc:Saint Lucia
li:Liechtenstein
lk:Sri Lanka
lr:Liberia
ls:Lesotho
lt:Lithuania
lu:Luxembourg
lv:Latvia
ly:Libya
ma:Morocco
mc:Monaco
md:Moldova
mg:Madagascar
mh:Marshall Islands
mk:Macedonia
ml:Mali
mm:Myanmar
mn:Mongolia
mo:Macau
mp:Northern Mariana Islands
mq:Martinique
mr:Mauritania
ms:Montserrat
mt:Malta
mu:Mauritius
mv:Maldives
mw:Malawi
mx:Mexico
my:Malaysia
mz:Mozambique
na:Namibia
nc:New Caledonia
ne:Niger
nf:Norfolk Island
ng:Nigeria
ni:Nicaragua
nl:Netherlands
no:Norway
np:Nepal
nr:Nauru
nt:Neutral Zone
nu:Niue
nz:New Zealand (Aotearoa)
om:Oman
pa:Panama
pe:Peru
pf:French Polynesia
pg:Papua New Guinea
ph:Philippines
pk:Pakistan
pl:Poland
pm:St. Pierre and Miquelon
pn:Pitcairn
pr:Puerto Rico
pt:Portugal
pw:Palau
py:Paraguay
qa:Qatar
re:Reunion
ro:Romania
ru:Russian Federation
rw:Rwanda
sa:Saudi Arabia
sb:Solomon Islands
sc:Seychelles
sd:Sudan
se:Sweden
sg:Singapore
sh:St. Helena
si:Slovenia
sj:Svalbard and Jan Mayen Isls.
sk:Slovak Republic
sl:Sierra Leone
sm:San Marino
sn:Senegal
so:Somalia
sr:Suriname
st:Sao Tome and Principe
su:USSR (former)
sv:El Salvador
sy:Syria
sz:Swaziland
tc:Turks and Caicos Islands
td:Chad
tf:French Southern Territories
tg:Togo
th:Thailand
tj:Tajikistan
tk:Tokelau
tm:Turkmenistan
tn:Tunisia
to:Tonga
tp:East Timor
tr:Turkey
tt:Trinidad and Tobago
tv:Tuvalu
tw:Taiwan
tz:Tanzania
ua:Ukraine
ug:Uganda
uk:United Kingdom
um:US Minor Outlying Islands
us:United States
uy:Uruguay
uz:Uzbekistan
va:Vatican City State (Holy See)
vc:Saint Vincent and the Grenadines
ve:Venezuela
vg:Virgin Islands (British)
vi:Virgin Islands (U.S.)
vn:Viet Nam
vu:Vanuatu
wf:Wallis and Futuna Islands
ws:Samoa
ye:Yemen
yt:Mayotte
yu:Yugoslavia
za:South Africa
zm:Zambia
zr:Zaire
ZW:Zimbabwe 
com:US Commercial
edu:US Educational 
gov:US Government
int:International
mil:US Military
net:Network
org:Non-Profit Organization
arpa:Old style Arpanet
nato:NATO Field