3259 lines
102 KiB
Perl
3259 lines
102 KiB
Perl
package SquidAnalyzer;
|
|
#------------------------------------------------------------------------------
|
|
# Project : Squid Log Analyzer
|
|
# Name : SquidAnalyzer.pm
|
|
# Language : Perl 5
|
|
# OS : All
|
|
# Copyright: Copyright (c) 2001-2012 Gilles Darold - All rights reserved.
|
|
# Licence : This program is free software; you can redistribute it
|
|
# and/or modify it under the same terms as Perl itself.
|
|
# Author : Gilles Darold, gilles _AT_ darold _DOT_ net
|
|
# Function : Main perl module for Squid Log Analyzer
|
|
# Usage : See documentation.
|
|
#------------------------------------------------------------------------------
|
|
use strict; # make things properly
|
|
|
|
BEGIN {
|
|
use Exporter();
|
|
use vars qw($VERSION $COPYRIGHT $AUTHOR @ISA @EXPORT $ZCAT_PROG $BZCAT_PROG);
|
|
use POSIX;
|
|
use IO::File;
|
|
|
|
# Set all internal variable
|
|
$VERSION = '5.0';
|
|
$COPYRIGHT = 'Copyright (c) 2001-2012 Gilles Darold - All rights reserved.';
|
|
$AUTHOR = "Gilles Darold - gilles _AT_ darold _DOT_ net";
|
|
|
|
@ISA = qw(Exporter);
|
|
@EXPORT = qw//;
|
|
|
|
$| = 1;
|
|
|
|
}
|
|
|
|
$ZCAT_PROG = "/bin/zcat";
|
|
$BZCAT_PROG = "/bin/bzcat";
|
|
|
|
# Default translation srings
|
|
my %Translate = (
|
|
'CharSet' => 'utf-8',
|
|
'01' => 'Jan',
|
|
'02' => 'Feb',
|
|
'03' => 'Mar',
|
|
'04' => 'Apr',
|
|
'05' => 'May',
|
|
'06' => 'Jun',
|
|
'07' => 'Jul',
|
|
'08' => 'Aug',
|
|
'09' => 'Sep',
|
|
'10' => 'Oct',
|
|
'11' => 'Nov',
|
|
'12' => 'Dec',
|
|
'KB' => 'Kilo bytes',
|
|
'MB' => 'Mega bytes',
|
|
'GB' => 'Giga bytes',
|
|
'Bytes' => 'Bytes',
|
|
'Total' => 'Total',
|
|
'Years' => 'Years',
|
|
'Users' => 'Users',
|
|
'Sites' => 'Sites',
|
|
'Cost' => 'Cost',
|
|
'Requests' => 'Requests',
|
|
'Megabytes' => 'Mega bytes',
|
|
'Months' => 'Months',
|
|
'Days' => 'Days',
|
|
'Hit' => 'Hit',
|
|
'Miss' => 'Miss',
|
|
'Domains' => 'Domains',
|
|
'Requests_graph' => 'Requests',
|
|
'Megabytes_graph' => 'Mega bytes',
|
|
'Months_graph' => 'Months',
|
|
'Days_graph' => 'Days',
|
|
'Hit_graph' => 'Hit',
|
|
'Miss_graph' => 'Miss',
|
|
'Total_graph' => 'Total',
|
|
'Domains_graph' => 'Domains',
|
|
'Users_help' => 'Total number of different users for this period',
|
|
'Sites_help' => 'Total number of different visited sites for this period',
|
|
'Domains_help' => 'Total number of different second level visited domain for this period',
|
|
'Hit_help' => 'Objects found in cache',
|
|
'Miss_help' => 'Objects not found in cache',
|
|
'Cost_help' => '1 Mega byte =',
|
|
'Generation' => 'Report generated on',
|
|
'Main_cache_title' => 'Cache Statistics',
|
|
'Cache_title' => 'Cache Statistics on',
|
|
'Stat_label' => 'Stat',
|
|
'Mime_link' => 'Mime Types',
|
|
'Network_link' => 'Networks',
|
|
'User_link' => 'Users',
|
|
'Top_url_link' => 'Top Urls',
|
|
'Top_domain_link' => 'Top Domains',
|
|
'Back_link' => 'Back',
|
|
'Graph_cache_hit_title' => '%s Requests statistics on',
|
|
'Graph_cache_byte_title' => '%s Mega Bytes statistics on',
|
|
'Hourly' => 'Hourly',
|
|
'Hours' => 'Hours',
|
|
'Daily' => 'Daily',
|
|
'Days' => 'Days',
|
|
'Monthly' => 'Monthly',
|
|
'Months' => 'Months',
|
|
'Mime_title' => 'Mime Type Statistics on',
|
|
'Mime_number' => 'Number of mime type',
|
|
'Network_title' => 'Network Statistics on',
|
|
'Network_number' => 'Number of network',
|
|
'Duration' => 'Duration',
|
|
'Time' => 'Time',
|
|
'Largest' => 'Largest',
|
|
'Url' => 'Url',
|
|
'User_title' => 'User Statistics on',
|
|
'User_number' => 'Number of user',
|
|
'Url_Hits_title' => 'Top %d Url hits on',
|
|
'Url_Bytes_title' => 'Top %d Url bytes on',
|
|
'Url_Duration_title' => 'Top %d Url duration on',
|
|
'Url_number' => 'Number of Url',
|
|
'Domain_Hits_title' => 'Top %d Domain hits on',
|
|
'Domain_Bytes_title' => 'Top %d Domain bytes on',
|
|
'Domain_Duration_title' => 'Top %d Domain duration on',
|
|
'Domain_number' => 'Number of domain',
|
|
'Domain_graph_hits_title' => 'Domain Hits Statistics on',
|
|
'Domain_graph_bytes_title' => 'Domain Bytes Statistiques on',
|
|
'First_visit' => 'First visit',
|
|
'Last_visit' => 'Last visit',
|
|
'Globals_Statistics' => 'Globals Statistics',
|
|
'Legend' => 'Legend',
|
|
'File_Generated' => 'File generated by',
|
|
'Up_link' => 'Up',
|
|
'Click_year_stat' => 'Click on year\'s statistics link for details',
|
|
'Mime_graph_hits_title' => 'Mime Type Hits Statistics on',
|
|
'Mime_graph_bytes_title' => 'Mime Type Bytes Statistiques on',
|
|
);
|
|
|
|
|
|
sub new
|
|
{
|
|
my ($class, $conf_file, $log_file) = @_;
|
|
|
|
# Construct the class
|
|
my $self = {};
|
|
bless $self, $class;
|
|
|
|
# Initialize all variables
|
|
$self->_init($conf_file, $log_file);
|
|
|
|
# Return the instance
|
|
return($self);
|
|
|
|
}
|
|
|
|
sub parseFile
|
|
{
|
|
my ($self) = @_;
|
|
|
|
return if ((!-f $self->{LogFile}) || (-z $self->{LogFile}));
|
|
|
|
# The log file format must be :
|
|
# time elapsed client code/status bytes method URL rfc931 peerstatus/peerhost type
|
|
# This is the default format of squid access log file.
|
|
|
|
# Open logfile
|
|
my $logfile = new IO::File;
|
|
if ($self->{LogFile} =~ /\.gz/) {
|
|
# Open a pipe to zcat program for compressed log
|
|
$logfile->open("$ZCAT_PROG $self->{LogFile} |") || die "ERROR: cannot read from pipe to $ZCAT_PROG $self->{LogFile}. $!\n";
|
|
} elsif ($self->{LogFile} =~ /\.bz2/) {
|
|
# Open a pipe to zcat program for compressed log
|
|
$logfile->open("$BZCAT_PROG $self->{LogFile} |") || die "ERROR: cannot read from pipe to $BZCAT_PROG $self->{LogFile}. $!\n";
|
|
} else {
|
|
$logfile->open($self->{LogFile}) || die "ERROR: Unable to open Squid access.log file $self->{LogFile}. $!\n";
|
|
}
|
|
|
|
my $line = '';
|
|
my $time = 0;
|
|
my $elapsed = 0;
|
|
my $client_ip = '';
|
|
my $client_name = '';
|
|
my $code = '';
|
|
my $bytes = 0;
|
|
my $method = '';
|
|
my $url = '';
|
|
my $login = '';
|
|
my $status = '';
|
|
my $mime_type = '';
|
|
|
|
my $line_count = 0;
|
|
my $line_processed_count = 0;
|
|
my $line_stored_count = 0;
|
|
# Read and parse each line of the access log file
|
|
while ($line = <$logfile>) {
|
|
chomp($line);
|
|
#logformat squid %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %un %Sh/%<A %mt
|
|
#logformat squidmime %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %un %Sh/%<A %mt [%>h] [%<h]
|
|
# The log format below are not supported
|
|
#logformat common %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st %Ss:%Sh
|
|
#logformat combined %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
|
|
# Parse log with format: time elapsed client code/status bytes method URL rfc931 peerstatus/peerhost mime_type
|
|
if ( $line =~ s#^(\d+\.\d{3})\s+(\d+)\s+([^\s]+)\s+([^\s]+)\s+(\d+)\s+([^\s]+)\s+## ) {
|
|
$time = $1 || 0;
|
|
$elapsed = $2 || 0;
|
|
$code = $4 || '';
|
|
$bytes = $5 || 0;
|
|
$method = $6 || '';
|
|
$client_ip = $3 || '';
|
|
|
|
# Go to last parsed date (incremental mode)
|
|
next if ($self->{history_time} && ($time <= $self->{history_time}));
|
|
|
|
# Register the last parsing time
|
|
$self->{end_time} = $time;
|
|
|
|
# Register the first parsing time
|
|
if (!$self->{begin_time}) {
|
|
$self->{begin_time} = $time;
|
|
print STDERR "START TIME: ", strftime("%a %b %e %H:%M:%S %Y", localtime($time)), "\n" if (!$self->{QuietMode});
|
|
}
|
|
# Only store (HIT|UNMODIFIED)/MISS status and peer CD_SIBLING_HIT/...
|
|
if ( ($code =~ m#(HIT|UNMODIFIED)/#) || ($self->{SiblingHit} && ($line =~ / CD_SIBLING_HIT/)) ) {
|
|
$code = 'HIT';
|
|
} elsif ($code =~ m#MISS|MODIFIED/#) {
|
|
$code = 'MISS';
|
|
} else {
|
|
next;
|
|
}
|
|
|
|
if ( $line =~ s#^(.*)\s+([^\s]+)\s+([^\s]+\/[^\s]+)\s+([^\s]+)\s*## ) {
|
|
$url = lc($1) || '';
|
|
$login = lc($2) || '';
|
|
$status = lc($3) || '';
|
|
$mime_type = lc($4) || '';
|
|
$mime_type = 'none' if (!$mime_type || ($mime_type eq '-'));
|
|
# Remove extra space character in username
|
|
$login =~ s/\%20//g;
|
|
|
|
my $id = $client_ip || '';
|
|
if ($login ne '-') {
|
|
$id = $login;
|
|
}
|
|
next if (!$id || !$bytes);
|
|
# check for client/user exclusion in old syntax
|
|
my $found = 0;
|
|
if (exists $self->{Exclude}{all}) {
|
|
foreach my $e (@{$self->{Exclude}{all}}) {
|
|
if ( ($client_ip =~ m#^$e$#i) || ($login =~ m#^$e$#i)) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
next if ($found);
|
|
}
|
|
# check for user exclusion
|
|
if (exists $self->{Exclude}{users}) {
|
|
foreach my $e (@{$self->{Exclude}{users}}) {
|
|
if ($login =~ m#^$e$#i) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
next if ($found);
|
|
}
|
|
# check for client exclusion
|
|
if (exists $self->{Exclude}{clients}) {
|
|
foreach my $e (@{$self->{Exclude}{clients}}) {
|
|
if ($client_ip =~ m#^$e$#i) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
next if ($found);
|
|
}
|
|
# check for URL exclusion
|
|
if (exists $self->{Exclude}{uris}) {
|
|
foreach my $e (@{$self->{Exclude}{uris}}) {
|
|
if ($url =~ m#^$e$#i) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
next if ($found);
|
|
}
|
|
# Anonymize all users
|
|
if ($self->{AnonymizeLogin} && ($client_ip ne $id)) {
|
|
if (!exists $self->{AnonymizedId}{$id}) {
|
|
$self->{AnonymizedId}{$id} = &anonymize_id();
|
|
}
|
|
$id = $self->{AnonymizedId}{$id};
|
|
}
|
|
# Now parse data and generate statistics
|
|
$self->_parseData($time, $elapsed, $client_ip, $code, $bytes, $url, $id, $mime_type);
|
|
$line_stored_count++;
|
|
}
|
|
$line_processed_count++;
|
|
}
|
|
$line_count++;
|
|
}
|
|
$logfile->close();
|
|
|
|
if (!$self->{last_year} && !$self->{last_month} && !$self->{last_day}) {
|
|
print STDERR "No new log registered...\n" if (!$self->{QuietMode});
|
|
} else {
|
|
# Save last parsed data
|
|
$self->_save_data("$self->{last_year}", "$self->{last_month}", "$self->{last_day}");
|
|
|
|
if (!$self->{QuietMode}) {
|
|
print STDERR "END TIME : ", strftime("%a %b %e %H:%M:%S %Y", localtime($self->{end_time})), "\n";
|
|
print "Read $line_count lines, matched $line_processed_count and found $line_stored_count new lines\n";
|
|
}
|
|
|
|
# Set the current start time into history file
|
|
if ($self->{end_time}) {
|
|
my $current = new IO::File;
|
|
$current->open(">$self->{Output}/SquidAnalyzer.current") or die "Error: Can't write to file $self->{Output}/SquidAnalyzer.current, $!\n";
|
|
print $current "$self->{end_time}";
|
|
$current->close;
|
|
}
|
|
|
|
# Compute month statistics
|
|
if (!$self->{QuietMode}) {
|
|
print STDERR "\nParsing ended, generating data files now...\n";
|
|
print STDERR "Compute and dump month statistics for $self->{first_year}/$self->{first_month} to $self->{last_year}/$self->{last_month}\n";
|
|
}
|
|
for my $date ("$self->{first_year}$self->{first_month}" .. "$self->{last_year}$self->{last_month}") {
|
|
$date =~ /^(\d{4})(\d{2})$/;
|
|
next if (($2 < 1) || ($2 > 12));
|
|
if (-d "$self->{Output}/$1/$2") {
|
|
$self->_save_data("$1", "$2");
|
|
}
|
|
}
|
|
|
|
# Compute year statistics
|
|
if (!$self->{QuietMode}) {
|
|
print STDERR "Compute and dump year statistics for $self->{first_year} to $self->{last_year}\n";
|
|
}
|
|
for my $year ($self->{first_year} .. $self->{last_year}) {
|
|
if (-d "$self->{Output}/$year") {
|
|
$self->_save_data($year);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sub _clear_stats
|
|
{
|
|
my $self = shift;
|
|
|
|
# Hashes to store user statistics
|
|
$self->{stat_user_hour} = ();
|
|
$self->{stat_user_day} = ();
|
|
$self->{stat_user_month} = ();
|
|
$self->{stat_usermax_hour} = ();
|
|
$self->{stat_usermax_day} = ();
|
|
$self->{stat_usermax_month} = ();
|
|
$self->{stat_user_url_hour} = ();
|
|
$self->{stat_user_url_day} = ();
|
|
$self->{stat_user_url_month} = ();
|
|
|
|
# Hashes to store network statistics
|
|
$self->{stat_network_hour} = ();
|
|
$self->{stat_network_day} = ();
|
|
$self->{stat_network_month} = ();
|
|
$self->{stat_netmax_hour} = ();
|
|
$self->{stat_netmax_day} = ();
|
|
$self->{stat_netmax_month} = ();
|
|
|
|
# Hashes to store user / network statistics
|
|
$self->{stat_netuser_hour} = ();
|
|
$self->{stat_netuser_day} = ();
|
|
$self->{stat_netuser_month} = ();
|
|
|
|
# Hashes to store cache status (hit/miss)
|
|
$self->{stat_code_hour} = ();
|
|
$self->{stat_code_day} = ();
|
|
$self->{stat_code_month} = ();
|
|
|
|
# Hashes to store mime type
|
|
$self->{stat_mime_type_hour} = ();
|
|
$self->{stat_mime_type_day} = ();
|
|
$self->{stat_mime_type_month} = ();
|
|
|
|
}
|
|
|
|
sub _init
|
|
{
|
|
my ($self, $conf_file, $log_file) = @_;
|
|
|
|
# Prevent for a call without instance
|
|
if (!ref($self)) {
|
|
print "ERROR - init : Unable to call init without an object instance.\n";
|
|
exit(0);
|
|
}
|
|
|
|
# Load configuration information
|
|
if (!$conf_file) {
|
|
if (-f '/etc/squidanalyzer.conf') {
|
|
$conf_file = '/etc/squidanalyzer.conf';
|
|
} elsif (-f 'squidanalyzer.conf') {
|
|
$conf_file = 'squidanalyzer.conf';
|
|
}
|
|
}
|
|
my %options = &parse_config($conf_file);
|
|
|
|
# Use squid log file given as command line parameter
|
|
$options{LogFile} = $log_file if ($log_file);
|
|
|
|
# Configuration options
|
|
$self->{MinPie} = $options{MinPie} || 2;
|
|
$self->{QuietMode} = $options{QuietMode} || 0;
|
|
$self->{UrlReport} = $options{UrlReport} || 0;
|
|
$self->{Output} = $options{Output} || '';
|
|
$self->{WebUrl} = $options{WebUrl} || '';
|
|
$self->{WebUrl} .= '/' if ($self->{WebUrl} && ($self->{WebUrl} !~ /\/$/));
|
|
$self->{DateFormat} = $options{DateFormat} || '%y-%m-%d';
|
|
$self->{Lang} = $options{Lang} || '';
|
|
$self->{AnonymizeLogin} = $options{AnonymizeLogin} || 0;
|
|
$self->{SiblingHit} = $options{SiblingHit} || 1;
|
|
$self->{ImgFormat} = $options{ImgFormat} || 'png';
|
|
$self->{Locale} = $options{Locale} || '';
|
|
if ($self->{Lang}) {
|
|
open(IN, "$self->{Lang}") or die "ERROR: can't open translation file $self->{Lang}, $!\n";
|
|
while (my $l = <IN>) {
|
|
chomp($l);
|
|
next if ($l =~ /^[\s\t]*#/);
|
|
next if (!$l);
|
|
my ($key, $str) = split(/\t+/, $l);
|
|
$Translate{$key} = $str;
|
|
}
|
|
close(IN);
|
|
}
|
|
if (!$self->{Output}) {
|
|
die "ERROR: 'Output' configuration option must be set.\n";
|
|
}
|
|
if (! -d $self->{Output}) {
|
|
die "ERROR: 'Output' dorectory $self->{Output} doesn't exists.\n";
|
|
}
|
|
$self->{LogFile} = $options{LogFile} || '/var/log/squid/access.log';
|
|
if (!$self->{LogFile}) {
|
|
die "ERROR: 'LogFile' configuration option must be set.\n";
|
|
}
|
|
if (! -e $self->{LogFile}) {
|
|
die "ERROR: 'LogFile' $self->{LogFile} doesn't exists.\n";
|
|
}
|
|
$self->{OrderUser} = lc($options{OrderUser}) || 'bytes';
|
|
$self->{OrderNetwork} = lc($options{OrderNetwork}) || 'bytes';
|
|
$self->{OrderUrl} = lc($options{OrderUrl}) || 'bytes';
|
|
$self->{OrderMime} = lc($options{OrderMime}) || 'bytes';
|
|
if ($self->{OrderUser} !~ /^(hits|bytes|duration)$/) {
|
|
die "ERROR: OrderUser must be one of these values: hits, bytes or duration\n";
|
|
}
|
|
if ($self->{OrderNetwork} !~ /^(hits|bytes|duration)$/) {
|
|
die "ERROR: OrderNetwork must be one of these values: hits, bytes or duration\n";
|
|
}
|
|
if ($self->{OrderUrl} !~ /^(hits|bytes|duration)$/) {
|
|
die "ERROR: OrderUrl must be one of these values: hits, bytes or duration\n";
|
|
}
|
|
if ($self->{OrderMime} !~ /^(hits|bytes)$/) {
|
|
die "ERROR: OrderMime must be one of these values: hits or bytes\n";
|
|
}
|
|
$self->{NetworkAlias} = &parse_network_aliases($options{NetworkAlias} || '');
|
|
$self->{UserAlias} = &parse_user_aliases($options{UserAlias} || '');
|
|
%{$self->{Exclude}} = &parse_exclusion($options{Exclude} || '');
|
|
|
|
$self->{CostPrice} = $options{CostPrice} || 0;
|
|
$self->{Currency} = $options{Currency} || '€';
|
|
$self->{TopNumber} = $options{TopNumber} || 10;
|
|
$self->{TransfertUnit} = $options{TransfertUnit} || 'BYTES';
|
|
if (!grep(/^$self->{TransfertUnit}$/i, 'BYTES', 'KB', 'MB', 'GB')) {
|
|
die "ERROR: TransfertUnit must be one of these values: KB, MB or GB\n";
|
|
} else {
|
|
if (uc($self->{TransfertUnit}) eq 'BYTES') {
|
|
$self->{TransfertUnitValue} = 1;
|
|
$self->{TransfertUnit} = 'Bytes';
|
|
} elsif (uc($self->{TransfertUnit}) eq 'KB') {
|
|
$self->{TransfertUnitValue} = 1024;
|
|
} elsif (uc($self->{TransfertUnit}) eq 'MB') {
|
|
$self->{TransfertUnitValue} = 1024*1024;
|
|
} elsif (uc($self->{TransfertUnit}) eq 'GB') {
|
|
$self->{TransfertUnitValue} = 1024*1024*1024;
|
|
}
|
|
}
|
|
|
|
# Init statistics storage hashes
|
|
$self->_clear_stats();
|
|
|
|
# Used to store the first and last date parsed
|
|
$self->{last_year} = 0;
|
|
$self->{last_month} = 0;
|
|
$self->{last_day} = 0;
|
|
$self->{first_year} = 0;
|
|
$self->{first_month} = 0;
|
|
$self->{first_day} = 0;
|
|
$self->{begin_time} = 0;
|
|
$self->{end_time} = 0;
|
|
$self->{history_time} = 0;
|
|
|
|
# Get the last parsing date for incremental parsing
|
|
if (-e "$self->{Output}/SquidAnalyzer.current") {
|
|
my $current = new IO::File;
|
|
unless($current->open("$self->{Output}/SquidAnalyzer.current")) {
|
|
print STDERR "ERROR: Can't read file $self->{Output}/SquidAnalyzer.current, $!\n" if (!$self->{QuietMode});
|
|
print STDERR "Starting at the first line of Squid access log file.\n" if (!$self->{QuietMode});
|
|
} else {
|
|
$self->{history_time} = <$current>;
|
|
chomp($self->{history_time});
|
|
$self->{begin_time} = $self->{history_time};
|
|
$current->close();
|
|
print STDERR "HISTORY TIME: ", strftime("%a %b %e %H:%M:%S %Y", localtime($self->{history_time})), "\n" if (!$self->{QuietMode});
|
|
}
|
|
}
|
|
|
|
$self->{menu} = qq{
|
|
<div id="menu">
|
|
<ul>
|
|
<li><a href="../index.html"><span class="iconArrow">$Translate{'Back_link'}</span></a></li>
|
|
};
|
|
if ($self->{UrlReport}) {
|
|
$self->{menu} .= qq{
|
|
<li><a href="domain.html"><span class="iconDomain">$Translate{'Top_domain_link'}</span></a></li>
|
|
<li><a href="url.html"><span class="iconUrl">$Translate{'Top_url_link'}</span></a></li>
|
|
};
|
|
}
|
|
$self->{menu} .= qq{
|
|
<li><a href="user.html"><span class="iconUser">$Translate{'User_link'}</span></a></li>
|
|
<li><a href="network.html"><span class="iconNetwork">$Translate{'Network_link'}</span></a></li>
|
|
<li><a href="mime_type.html"><span class="iconMime">$Translate{'Mime_link'}</span></a></li>
|
|
</ul>
|
|
</div>
|
|
};
|
|
|
|
$self->{menu2} = qq{
|
|
<div id="menu">
|
|
<ul>
|
|
<li><a href="../../index.html"><span class="iconArrow">$Translate{'Back_link'}</span></a></li>
|
|
};
|
|
if ($self->{UrlReport}) {
|
|
$self->{menu2} .= qq{
|
|
<li><a href="../../domain.html"><span class="iconDomain">$Translate{'Top_domain_link'}</span></a></li>
|
|
<li><a href="../../url.html"><span class="iconUrl">$Translate{'Top_url_link'}</span></a></li>A
|
|
};
|
|
}
|
|
$self->{menu2} .= qq{
|
|
<li><a href="../../user.html"><span class="iconUser">$Translate{'User_link'}</span></a></li>
|
|
<li><a href="../../network.html"><span class="iconNetwork">$Translate{'Network_link'}</span></a></li>
|
|
<li><a href="../../mime_type.html"><span class="iconMime">$Translate{'Mime_link'}</span></a></li>
|
|
</ul>
|
|
</div>
|
|
};
|
|
|
|
}
|
|
|
|
sub _parseData
|
|
{
|
|
my ($self, $time, $elapsed, $client, $code, $bytes, $url, $id, $type) = @_;
|
|
|
|
# Get the current year and month
|
|
my ($sec,$min,$hour,$wday,$yday,$isdst) = '';
|
|
($sec,$min,$hour,$self->{last_day},$self->{last_month},$self->{last_year},$wday,$yday,$isdst) = localtime($time);
|
|
$self->{last_year} += 1900;
|
|
$self->{last_month}++;
|
|
$self->{last_month} = "0$self->{last_month}" if ($self->{last_month} < 10);
|
|
$self->{last_day} = "0$self->{last_day}" if ($self->{last_day} < 10);
|
|
$hour = "0$hour" if ($hour < 10);
|
|
|
|
# Set the year/month value for history check
|
|
my $date = "$self->{last_year}$self->{last_month}$self->{last_day}";
|
|
|
|
# Extract the domainname part of the URL
|
|
my $dest = $url;
|
|
$dest =~ s#^[^\/]*\/\/##;
|
|
$dest =~ s#\/.*##;
|
|
$dest =~ s#:\d+$##;
|
|
|
|
# Replace network by his aliases if any
|
|
my $network = '';
|
|
foreach my $n (keys %{$self->{NetworkAlias}}) {
|
|
if ( grep($client =~ /^$_/, @{$self->{NetworkAlias}->{$n}}) ) {
|
|
$network = $n;
|
|
last;
|
|
}
|
|
}
|
|
# Set default to a class A network
|
|
if (!$network) {
|
|
$network = $client;
|
|
$network =~ s/\.\d+$/\.0/;
|
|
}
|
|
# Replace username by his alias if any
|
|
foreach my $u (keys %{$self->{UserAlias}}) {
|
|
if ( grep($id =~ /^$_$/, @{$self->{UserAlias}->{$u}}) ) {
|
|
$id = $u;
|
|
last;
|
|
}
|
|
}
|
|
|
|
# Store data when day change to save memory
|
|
if ($self->{tmp_saving} =~ /^(\d{4})(\d{2})(\d{2})$/) {
|
|
# If the day has changed then we want to save stats of the previous one
|
|
if ($self->{last_day} && ($3 ne $self->{last_day})) {
|
|
$self->_save_data("$1", "$2", "$3");
|
|
$self->{first_day} = '';
|
|
}
|
|
# If the month has changed then we want to save stats of the previous one
|
|
if ($self->{last_month} && ($2 ne $self->{last_month})) {
|
|
$self->_save_data("$1", "$2");
|
|
$self->{first_month} = '';
|
|
}
|
|
# If the year has changed then we want to save stats of the previous one
|
|
if ($self->{last_year} && ($1 ne $self->{last_year})) {
|
|
$self->_save_data("$1");
|
|
$self->{first_year} = '';
|
|
# Stats can be cleared
|
|
print STDERR "Clearing complete statistics storage hashes for year $1.\n" if (!$self->{QuietMode});
|
|
$self->_clear_stats();
|
|
}
|
|
}
|
|
|
|
$self->{first_year} ||= $self->{last_year};
|
|
$self->{first_month} ||= $self->{last_month};
|
|
$self->{first_day} ||= $self->{last_day};
|
|
|
|
$self->{tmp_saving} = $date;
|
|
|
|
#### Store client statistics
|
|
$self->{stat_user_hour}{$id}{$hour}{hits}++;
|
|
$self->{stat_user_hour}{$id}{$hour}{bytes} += $bytes;
|
|
$self->{stat_user_hour}{$id}{$hour}{duration} += $elapsed;
|
|
$self->{stat_user_day}{$id}{$self->{last_day}}{hits}++;
|
|
$self->{stat_user_day}{$id}{$self->{last_day}}{bytes} += $bytes;
|
|
$self->{stat_user_day}{$id}{$self->{last_day}}{duration} += $elapsed;
|
|
$self->{stat_user_month}{$id}{$self->{last_month}}{hits}++;
|
|
$self->{stat_user_month}{$id}{$self->{last_month}}{bytes} += $bytes;
|
|
$self->{stat_user_month}{$id}{$self->{last_month}}{duration} += $elapsed;
|
|
if ($bytes > $self->{stat_usermax_hour}{$id}{largest_file_size}) {
|
|
$self->{stat_usermax_hour}{$id}{largest_file_size} = $bytes;
|
|
$self->{stat_usermax_hour}{$id}{largest_file_url} = $url;
|
|
}
|
|
if ($bytes > $self->{stat_usermax_day}{$id}{largest_file_size}) {
|
|
$self->{stat_usermax_day}{$id}{largest_file_size} = $bytes;
|
|
$self->{stat_usermax_day}{$id}{largest_file_url} = $url;
|
|
}
|
|
if ($bytes > $self->{stat_usermax_month}{$id}{largest_file_size}) {
|
|
$self->{stat_usermax_month}{$id}{largest_file_size} = $bytes;
|
|
$self->{stat_usermax_month}{$id}{largest_file_url} = $url;
|
|
}
|
|
|
|
#### Store networks statistics
|
|
$self->{stat_network_hour}{$network}{$hour}{hits}++;
|
|
$self->{stat_network_hour}{$network}{$hour}{bytes} += $bytes;
|
|
$self->{stat_network_hour}{$network}{$hour}{duration} += $elapsed;
|
|
$self->{stat_network_day}{$network}{$self->{last_day}}{hits}++;
|
|
$self->{stat_network_day}{$network}{$self->{last_day}}{bytes} += $bytes;
|
|
$self->{stat_network_day}{$network}{$self->{last_day}}{duration} += $elapsed;
|
|
$self->{stat_network_month}{$network}{$self->{last_month}}{hits}++;
|
|
$self->{stat_network_month}{$network}{$self->{last_month}}{bytes} += $bytes;
|
|
$self->{stat_network_month}{$network}{$self->{last_month}}{duration} += $elapsed;
|
|
if ($bytes > $self->{stat_netmax_hour}{$network}{largest_file_size}) {
|
|
$self->{stat_netmax_hour}{$network}{largest_file_size} = $bytes;
|
|
$self->{stat_netmax_hour}{$network}{largest_file_url} = $url;
|
|
}
|
|
if ($bytes > $self->{stat_netmax_day}{$network}{largest_file_size}) {
|
|
$self->{stat_netmax_day}{$network}{largest_file_size} = $bytes;
|
|
$self->{stat_netmax_day}{$network}{largest_file_url} = $url;
|
|
}
|
|
if ($bytes > $self->{stat_netmax_month}{$network}{largest_file_size}) {
|
|
$self->{stat_netmax_month}{$network}{largest_file_size} = $bytes;
|
|
$self->{stat_netmax_month}{$network}{largest_file_url} = $url;
|
|
}
|
|
|
|
#### Store HIT/MISS statistics
|
|
$self->{stat_code_hour}{$code}{$hour}{hits}++;
|
|
$self->{stat_code_hour}{$code}{$hour}{bytes} += $bytes;
|
|
$self->{stat_code_day}{$code}{$self->{last_day}}{hits}++;
|
|
$self->{stat_code_day}{$code}{$self->{last_day}}{bytes} += $bytes;
|
|
$self->{stat_code_month}{$code}{$self->{last_month}}{hits}++;
|
|
$self->{stat_code_month}{$code}{$self->{last_month}}{bytes} += $bytes;
|
|
|
|
#### Store url statistics
|
|
if ($self->{UrlReport}) {
|
|
$self->{stat_user_url_hour}{$id}{$dest}{duration} += $elapsed;
|
|
$self->{stat_user_url_hour}{$id}{$dest}{hits}++;
|
|
$self->{stat_user_url_hour}{$id}{$dest}{bytes} += $bytes;
|
|
$self->{stat_user_url_hour}{$id}{$dest}{firsthit} = $time if (!$self->{stat_user_url_hour}{$id}{$dest}{firsthit});
|
|
$self->{stat_user_url_hour}{$id}{$dest}{lasthit} = $time;
|
|
$self->{stat_user_url_day}{$id}{$dest}{duration} += $elapsed;
|
|
$self->{stat_user_url_day}{$id}{$dest}{hits}++;
|
|
$self->{stat_user_url_day}{$id}{$dest}{firsthit} = $time if (!$self->{stat_user_url_day}{$id}{$dest}{firsthit});
|
|
$self->{stat_user_url_day}{$id}{$dest}{lasthit} = $time;
|
|
$self->{stat_user_url_day}{$id}{$dest}{bytes} += $bytes;
|
|
$self->{stat_user_url_month}{$id}{$dest}{duration} += $elapsed;
|
|
$self->{stat_user_url_month}{$id}{$dest}{hits}++;
|
|
$self->{stat_user_url_month}{$id}{$dest}{bytes} += $bytes;
|
|
}
|
|
|
|
#### Store user per networks statistics
|
|
$self->{stat_netuser_hour}{$network}{$id}{duration} += $elapsed;
|
|
$self->{stat_netuser_hour}{$network}{$id}{bytes} += $bytes;
|
|
$self->{stat_netuser_hour}{$network}{$id}{hits}++;
|
|
if ($bytes > $self->{stat_netuser_hour}{$network}{$id}{largest_file_size}) {
|
|
$self->{stat_netuser_hour}{$network}{$id}{largest_file_size} = $bytes;
|
|
$self->{stat_netuser_hour}{$network}{$id}{largest_file_url} = $url;
|
|
}
|
|
$self->{stat_netuser_day}{$network}{$id}{duration} += $elapsed;
|
|
$self->{stat_netuser_day}{$network}{$id}{bytes} += $bytes;
|
|
$self->{stat_netuser_day}{$network}{$id}{hits}++;
|
|
if ($bytes > $self->{stat_netuser_day}{$network}{$id}{largest_file_size}) {
|
|
$self->{stat_netuser_day}{$network}{$id}{largest_file_size} = $bytes;
|
|
$self->{stat_netuser_day}{$network}{$id}{largest_file_url} = $url;
|
|
}
|
|
|
|
$self->{stat_netuser_month}{$network}{$id}{duration} += $elapsed;
|
|
$self->{stat_netuser_month}{$network}{$id}{bytes} += $bytes;
|
|
$self->{stat_netuser_month}{$network}{$id}{hits}++;
|
|
if ($bytes > $self->{stat_netuser_month}{$network}{$id}{largest_file_size}) {
|
|
$self->{stat_netuser_month}{$network}{$id}{largest_file_size} = $bytes;
|
|
$self->{stat_netuser_month}{$network}{$id}{largest_file_url} = $url;
|
|
}
|
|
|
|
#### Store mime type statistics
|
|
$self->{stat_mime_type_hour}{"$type"}{hits}++;
|
|
$self->{stat_mime_type_hour}{"$type"}{bytes} += $bytes;
|
|
$self->{stat_mime_type_day}{"$type"}{hits}++;
|
|
$self->{stat_mime_type_day}{"$type"}{bytes} += $bytes;
|
|
$self->{stat_mime_type_month}{"$type"}{hits}++;
|
|
$self->{stat_mime_type_month}{"$type"}{bytes} += $bytes;
|
|
|
|
}
|
|
|
|
sub _save_stat
|
|
{
|
|
my ($self, $year, $month, $day) = @_;
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
my $path = join('/', $year, $month, $day);
|
|
$path =~ s/[\/]+$//;
|
|
|
|
#### Load history
|
|
$self->_read_stat($year, $month, $day);
|
|
|
|
#### Save url statistics per user
|
|
if ($self->{UrlReport}) {
|
|
my $dat_file_user_url = new IO::File;
|
|
$dat_file_user_url->open(">$self->{Output}/$path/stat_user_url.dat")
|
|
or die "ERROR: Can't write to file $self->{Output}/$path/stat_user_url.dat, $!\n";
|
|
foreach my $id (sort {$a cmp $b} keys %{$self->{"stat_user_url_$type"}}) {
|
|
foreach my $dest (keys %{$self->{"stat_user_url_$type"}{$id}}) {
|
|
$dat_file_user_url->print("$id hits=" . $self->{"stat_user_url_$type"}{$id}{$dest}{hits} . ";" .
|
|
"bytes=" . $self->{"stat_user_url_$type"}{$id}{$dest}{bytes} . ";" .
|
|
"duration=" . $self->{"stat_user_url_$type"}{$id}{$dest}{duration} . ";" .
|
|
"first=" . $self->{"stat_user_url_$type"}{$id}{$dest}{firsthit} . ";" .
|
|
"last=" . $self->{"stat_user_url_$type"}{$id}{$dest}{lasthit} . ";" .
|
|
"url=$dest\n");
|
|
}
|
|
}
|
|
$dat_file_user_url->close();
|
|
$self->{"stat_user_url_$type"} = ();
|
|
}
|
|
|
|
#### Save user statistics
|
|
my $dat_file_user = new IO::File;
|
|
$dat_file_user->open(">$self->{Output}/$path/stat_user.dat")
|
|
or die "ERROR: Can't write to file $self->{Output}/$path/stat_user.dat, $!\n";
|
|
foreach my $id (sort {$a cmp $b} keys %{$self->{"stat_user_$type"}}) {
|
|
my $name = $id;
|
|
$name =~ s/\s+//g;
|
|
$dat_file_user->print("$name hits_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_user_$type"}{$id}}) {
|
|
$dat_file_user->print("$tmp:" . $self->{"stat_user_$type"}{$id}{$tmp}{hits} . ",");
|
|
}
|
|
$dat_file_user->print(";bytes_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_user_$type"}{$id}}) {
|
|
$dat_file_user->print("$tmp:" . $self->{"stat_user_$type"}{$id}{$tmp}{bytes} . ",");
|
|
}
|
|
$dat_file_user->print(";duration_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_user_$type"}{$id}}) {
|
|
$dat_file_user->print("$tmp:" . $self->{"stat_user_$type"}{$id}{$tmp}{duration} . ",");
|
|
}
|
|
$dat_file_user->print(";largest_file_size=" . $self->{"stat_usermax_$type"}{$id}{largest_file_size});
|
|
$dat_file_user->print(";largest_file_url=" . $self->{"stat_usermax_$type"}{$id}{largest_file_url} . "\n");
|
|
}
|
|
$dat_file_user->close();
|
|
$self->{"stat_user_$type"} = ();
|
|
$self->{"stat_usermax_$type"} = ();
|
|
|
|
#### Save network statistics
|
|
my $dat_file_network = new IO::File;
|
|
$dat_file_network->open(">$self->{Output}/$path/stat_network.dat")
|
|
or die "ERROR: Can't write to file $self->{Output}/$path/stat_network.dat, $!\n";
|
|
foreach my $net (sort {$a cmp $b} keys %{$self->{"stat_network_$type"}}) {
|
|
$dat_file_network->print("$net\thits_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_network_$type"}{$net}}) {
|
|
$dat_file_network->print("$tmp:" . $self->{"stat_network_$type"}{$net}{$tmp}{hits} . ",");
|
|
}
|
|
$dat_file_network->print(";bytes_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_network_$type"}{$net}}) {
|
|
$dat_file_network->print("$tmp:" . $self->{"stat_network_$type"}{$net}{$tmp}{bytes} . ",");
|
|
}
|
|
$dat_file_network->print(";duration_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_network_$type"}{$net}}) {
|
|
$dat_file_network->print("$tmp:" . $self->{"stat_network_$type"}{$net}{$tmp}{duration} . ",");
|
|
}
|
|
$dat_file_network->print(";largest_file_size=" . $self->{"stat_netmax_$type"}{$net}{largest_file_size});
|
|
$dat_file_network->print(";largest_file_url=" . $self->{"stat_netmax_$type"}{$net}{largest_file_url} . "\n");
|
|
}
|
|
$dat_file_network->close();
|
|
$self->{"stat_network_$type"} = ();
|
|
$self->{"stat_netmax_$type"} = ();
|
|
|
|
#### Save user per network statistics
|
|
my $dat_file_netuser = new IO::File;
|
|
$dat_file_netuser->open(">$self->{Output}/$path/stat_netuser.dat")
|
|
or die "ERROR: Can't write to file $self->{Output}/$path/stat_netuser.dat, $!\n";
|
|
foreach my $net (sort {$a cmp $b} keys %{$self->{"stat_netuser_$type"}}) {
|
|
foreach my $id (sort {$a cmp $b} keys %{$self->{"stat_netuser_$type"}{$net}}) {
|
|
$dat_file_netuser->print("$net\t$id\thits=" . $self->{"stat_netuser_$type"}{$net}{$id}{hits} . ";" .
|
|
"bytes=" . $self->{"stat_netuser_$type"}{$net}{$id}{bytes} . ";" .
|
|
"duration=" . $self->{"stat_netuser_$type"}{$net}{$id}{duration} . ";");
|
|
$dat_file_netuser->print("largest_file_size=" .
|
|
$self->{"stat_netuser_$type"}{$net}{$id}{largest_file_size} . ";" .
|
|
"largest_file_url=" . $self->{"stat_netuser_$type"}{$net}{$id}{largest_file_url} . "\n");
|
|
}
|
|
}
|
|
$dat_file_netuser->close();
|
|
$self->{"stat_netuser_$type"} = ();
|
|
|
|
|
|
#### Save cache statistics
|
|
my $dat_file_code = new IO::File;
|
|
$dat_file_code->open(">$self->{Output}/$path/stat_code.dat")
|
|
or die "ERROR: Can't write to file $self->{Output}/$path/stat_code.dat, $!\n";
|
|
foreach my $code (sort {$a cmp $b} keys %{$self->{"stat_code_$type"}}) {
|
|
$dat_file_code->print("$code " .
|
|
"hits_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_code_$type"}{$code}}) {
|
|
$dat_file_code->print("$tmp:" . $self->{"stat_code_$type"}{$code}{$tmp}{hits} . ",");
|
|
}
|
|
$dat_file_code->print(";bytes_$type=");
|
|
foreach my $tmp (sort {$a <=> $b} keys %{$self->{"stat_code_$type"}{$code}}) {
|
|
$dat_file_code->print("$tmp:" . $self->{"stat_code_$type"}{$code}{$tmp}{bytes} . ",");
|
|
}
|
|
$dat_file_code->print("\n");
|
|
}
|
|
$dat_file_code->close();
|
|
$self->{"stat_code_$type"} = ();
|
|
$self->{stat_code} = ();
|
|
|
|
#### Save mime statistics
|
|
my $dat_file_mime_type = new IO::File;
|
|
$dat_file_mime_type->open(">$self->{Output}/$path/stat_mime_type.dat")
|
|
or die "ERROR: Can't write to file $self->{Output}/$path/stat_mime_type.dat, $!\n";
|
|
foreach my $mime (sort {$a cmp $b} keys %{$self->{"stat_mime_type_$type"}}) {
|
|
$dat_file_mime_type->print("$mime hits=" . $self->{"stat_mime_type_$type"}{$mime}{hits} . ";" .
|
|
"bytes=" . $self->{"stat_mime_type_$type"}{$mime}{bytes} . "\n");
|
|
}
|
|
$dat_file_mime_type->close();
|
|
$self->{"stat_mime_type_$type"} = ();
|
|
|
|
}
|
|
|
|
sub _save_data
|
|
{
|
|
my ($self, $year, $month, $day) = @_;
|
|
|
|
my $path = join('/', $year, $month, $day);
|
|
$path =~ s/[\/]+$//;
|
|
|
|
#### Create directory structure
|
|
if (!-d "$self->{Output}/$year") {
|
|
mkdir("$self->{Output}/$year", 0755) || die "ERROR: can't create directory $self->{Output}/$year, $!\n";
|
|
}
|
|
if ($month && !-d "$self->{Output}/$year/$month") {
|
|
mkdir("$self->{Output}/$year/$month", 0755) || die "ERROR: can't create directory $self->{Output}/$year/$month, $!\n";
|
|
}
|
|
if ($day && !-d "$self->{Output}/$year/$month/$day") {
|
|
mkdir("$self->{Output}/$year/$month/$day", 0755) || die "ERROR: can't create directory $self->{Output}/$year/$month/$day, $!\n";
|
|
}
|
|
print STDERR "Dumping data into $self->{Output}/$path\n" if (!$self->{QuietMode});
|
|
$self->_save_stat($year, $month, $day);
|
|
|
|
}
|
|
|
|
|
|
sub _read_stat
|
|
{
|
|
my ($self, $year, $month, $day) = @_;
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
my $path = join('/', $year, $month, $day);
|
|
$path =~ s/[\/]+$//;
|
|
|
|
#### Read previous client statistics
|
|
my $dat_file_user = new IO::File;
|
|
if ($dat_file_user->open("$self->{Output}/$path/stat_user.dat")) {
|
|
my $i = 1;
|
|
while (my $l = <$dat_file_user>) {
|
|
chomp($l);
|
|
if ($l =~ s/^([^\s]+)\s+hits_$type=([^;]+);bytes_$type=([^;]+);duration_$type=([^;]+);largest_file_size=([^;]*);largest_file_url=(.*)$//) {
|
|
my $id = $1;
|
|
my $hits = $2 || '';
|
|
my $bytes = $3 || '';
|
|
my $duration = $4 || '';
|
|
if ($5 > $self->{"stat_usermax_$type"}{$id}{largest_file_size}) {
|
|
$self->{"stat_usermax_$type"}{$id}{largest_file_size} = $5;
|
|
$self->{"stat_usermax_$type"}{$id}{largest_file_url} = $6;
|
|
}
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
$duration =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$self->{"stat_user_$type"}{$id}{$tmp}{hits} += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$self->{"stat_user_$type"}{$id}{$tmp}{bytes} += $bytes_tmp{$tmp};
|
|
}
|
|
my %duration_tmp = split(/[:,]/, $duration);
|
|
foreach my $tmp (sort {$a <=> $b} keys %duration_tmp) {
|
|
$self->{"stat_user_$type"}{$id}{$tmp}{duration} += $duration_tmp{$tmp};
|
|
}
|
|
} else {
|
|
print STDERR "ERROR: bad format at line $i into $self->{Output}/$path/stat_user.dat:\n";
|
|
print STDERR "$l\n";
|
|
exit 0;
|
|
}
|
|
$i++;
|
|
}
|
|
$dat_file_user->close();
|
|
}
|
|
|
|
#### Read previous url statistics
|
|
if ($self->{UrlReport}) {
|
|
my $dat_file_user_url = new IO::File;
|
|
if ($dat_file_user_url->open("$self->{Output}/$path/stat_user_url.dat")) {
|
|
my $i = 1;
|
|
while (my $l = <$dat_file_user_url>) {
|
|
chomp($l);
|
|
if ($l =~ s/^([^\s]+)\s+hits=(\d+);bytes=(\d+);duration=(\d+);first=([^;]*);last=([^;]*);url=(.*)$//) {
|
|
$self->{"stat_user_url_$type"}{$1}{"$7"}{hits} += $2;
|
|
$self->{"stat_user_url_$type"}{$1}{"$7"}{bytes} += $3;
|
|
$self->{"stat_user_url_$type"}{$1}{"$7"}{duration} += $4;
|
|
$self->{"stat_user_url_$type"}{$1}{"$7"}{firsthit} = $5 if (!$self->{"stat_user_url_$type"}{$1}{"$7"}{firsthit});
|
|
$self->{"stat_user_url_$type"}{$1}{"$7"}{lasthit} = $6;
|
|
} elsif ($l =~ s/^([^\s]+)\s+hits=(\d+);bytes=(\d+);duration=(\d+);url=(.*)$//) {
|
|
$self->{"stat_user_url_$type"}{$1}{"$5"}{hits} += $2;
|
|
$self->{"stat_user_url_$type"}{$1}{"$5"}{bytes} += $3;
|
|
$self->{"stat_user_url_$type"}{$1}{"$5"}{duration} += $4;
|
|
} else {
|
|
print STDERR "ERROR: bad format at line $i into $self->{Output}/$path/stat_user_url.dat\n";
|
|
print STDERR "$l\n";
|
|
exit 0;
|
|
}
|
|
$i++;
|
|
}
|
|
$dat_file_user_url->close();
|
|
}
|
|
}
|
|
|
|
#### Read previous network statistics
|
|
my $dat_file_network = new IO::File;
|
|
if ($dat_file_network->open("$self->{Output}/$path/stat_network.dat")) {
|
|
my $i = 1;
|
|
while (my $l = <$dat_file_network>) {
|
|
chomp($l);
|
|
my ($net, $data) = split(/\t/, $l);
|
|
if (!$data) {
|
|
# Assume backward compatibility
|
|
$l =~ s/^(.*)\shits_$type=/hits_$type=/;
|
|
$net = $1;
|
|
$data = $l;
|
|
}
|
|
if ($data =~ s/^hits_$type=([^;]+);bytes_$type=([^;]+);duration_$type=([^;]+);largest_file_size=([^;]*);largest_file_url=(.*)$//) {
|
|
my $hits = $1 || '';
|
|
my $bytes = $2 || '';
|
|
my $duration = $3 || '';
|
|
if ($4 > $self->{"stat_netmax_$type"}{$net}{largest_file_size}) {
|
|
$self->{"stat_netmax_$type"}{$net}{largest_file_size} = $4;
|
|
$self->{"stat_netmax_$type"}{$net}{largest_file_url} = $5;
|
|
}
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
$duration =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$self->{"stat_network_$type"}{$net}{$tmp}{hits} += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$self->{"stat_network_$type"}{$net}{$tmp}{bytes} += $bytes_tmp{$tmp};
|
|
}
|
|
my %duration_tmp = split(/[:,]/, $duration);
|
|
foreach my $tmp (sort {$a <=> $b} keys %duration_tmp) {
|
|
$self->{"stat_network_$type"}{$net}{$tmp}{duration} += $duration_tmp{$tmp};
|
|
}
|
|
} else {
|
|
print STDERR "ERROR: bad format at line $i into $self->{Output}/$path/stat_network.dat\n";
|
|
print STDERR "$l\n";
|
|
exit 0;
|
|
}
|
|
$i++;
|
|
}
|
|
$dat_file_network->close();
|
|
}
|
|
|
|
#### Read previous user per network statistics
|
|
my $dat_file_netuser = new IO::File;
|
|
if ($dat_file_netuser->open("$self->{Output}/$path/stat_netuser.dat")) {
|
|
my $i = 1;
|
|
while (my $l = <$dat_file_netuser>) {
|
|
chomp($l);
|
|
my ($net, $id, $data) = split(/\t/, $l);
|
|
if (!$data) {
|
|
# Assume backward compatibility
|
|
$l =~ s/^(.*)\s([^\s]+)\shits=/hits=/;
|
|
$net = $1;
|
|
$id = $2;
|
|
$data = $l;
|
|
}
|
|
if ($data =~ s/^hits=(\d+);bytes=(\d+);duration=(\d+);largest_file_size=([^;]*);largest_file_url=(.*)$//) {
|
|
$self->{"stat_netuser_$type"}{$net}{$id}{hits} += $1;
|
|
$self->{"stat_netuser_$type"}{$net}{$id}{bytes} += $2;
|
|
$self->{"stat_netuser_$type"}{$net}{$id}{duration} += $3;
|
|
if ($6 > $self->{"stat_netuser_$type"}{$net}{$id}{largest_file_size}) {
|
|
$self->{"stat_netuser_$type"}{$net}{$id}{largest_file_size} = $4;
|
|
$self->{"stat_netuser_$type"}{$net}{$id}{largest_file_url} = $5;
|
|
}
|
|
} else {
|
|
print STDERR "ERROR: bad format at line $i into $self->{Output}/$path/stat_netuser.dat\n";
|
|
print STDERR "$l\n";
|
|
exit 0;
|
|
}
|
|
$i++;
|
|
}
|
|
$dat_file_netuser->close();
|
|
}
|
|
|
|
#### Read previous cache statistics
|
|
my $dat_file_code = new IO::File;
|
|
if ($dat_file_code->open("$self->{Output}/$path/stat_code.dat")) {
|
|
my $i = 1;
|
|
while (my $l = <$dat_file_code>) {
|
|
chomp($l);
|
|
if ($l =~ s/^([^\s]+)\s+hits_$type=([^;]+);bytes_$type=([^;]+)$//) {
|
|
my $code = $1;
|
|
my $hits = $2 || '';
|
|
my $bytes = $3 || '';
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$self->{"stat_code_$type"}{$code}{$tmp}{hits} += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$self->{"stat_code_$type"}{$code}{$tmp}{bytes} += $bytes_tmp{$tmp};
|
|
}
|
|
} else {
|
|
print STDERR "ERROR: bad format at line $i into $self->{Output}/$path/stat_code.dat\n";
|
|
print STDERR "$l\n";
|
|
exit 0;
|
|
}
|
|
$i++;
|
|
}
|
|
$dat_file_code->close();
|
|
}
|
|
|
|
#### Read previous mime statistics
|
|
my $dat_file_mime_type = new IO::File;
|
|
if ($dat_file_mime_type->open("$self->{Output}/$path/stat_mime_type.dat")) {
|
|
my $i = 1;
|
|
while (my $l = <$dat_file_mime_type>) {
|
|
chomp($l);
|
|
if ($l =~ s/^([^\s]+)\s+hits=(\d+);bytes=(\d+)//) {
|
|
my $mime = $1;
|
|
$self->{"stat_mime_type_$type"}{$mime}{hits} += $2;
|
|
$self->{"stat_mime_type_$type"}{$mime}{bytes} += $3;
|
|
} else {
|
|
print STDERR "ERROR: bad format at line $i into $self->{Output}/$path/stat_mime_type.dat\n";
|
|
print STDERR "$l\n";
|
|
exit 0;
|
|
}
|
|
$i++;
|
|
}
|
|
$dat_file_mime_type->close();
|
|
}
|
|
|
|
}
|
|
|
|
sub _print_header
|
|
{
|
|
my ($self, $fileout, $menu, $calendar) = @_;
|
|
|
|
my $lang = '';
|
|
if ($self->{Locale}) {
|
|
$lang = 'LANG=' . $self->{Locale};
|
|
}
|
|
my $curdate = `$lang date | iconv -t $Translate{CharSet} 2>/dev/null`;
|
|
chomp($curdate);
|
|
my $now = $curdate || strftime("%a %b %e %H:%M:%S %Y", localtime);
|
|
|
|
print $$fileout qq{
|
|
<!DOCTYPE html
|
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
|
|
<head>
|
|
<meta NAME="robots" CONTENT="noindex,nofollow" />
|
|
<meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />
|
|
<meta HTTP-EQUIV="Cache-Control" content="no-cache" />
|
|
<meta HTTP-EQUIV="Expires" CONTENT="$now" />
|
|
<meta HTTP-EQUIV="Generator" CONTENT="SquidAnalyzer $VERSION" />
|
|
<meta HTTP-EQUIV="Date" CONTENT="$now" />
|
|
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=$Translate{'CharSet'}" />
|
|
<title>SquidAnalyzer $VERSION Report</title>
|
|
<link rel="stylesheet" type="text/css" href="$self->{WebUrl}squidanalyzer.css" media="screen" />
|
|
<!-- javascript to sort table -->
|
|
<script type="text/javascript" src="$self->{WebUrl}sorttable.js"></script>
|
|
<!-- javascript to draw graphics -->
|
|
<script type="text/javascript" src="$self->{WebUrl}flotr2.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="conteneur">
|
|
<a name="atop"></a>
|
|
<div id="header">
|
|
<div id="alignLeft">
|
|
<h1>
|
|
<a href="$self->{WebUrl}"><img src="$self->{WebUrl}images/logo-squidanalyzer.png" title="SquidAnalyzer $VERSION" border="0"></a>
|
|
SquidAnalyzer
|
|
</h1>
|
|
<p class="sous-titre">
|
|
$Translate{'Generation'} $now.
|
|
</p>
|
|
</div>
|
|
$calendar
|
|
</div>
|
|
$menu
|
|
<div id="contenu">
|
|
};
|
|
|
|
}
|
|
|
|
sub _print_footer
|
|
{
|
|
my ($self, $fileout) = @_;
|
|
|
|
print $$fileout qq{
|
|
</div>
|
|
<div id="footer">
|
|
<h4>
|
|
$Translate{'File_Generated'} <a href="http://squidanalyzer.darold.net/">SquidAnalyzer v$VERSION</a>
|
|
</h4>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
};
|
|
|
|
}
|
|
|
|
sub buildHTML
|
|
{
|
|
my ($self, $outdir) = @_;
|
|
|
|
$outdir ||= $self->{Output};
|
|
|
|
print STDERR "Building HTML output into $outdir\n" if (!$self->{QuietMode});
|
|
|
|
# Load history data for incremental scan
|
|
my $old_year = 0;
|
|
my $old_month = 0;
|
|
my $old_day = 0;
|
|
if ($self->{history_time}) {
|
|
$old_year = (localtime($self->{history_time}))[5]+1900;
|
|
$old_month = (localtime($self->{history_time}))[4]+1;
|
|
$old_month = "0$old_month" if ($old_month < 10);
|
|
$old_day = (localtime($self->{history_time}))[3];
|
|
$old_day = "0$old_day" if ($old_day < 10);
|
|
}
|
|
|
|
# Generate all HTML output
|
|
opendir(DIR, $outdir) || die "Error: can't opendir $outdir: $!";
|
|
my @years = grep { /^\d{4}$/ && -d "$outdir/$_"} readdir(DIR);
|
|
closedir DIR;
|
|
foreach my $y (sort {$a <=> $b} @years) {
|
|
next if ($y < $old_year);
|
|
opendir(DIR, "$outdir/$y") || die "Error: can't opendir $outdir/$y: $!";
|
|
my @months = grep { /^\d{2}$/ && -d "$outdir/$y/$_"} readdir(DIR);
|
|
closedir DIR;
|
|
foreach my $m (sort {$a <=> $b} @months) {
|
|
next if ($m < $old_month);
|
|
opendir(DIR, "$outdir/$y/$m") || die "Error: can't opendir $outdir/$y/$m: $!";
|
|
my @days = grep { /^\d{2}$/ && -d "$outdir/$y/$m/$_"} readdir(DIR);
|
|
closedir DIR;
|
|
foreach my $d (sort {$a <=> $b} @days) {
|
|
next if ($d < $old_day);
|
|
print STDERR "Generating daily statistics for day $y-$m-$d\n" if (!$self->{QuietMode});
|
|
$self->gen_html_output($outdir, $y, $m, $d);
|
|
}
|
|
print STDERR "Generating monthly statistics for month $y-$m\n" if (!$self->{QuietMode});
|
|
$self->gen_html_output($outdir, $y, $m);
|
|
}
|
|
print STDERR "Generating yearly statistics for year $y\n" if (!$self->{QuietMode});
|
|
$self->gen_html_output($outdir, $y);
|
|
}
|
|
|
|
$self->_gen_summary($outdir);
|
|
}
|
|
|
|
sub gen_html_output
|
|
{
|
|
my ($self, $outdir, $year, $month, $day) = @_;
|
|
|
|
my $dir = "$outdir";
|
|
if ($year) {
|
|
$dir .= "/$year";
|
|
}
|
|
if ($month) {
|
|
$dir .= "/$month";
|
|
}
|
|
if ($day) {
|
|
$dir .= "/$day";
|
|
}
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
print STDERR "\tUser statistics in $dir...\n" if (!$self->{QuietMode});
|
|
my $nuser = $self->_print_user_stat($dir, $year, $month, $day);
|
|
print STDERR "\tMime type statistics in $dir...\n" if (!$self->{QuietMode});
|
|
$self->_print_mime_stat($dir, $year, $month, $day);
|
|
print STDERR "\tNetwork statistics in $dir...\n" if (!$self->{QuietMode});
|
|
$self->_print_network_stat($dir, $year, $month, $day);
|
|
my $nurl = 0;
|
|
my $ndomain = 0;
|
|
if ($self->{UrlReport}) {
|
|
print STDERR "\tTop URL statistics in $dir...\n" if (!$self->{QuietMode});
|
|
$nurl = $self->_print_top_url_stat($dir, $year, $month, $day);
|
|
print STDERR "\tTop domain statistics in $dir...\n" if (!$self->{QuietMode});
|
|
$ndomain = $self->_print_top_domain_stat($dir, $year, $month, $day);
|
|
}
|
|
print STDERR "\tCache statistics in $dir...\n" if (!$self->{QuietMode});
|
|
$self->_print_cache_stat($dir, $year, $month, $day, $nuser, $nurl, $ndomain);
|
|
|
|
return ($nuser, $nurl, $ndomain);
|
|
|
|
}
|
|
|
|
sub parse_duration
|
|
{
|
|
my ($secondes) = @_;
|
|
|
|
my $hours = int($secondes/3600);
|
|
$hours = "0$hours" if ($hours < 10);
|
|
$secondes = $secondes - ($hours*3600);
|
|
my $minutes = int($secondes/60);
|
|
$minutes = "0$minutes" if ($minutes < 10);
|
|
$secondes = $secondes - ($minutes*60);
|
|
$secondes = "0$secondes" if ($secondes < 10);
|
|
|
|
return "$hours:$minutes:$secondes";
|
|
}
|
|
|
|
sub _print_cache_stat
|
|
{
|
|
my ($self, $outdir, $year, $month, $day, $nuser, $nurl, $ndomain) = @_;
|
|
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_code.dat") || return;
|
|
my %code_stat = ();
|
|
my %detail_code_stat = ();
|
|
while (my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($code, $data) = split(/\s/, $l);
|
|
$data =~ /hits_$type=([^;]+);bytes_$type=([^;]+)/;
|
|
my $hits = $1 || '';
|
|
my $bytes = $2 || '';
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$detail_code_stat{$code}{$tmp}{request} = $hits_tmp{$tmp};
|
|
$code_stat{$code}{request} += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$detail_code_stat{$code}{$tmp}{bytes} = $bytes_tmp{$tmp};
|
|
$code_stat{$code}{bytes} += $bytes_tmp{$tmp};
|
|
}
|
|
}
|
|
$infile->close();
|
|
my $total_request = $code_stat{HIT}{request} + $code_stat{MISS}{request};
|
|
my $total_bytes = $code_stat{HIT}{bytes} + $code_stat{MISS}{bytes};
|
|
|
|
my $file = $outdir . '/index.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir);
|
|
$self->_print_header(\$out, $self->{menu}, $cal);
|
|
|
|
# Print title and calendar view
|
|
print $out $self->_print_title($Translate{'Cache_title'}, $stat_date);
|
|
|
|
my $total_cost = sprintf("%2.2f", int($total_bytes/1000000) * $self->{CostPrice});
|
|
my $comma_bytes = $self->format_bytes($total_bytes);
|
|
my $hit_bytes = $self->format_bytes($code_stat{HIT}{bytes});
|
|
my $miss_bytes = $self->format_bytes($code_stat{MISS}{bytes});
|
|
my $colspn = 5;
|
|
$colspn = 6 if ($self->{CostPrice});
|
|
|
|
my $last = '23';
|
|
my $first = '00';
|
|
my $title = $Translate{'Hourly'} || 'Hourly';
|
|
my $unit = $Translate{'Hours'} || 'Hours';
|
|
if ($type eq 'day') {
|
|
$last = '31';
|
|
$first = '01';
|
|
$title = $Translate{'Daily'} || 'Daily';
|
|
$unit = $Translate{'Days'} || 'Days';
|
|
} elsif ($type eq 'month') {
|
|
$last = '12';
|
|
$first = '01';
|
|
$title = $Translate{'Monthly'} || 'Monthly';
|
|
$unit = $Translate{'Months'} || 'Months';
|
|
}
|
|
my @hit = ();
|
|
my @miss = ();
|
|
my @total = ();
|
|
for ("$first" .. "$last") {
|
|
my $tot = 0;
|
|
if (exists $detail_code_stat{HIT}{$_}{request}) {
|
|
push(@hit, "[ $_, $detail_code_stat{HIT}{$_}{request} ]");
|
|
$tot += $detail_code_stat{HIT}{$_}{request};
|
|
} else {
|
|
push(@hit, "[ $_, 0 ]");
|
|
}
|
|
if (exists $detail_code_stat{MISS}{$_}{request}) {
|
|
push(@miss, "[ $_, $detail_code_stat{MISS}{$_}{request} ]");
|
|
$tot += $detail_code_stat{MISS}{$_}{request};
|
|
} else {
|
|
push(@miss, "[ $_, 0 ]");
|
|
}
|
|
push(@total, "[ $_, $tot ]");
|
|
delete $detail_code_stat{HIT}{$_}{request};
|
|
delete $detail_code_stat{MISS}{$_}{request};
|
|
}
|
|
|
|
my $t1 = $Translate{'Graph_cache_hit_title'};
|
|
$t1 =~ s/\%s/$title/;
|
|
$t1 = "$t1 $stat_date";
|
|
my $xlabel = $unit || '';
|
|
my $ylabel = $Translate{'Requests_graph'} || 'Requests';
|
|
my $code_requests = $self->flotr2_bargraph(1, 'code_requests', $type, $t1, $xlabel, $ylabel,
|
|
join(',', @total), $Translate{'Total_graph'},
|
|
join(',', @hit), $Translate{'Hit_graph'},
|
|
join(',', @miss), $Translate{'Miss_graph'} );
|
|
@hit = ();
|
|
@miss = ();
|
|
@total = ();
|
|
|
|
for ("$first" .. "$last") {
|
|
my $tot = 0;
|
|
if (exists $detail_code_stat{HIT}{$_}{bytes}) {
|
|
push(@hit, "[ $_, " . int($detail_code_stat{HIT}{$_}{bytes}/1000000) . " ]");
|
|
$tot += $detail_code_stat{HIT}{$_}{bytes};
|
|
} else {
|
|
push(@hit, "[ $_, 0 ]");
|
|
}
|
|
if (exists $detail_code_stat{MISS}{$_}{bytes}) {
|
|
push(@miss, "[ $_, " . int($detail_code_stat{MISS}{$_}{bytes}/1000000) . " ]");
|
|
$tot += $detail_code_stat{MISS}{$_}{bytes};
|
|
} else {
|
|
push(@miss, "[ $_, 0 ]");
|
|
}
|
|
push(@total, "[ $_, " . int($tot/1000000) . " ]");
|
|
}
|
|
%detail_code_stat = ();
|
|
$t1 = $Translate{'Graph_cache_byte_title'};
|
|
$t1 =~ s/\%s/$title/;
|
|
$t1 = "$t1 $stat_date";
|
|
$ylabel = $Translate{'Megabytes_graph'} || $Translate{'Megabytes'};
|
|
my $code_bytes = $self->flotr2_bargraph(2, 'code_bytes', $type, $t1, $xlabel, $ylabel,
|
|
join(',', @total), $Translate{'Total_graph'},
|
|
join(',', @hit), $Translate{'Hit_graph'},
|
|
join(',', @miss), $Translate{'Miss_graph'} );
|
|
@hit = ();
|
|
@miss = ();
|
|
@total = ();
|
|
|
|
print $out qq{
|
|
<table class="stata">
|
|
<tr>
|
|
<th colspan="2" class="headerBlack">$Translate{'Requests'}</th>
|
|
<th colspan="2" class="headerBlack">$Translate{$self->{TransfertUnit}}</th>
|
|
<th colspan="$colspn" class="headerBlack">$Translate{'Total'}</th>
|
|
</tr>
|
|
<tr>
|
|
<th>$Translate{'Hit'}</th>
|
|
<th>$Translate{'Miss'}</th>
|
|
<th>$Translate{'Hit'}</th>
|
|
<th>$Translate{'Miss'}</th>
|
|
<th>$Translate{'Requests'}</th>
|
|
<th>$Translate{$self->{TransfertUnit}}</th>
|
|
<th>$Translate{'Users'}</th>
|
|
<th>$Translate{'Sites'}</th>
|
|
<th>$Translate{'Domains'}</th>
|
|
};
|
|
print $out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</tr>
|
|
<tr>
|
|
<td>$code_stat{HIT}{request}</td>
|
|
<td>$code_stat{MISS}{request}</td>
|
|
<td>$hit_bytes</td>
|
|
<td>$miss_bytes</td>
|
|
<td>$total_request</td>
|
|
<td>$comma_bytes</td>
|
|
<td>$nuser</td>
|
|
<td>$nurl</td>
|
|
<td>$ndomain</td>
|
|
};
|
|
print $out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</tr>
|
|
</table>
|
|
|
|
<table class="graphs"><tr><td>$code_requests</td><td>$code_bytes</td></tr></table>
|
|
|
|
<h4>$Translate{'Legend'}</h4>
|
|
<div class="line-separator"></div>
|
|
<div class="displayLegend">
|
|
<span class="legendeTitle">$Translate{'Hit'}:</span> <span class="descLegend">$Translate{'Hit_help'}</span><br/>
|
|
<span class="legendeTitle">$Translate{'Miss'}:</span> <span class="descLegend">$Translate{'Miss_help'}</span><br/>
|
|
<span class="legendeTitle">$Translate{'Users'}:</span> <span class="descLegend">$Translate{'Users_help'}</span><br/>
|
|
<span class="legendeTitle">$Translate{'Sites'}:</span> <span class="descLegend">$Translate{'Sites_help'}</span><br/>
|
|
<span class="legendeTitle">$Translate{'Domains'}:</span> <span class="descLegend">$Translate{'Domains_help'}</span><br/>
|
|
};
|
|
print $out qq{
|
|
<span class="legendeTitle">$Translate{'Cost'}:</span> <span class="descLegend">$Translate{'Cost_help'} $self->{CostPrice} $self->{Currency}</span><br/>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</div>
|
|
};
|
|
|
|
%code_stat = ();
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
}
|
|
|
|
|
|
sub _print_mime_stat
|
|
{
|
|
my ($self, $outdir, $year, $month, $day) = @_;
|
|
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_mime_type.dat") || return;
|
|
my %mime_stat = ();
|
|
my $total_count = 0;
|
|
my $total_bytes = 0;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($code, $data) = split(/\s/, $l);
|
|
$data =~ /hits=(\d+);bytes=(\d+)/;
|
|
$mime_stat{$code}{hits} = $1;
|
|
$mime_stat{$code}{bytes} = $2;
|
|
$total_count += $1;
|
|
$total_bytes += $2;
|
|
}
|
|
$infile->close();
|
|
|
|
my $ntype = scalar keys %mime_stat;
|
|
|
|
my $file = $outdir . '/mime_type.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir);
|
|
$self->_print_header(\$out, $self->{menu}, $cal);
|
|
# Print title and calendar view
|
|
print $out $self->_print_title($Translate{'Mime_title'}, $stat_date);
|
|
|
|
my %data = ();
|
|
$total_count ||= 1;
|
|
foreach my $mime (keys %mime_stat) {
|
|
if (($mime_stat{$mime}{hits}/$total_count)*100 > $self->{MinPie}) {
|
|
$data{$mime} = $mime_stat{$mime}{hits};
|
|
} else {
|
|
$data{'others'} += $mime_stat{$mime}{hits};
|
|
}
|
|
}
|
|
my $title = "$Translate{'Mime_graph_hits_title'} $stat_date";
|
|
my $mime_hits = $self->flotr2_piegraph(1, 'mime_hits', $title, $Translate{'Mime_graph'}, '', %data);
|
|
print $out qq{<table class="graphs"><tr><td>$mime_hits</td>};
|
|
$mime_hits = '';
|
|
%data = ();
|
|
$total_bytes ||= 1;
|
|
foreach my $mime (keys %mime_stat) {
|
|
if (($mime_stat{$mime}{bytes}/$total_bytes)*100 > $self->{MinPie}) {
|
|
$data{$mime} = int($mime_stat{$mime}{bytes}/1000000);
|
|
} else {
|
|
$data{'others'} += $mime_stat{$mime}{bytes};
|
|
}
|
|
}
|
|
$data{'others'} = int($data{'others'}/1000000);
|
|
$title = "$Translate{'Mime_graph_bytes_title'} $stat_date";
|
|
my $mime_bytes = $self->flotr2_piegraph(1, 'mime_bytes', $title, $Translate{'Mime_graph'}, '', %data);
|
|
print $out qq{<td>$mime_bytes</td></tr></table>};
|
|
$mime_bytes = '';
|
|
%data = ();
|
|
|
|
print $out "<h3>$Translate{'Mime_number'}: $ntype</h3>\n";
|
|
print $out qq{
|
|
<table class="sortable stata">
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Mime_link'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
};
|
|
print $out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
foreach (sort { $mime_stat{$b}{"$self->{OrderMime}"} <=> $mime_stat{$a}{"$self->{OrderMime}"} } keys %mime_stat) {
|
|
my $c_percent = '0.0';
|
|
$c_percent = sprintf("%2.2f", ($mime_stat{$_}{hits}/$total_count) * 100) if ($total_count);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($mime_stat{$_}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $total_cost = sprintf("%2.2f", int($mime_stat{$_}{bytes}/1000000) * $self->{CostPrice});
|
|
my $comma_bytes = $self->format_bytes($mime_stat{$_}{bytes});
|
|
print $out qq{
|
|
<tr>
|
|
<td>$_</td>
|
|
<td>$mime_stat{$_}{hits} <span class="italicPercent">($c_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
};
|
|
print $out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</tr>};
|
|
}
|
|
print $out qq{
|
|
</tbody>
|
|
</table>
|
|
};
|
|
|
|
print $out qq{
|
|
<div class="uplink">
|
|
<a href="#atop"><span class="iconUpArrow">$Translate{'Up_link'}</span></a>
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
|
|
}
|
|
|
|
sub _print_network_stat
|
|
{
|
|
my ($self, $outdir, $year, $month, $day) = @_;
|
|
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_network.dat") || return;
|
|
my %network_stat = ();
|
|
my %detail_network_stat = ();
|
|
my %total_net_detail = ();
|
|
my $total_hit = 0;
|
|
my $total_bytes = 0;
|
|
my $total_duration = 0;
|
|
while (my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($network, $data) = split(/\t/, $l);
|
|
if (!$data) {
|
|
# Assume backward compatibility
|
|
$l =~ s/^(.*)\shits_$type=/hits_$type=/;
|
|
$network = $1;
|
|
$data = $l;
|
|
}
|
|
$data =~ /^hits_$type=([^;]+);bytes_$type=([^;]+);duration_$type=([^;]+);largest_file_size=([^;]*);largest_file_url=(.*)/;
|
|
my $hits = $1 || '';
|
|
my $bytes = $2 || '';
|
|
my $duration = $3 || '';
|
|
$network_stat{$network}{largest_file} = $4;
|
|
$network_stat{$network}{url} = $5;
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
$duration =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$detail_network_stat{$network}{$tmp}{hits} = $hits_tmp{$tmp};
|
|
$total_net_detail{$tmp}{hits} += $hits_tmp{$tmp};
|
|
$network_stat{$network}{hits} += $hits_tmp{$tmp};
|
|
$total_hit += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$detail_network_stat{$network}{$tmp}{bytes} = $bytes_tmp{$tmp};
|
|
$total_net_detail{$tmp}{bytes} += $bytes_tmp{$tmp};
|
|
$network_stat{$network}{bytes} += $bytes_tmp{$tmp};
|
|
$total_bytes += $bytes_tmp{$tmp};
|
|
}
|
|
my %duration_tmp = split(/[:,]/, $duration);
|
|
foreach my $tmp (sort {$a <=> $b} keys %duration_tmp) {
|
|
$detail_network_stat{$network}{$tmp}{duration} = $duration_tmp{$tmp};
|
|
$total_net_detail{$tmp}{duration} += $duration_tmp{$tmp};
|
|
$network_stat{$network}{duration} += $duration_tmp{$tmp};
|
|
$total_duration += $duration_tmp{$tmp};
|
|
}
|
|
}
|
|
$infile->close();
|
|
my $nnet = scalar keys %network_stat;
|
|
|
|
my $file = $outdir . '/network.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir);
|
|
$self->_print_header(\$out, $self->{menu}, $cal);
|
|
print $out $self->_print_title($Translate{'Network_title'}, $stat_date);
|
|
|
|
my $last = '23';
|
|
my $first = '00';
|
|
my $title = $Translate{'Hourly'} || 'Hourly';
|
|
my $unit = $Translate{'Hours'} || 'Hours';
|
|
if ($type eq 'day') {
|
|
$last = '31';
|
|
$first = '01';
|
|
$title = $Translate{'Daily'} || 'Daily';
|
|
$unit = $Translate{'Days'} || 'Days';
|
|
} elsif ($type eq 'month') {
|
|
$last = '12';
|
|
$first = '01';
|
|
$title = $Translate{'Monthly'} || 'Monthly';
|
|
$unit = $Translate{'Months'} || 'Months';
|
|
}
|
|
#
|
|
# my @hits = ();
|
|
# my @bytes = ();
|
|
# for ("$first" .. "$last") {
|
|
# if (exists $total_net_detail{$_}{hits}) {
|
|
# push(@hits, "[ $_, $total_net_detail{$_}{hits} ]");
|
|
# } else {
|
|
# push(@hits, "[ $_, 0 ]");
|
|
# }
|
|
# if (exists $total_net_detail{$_}{bytes}) {
|
|
# push(@bytes, "[ $_, " . int($total_net_detail{$_}{bytes}/1000000) . " ]");
|
|
# } else {
|
|
# push(@bytes, "[ $_, 0 ]");
|
|
# }
|
|
# }
|
|
# %total_net_detail = ();
|
|
#
|
|
# my $t1 = $Translate{'Graph_cache_hit_title'};
|
|
# $t1 =~ s/\%s/$title/;
|
|
# $t1 = "$t1 $stat_date";
|
|
# my $xlabel = $unit || '';
|
|
# my $ylabel = $Translate{'Requests_graph'} || 'Requests';
|
|
# my $network_hits = $self->flotr2_bargraph(1, 'network_hits', $type, $t1, $xlabel, $ylabel,
|
|
# join(',', @hits), $Translate{'Hit_graph'} );
|
|
# @hits = ();
|
|
# print $out qq{<table class="graphs"><tr><td>$network_hits</td>};
|
|
# $network_hits = '';
|
|
#
|
|
#
|
|
# $t1 = $Translate{'Graph_cache_byte_title'};
|
|
# $t1 =~ s/\%s/$title/;
|
|
# $t1 = "$t1 $stat_date";
|
|
# $xlabel = $unit || '';
|
|
# $ylabel = $Translate{'Megabytes_graph'} || $Translate{'Megabytes'};
|
|
# my $network_bytes = $self->flotr2_bargraph(1, 'network_bytes', $type, $t1, $xlabel, $ylabel,
|
|
# join(',', @bytes), $Translate{'Bytes'} );
|
|
# @bytes = ();
|
|
#
|
|
# print $out qq{<td>$network_bytes</td></tr></table>};
|
|
# $network_bytes = '';
|
|
print $out "<h3>$Translate{'Network_number'}: $nnet</h3>\n";
|
|
print $out qq{
|
|
<table class="sortable stata">
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Network_link'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
<th>$Translate{'Duration'} (%)</th>
|
|
};
|
|
print $out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
<th>$Translate{'Users'}</th>
|
|
<th>$Translate{'Largest'}</th>
|
|
<th style="text-align: left;">$Translate{'Url'}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
if (!-d "$outdir/networks") {
|
|
mkdir("$outdir/networks", 0755) || return;
|
|
}
|
|
foreach my $net (sort { $network_stat{$b}{"$self->{OrderNetwork}"} <=> $network_stat{$a}{"$self->{OrderNetwork}"} } keys %network_stat) {
|
|
|
|
my $h_percent = '0.0';
|
|
$h_percent = sprintf("%2.2f", ($network_stat{$net}{hits}/$total_hit) * 100) if ($total_hit);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($network_stat{$net}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $d_percent = '0.0';
|
|
$d_percent = sprintf("%2.2f", ($network_stat{$net}{duration}/$total_duration) * 100) if ($total_duration);
|
|
$network_stat{$net}{duration} = &parse_duration(int($network_stat{$net}{duration}/1000));
|
|
my $total_cost = sprintf("%2.2f", int($network_stat{$net}{bytes}/1000000) * $self->{CostPrice});
|
|
my $show = $net;
|
|
if ($net =~ /^(\d+\.\d+\.\d+)/) {
|
|
$show = "$1.0";
|
|
foreach my $n (keys %{$self->{NetworkAlias}}) {
|
|
if ($show =~ /$self->{NetworkAlias}->{$n}/) {
|
|
$show = $n;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
my $comma_bytes = $self->format_bytes($network_stat{$net}{bytes});
|
|
print $out qq{
|
|
<tr>
|
|
<td><a href="networks/$net/$net.html">$show</a></td>
|
|
<td>$network_stat{$net}{hits} <span class="italicPercent">($h_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
<td>$network_stat{$net}{duration} <span class="italicPercent">($d_percent)</span></td>
|
|
};
|
|
print $out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
|
|
if (!-d "$outdir/networks/$net") {
|
|
mkdir("$outdir/networks/$net", 0755) || return;
|
|
}
|
|
my $outnet = new IO::File;
|
|
$outnet->open(">$outdir/networks/$net/$net.html") || return;
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir, '../../');
|
|
$self->_print_header(\$outnet, $self->{menu2}, $cal);
|
|
print $outnet $self->_print_title("$Translate{'Network_title'} $show -", $stat_date);
|
|
|
|
my @hits = ();
|
|
my @bytes = ();
|
|
for ("$first" .. "$last") {
|
|
if (exists $detail_network_stat{$net}{$_}{hits}) {
|
|
push(@hits, "[ $_, " . $detail_network_stat{$net}{$_}{hits} . " ]");
|
|
} else {
|
|
push(@hits, "[ $_, 0 ]");
|
|
}
|
|
if (exists $detail_network_stat{$net}{$_}{bytes}) {
|
|
push(@bytes, "[ $_, " . int($detail_network_stat{$net}{$_}{bytes}/1000000) . " ]");
|
|
} else {
|
|
push(@bytes, "[ $_, 0 ]");
|
|
}
|
|
}
|
|
delete $detail_network_stat{$net};
|
|
|
|
my $t1 = $Translate{'Graph_cache_hit_title'};
|
|
$t1 =~ s/\%s/$title $show/;
|
|
$t1 = "$t1 $stat_date";
|
|
my $xlabel = $unit || '';
|
|
my $ylabel = $Translate{'Requests_graph'} || 'Requests';
|
|
my $network_hits = $self->flotr2_bargraph(1, 'network_hits', $type, $t1, $xlabel, $ylabel,
|
|
join(',', @hits), $Translate{'Hit_graph'} );
|
|
@hits = ();
|
|
print $outnet qq{<table class="graphs"><tr><td>$network_hits</td>};
|
|
$network_hits = '';
|
|
|
|
$t1 = $Translate{'Graph_cache_byte_title'};
|
|
$t1 =~ s/\%s/$title/;
|
|
$t1 = "$t1 $stat_date";
|
|
$xlabel = $unit || '';
|
|
$ylabel = $Translate{'Megabytes_graph'} || $Translate{'Megabytes'};
|
|
my $network_bytes = $self->flotr2_bargraph(1, 'network_bytes', $type, $t1, $xlabel, $ylabel,
|
|
join(',', @bytes), $Translate{'Bytes'} );
|
|
@bytes = ();
|
|
|
|
print $outnet qq{<td>$network_bytes</td></tr></table>};
|
|
$network_bytes = '';
|
|
my $retuser = $self->_print_netuser_stat($outdir, \$outnet, $net);
|
|
my $comma_largest = $self->format_bytes($network_stat{$net}{largest_file});
|
|
print $out qq{
|
|
<td>$retuser</td>
|
|
<td>$comma_largest</td>
|
|
<td style="text-align: left;">$network_stat{$net}{url}</td>
|
|
</tr>
|
|
};
|
|
print $outnet qq{
|
|
<div class="uplink">
|
|
<a href="#atop"><span class="iconUpArrow">$Translate{'Up_link'}</span></a>
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$outnet);
|
|
$outnet->close();
|
|
}
|
|
print $out "</tbody></table>\n";
|
|
|
|
print $out qq{
|
|
<div class="uplink">
|
|
<a href="#atop"><span class="iconUpArrow">$Translate{'Up_link'}</span></a>
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
}
|
|
|
|
sub _print_user_stat
|
|
{
|
|
my ($self, $outdir, $year, $month, $day) = @_;
|
|
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_user.dat") || return;
|
|
my %user_stat = ();
|
|
my %detail_user_stat = ();
|
|
my %total_user_detail = ();
|
|
my $total_hit = 0;
|
|
my $total_bytes = 0;
|
|
my $total_duration = 0;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($user, $data) = split(/\s/, $l);
|
|
$data =~ /hits_$type=([^;]+);bytes_$type=([^;]+);duration_$type=([^;]+);largest_file_size=([^;]*);largest_file_url=(.*)/;
|
|
my $hits = $1 || '';
|
|
my $bytes = $2 || '';
|
|
my $duration = $3 || '';
|
|
$user_stat{$user}{largest_file} = $4;
|
|
$user_stat{$user}{url} = $5;
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
$duration =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$detail_user_stat{$user}{$tmp}{hits} = $hits_tmp{$tmp};
|
|
$total_user_detail{$tmp}{hits} += $hits_tmp{$tmp};
|
|
$user_stat{$user}{hits} += $hits_tmp{$tmp};
|
|
$total_hit += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$detail_user_stat{$user}{$tmp}{bytes} = $bytes_tmp{$tmp};
|
|
$total_user_detail{$tmp}{bytes} += $bytes_tmp{$tmp};
|
|
$user_stat{$user}{bytes} += $bytes_tmp{$tmp};
|
|
$total_bytes += $bytes_tmp{$tmp};
|
|
}
|
|
my %duration_tmp = split(/[:,]/, $duration);
|
|
foreach my $tmp (sort {$a <=> $b} keys %duration_tmp) {
|
|
$detail_user_stat{$user}{$tmp}{duration} = $duration_tmp{$tmp};
|
|
$total_user_detail{$tmp}{duration} += $duration_tmp{$tmp};
|
|
$user_stat{$user}{duration} += $duration_tmp{$tmp};
|
|
$total_duration += $duration_tmp{$tmp};
|
|
}
|
|
}
|
|
$infile->close();
|
|
my $nuser = scalar keys %user_stat;
|
|
|
|
my $file = $outdir . '/user.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir);
|
|
$self->_print_header(\$out, $self->{menu}, $cal);
|
|
|
|
my $last = '23';
|
|
my $first = '00';
|
|
my $title = $Translate{'Hourly'} || 'Hourly';
|
|
my $unit = $Translate{'Hours'} || 'Hours';
|
|
if ($type eq 'day') {
|
|
$last = '31';
|
|
$first = '01';
|
|
$title = $Translate{'Daily'} || 'Daily';
|
|
$unit = $Translate{'Days'} || 'Days';
|
|
} elsif ($type eq 'month') {
|
|
$last = '12';
|
|
$first = '01';
|
|
$title = $Translate{'Monthly'} || 'Monthly';
|
|
$unit = $Translate{'Months'} || 'Months';
|
|
}
|
|
|
|
# my @hits = ();
|
|
# my @bytes = ();
|
|
# for ("$first" .. "$last") {
|
|
# if (exists $total_user_detail{$_}{hits}) {
|
|
# push(@hits, "[ $_, $total_user_detail{$_}{hits} ]");
|
|
# } else {
|
|
# push(@hits, "[ $_, 0 ]");
|
|
# }
|
|
# if (exists $total_user_detail{$_}{bytes}) {
|
|
# push(@bytes, "[ $_, " . int($total_user_detail{$_}{bytes}/1000000) . " ]");
|
|
# } else {
|
|
# push(@bytes, "[ $_, 0 ]");
|
|
# }
|
|
# }
|
|
%total_user_detail = ();
|
|
|
|
print $out $self->_print_title($Translate{'User_title'}, $stat_date);
|
|
|
|
print $out "<h3>$Translate{'User_number'}: $nuser</h3>\n";
|
|
#
|
|
# my $t1 = $Translate{'Graph_cache_hit_title'};
|
|
# $t1 =~ s/\%s/$title/;
|
|
# $t1 = "$t1 $stat_date";
|
|
# my $xlabel = $unit || '';
|
|
# my $ylabel = $Translate{'Requests_graph'} || 'Requests';
|
|
# my $user_hits = $self->flotr2_bargraph(1, 'user_hits', $type, $t1, $xlabel, $ylabel,
|
|
# join(',', @hits), $Translate{'Hit_graph'});
|
|
# @hits = ();
|
|
# print $out qq{<table class="graphs"><tr><td>$user_hits</td>};
|
|
# $user_hits = '';
|
|
#
|
|
# $t1 = $Translate{'Graph_cache_byte_title'};
|
|
# $t1 =~ s/\%s/$title/;
|
|
# $t1 = "$t1 $stat_date";
|
|
# $xlabel = $unit || '';
|
|
# $ylabel = $Translate{'Megabytes_graph'} || $Translate{'Megabytes'};
|
|
# my $user_bytes = $self->flotr2_bargraph(1, 'user_bytes', $type, $t1, $xlabel, $ylabel,
|
|
# join(',', @bytes), $Translate{'Bytes'});
|
|
# @bytes = ();
|
|
# print $out qq{<td>$user_bytes</td></tr></table>};
|
|
# $user_bytes = '';
|
|
#
|
|
print $out qq{
|
|
<table class="sortable stata" >
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Users'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
<th>$Translate{'Duration'} (%)</th>
|
|
};
|
|
print $out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
<th>$Translate{'Largest'}</th>
|
|
<th style="text-align: left;">$Translate{'Url'}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
if (!-d "$outdir/users") {
|
|
mkdir("$outdir/users", 0755) || return;
|
|
}
|
|
|
|
foreach my $usr (sort { $user_stat{$b}{"$self->{OrderUser}"} <=> $user_stat{$a}{"$self->{OrderUser}"} } keys %user_stat) {
|
|
my $h_percent = '0.0';
|
|
$h_percent = sprintf("%2.2f", ($user_stat{$usr}{hits}/$total_hit) * 100) if ($total_hit);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($user_stat{$usr}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $d_percent = '0.0';
|
|
$d_percent = sprintf("%2.2f", ($user_stat{$usr}{duration}/$total_duration) * 100) if ($total_duration);
|
|
$user_stat{$usr}{duration} = &parse_duration(int($user_stat{$usr}{duration}/1000));
|
|
my $total_cost = sprintf("%2.2f", int($user_stat{$usr}{bytes}/1000000) * $self->{CostPrice});
|
|
my $show = $usr;
|
|
foreach my $u (keys %{$self->{UserAlias}}) {
|
|
if ( grep($usr =~ /^$_$/, @{$self->{UserAlias}->{$u}}) ) {
|
|
$show = $u;
|
|
last;
|
|
}
|
|
}
|
|
my $url = &escape($usr);
|
|
my $comma_bytes = $self->format_bytes($user_stat{$usr}{bytes});
|
|
if ($self->{UrlReport}) {
|
|
print $out qq{
|
|
<tr>
|
|
<td><a href="users/$url/$url.html">$show</a></td>
|
|
};
|
|
} else {
|
|
print $out qq{
|
|
<tr>
|
|
<td>$show</td>
|
|
};
|
|
}
|
|
print $out qq{
|
|
<td>$user_stat{$usr}{hits} <span class="italicPercent">($h_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
<td>$user_stat{$usr}{duration} <span class="italicPercent">($d_percent)</span></td>
|
|
};
|
|
print $out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
my $comma_largest = $self->format_bytes($user_stat{$usr}{largest_file});
|
|
print $out qq{
|
|
<td>$comma_largest</td>
|
|
<td style="text-align: left;">$user_stat{$usr}{url}</td>
|
|
</tr>};
|
|
|
|
if (!-d "$outdir/users/$url") {
|
|
mkdir("$outdir/users/$url", 0755) || return;
|
|
}
|
|
my $outusr = new IO::File;
|
|
$outusr->open(">$outdir/users/$url/$url.html") || return;
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir, '../../');
|
|
$self->_print_header(\$outusr, $self->{menu2}, $cal);
|
|
print $outusr $self->_print_title("$Translate{'User_title'} $usr -", $stat_date);
|
|
|
|
my @hits = ();
|
|
my @bytes = ();
|
|
for ("$first" .. "$last") {
|
|
if (exists $detail_user_stat{$usr}{$_}{hits}) {
|
|
push(@hits, "[ $_, $detail_user_stat{$usr}{$_}{hits} ]");
|
|
} else {
|
|
push(@hits, "[ $_, 0 ]");
|
|
}
|
|
if (exists $detail_user_stat{$usr}{$_}{bytes}) {
|
|
push(@bytes, "[ $_, " . int($detail_user_stat{$usr}{$_}{bytes}/1000000) . " ]");
|
|
} else {
|
|
push(@bytes, "[ $_, 0 ]");
|
|
}
|
|
}
|
|
delete $detail_user_stat{$usr};
|
|
|
|
my $t1 = $Translate{'Graph_cache_hit_title'};
|
|
$t1 =~ s/\%s/$title $show/;
|
|
$t1 = "$t1 $stat_date";
|
|
my $xlabel = $unit || '';
|
|
my $ylabel = $Translate{'Requests_graph'} || 'Requests';
|
|
my $user_hits = $self->flotr2_bargraph(1, 'user_hits', $type, $t1, $xlabel, $ylabel,
|
|
join(',', @hits), $Translate{'Hit_graph'});
|
|
@hits = ();
|
|
print $outusr qq{<table class="graphs"><tr><td>$user_hits</td>};
|
|
$user_hits = '';
|
|
|
|
$t1 = $Translate{'Graph_cache_byte_title'};
|
|
$t1 =~ s/\%s/$title $show/;
|
|
$t1 = "$t1 $stat_date";
|
|
$xlabel = $unit || '';
|
|
$ylabel = $Translate{'Megabytes_graph'} || $Translate{'Megabytes'};
|
|
my $user_bytes = $self->flotr2_bargraph(1, 'user_bytes', $type, $t1, $xlabel, $ylabel,
|
|
join(',', @bytes), $Translate{'Bytes'});
|
|
@bytes = ();
|
|
print $outusr qq{<td>$user_bytes</td></tr></table>};
|
|
$user_bytes = '';
|
|
|
|
delete $user_stat{$usr};
|
|
if ($self->{UrlReport}) {
|
|
$self->_print_user_detail(\$outusr, $outdir, $usr);
|
|
}
|
|
$self->_print_footer(\$outusr);
|
|
$outusr->close();
|
|
}
|
|
print $out qq{
|
|
</tbody>
|
|
</table>
|
|
};
|
|
|
|
print $out qq{
|
|
<div class="uplink">
|
|
<a href="#atop"><span class="iconUpArrow">$Translate{'Up_link'}</span></a>
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
|
|
return $nuser;
|
|
}
|
|
|
|
sub _print_netuser_stat
|
|
{
|
|
my ($self, $outdir, $out, $usrnet) = @_;
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_netuser.dat") || return;
|
|
my %netuser_stat = ();
|
|
my $total_hit = 0;
|
|
my $total_bytes = 0;
|
|
my $total_duration = 0;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($network, $user, $data) = split(/\t/, $l);
|
|
if (!$data) {
|
|
# Assume backward compatibility
|
|
$l =~ s/^(.*)\s([^\s]+)\shits=/hits=/;
|
|
$network = $1;
|
|
$user = $2;
|
|
$data = $l;
|
|
}
|
|
next if ($network ne $usrnet);
|
|
$data =~ /^hits=(\d+);bytes=(\d+);duration=(\d+);largest_file_size=([^;]*);largest_file_url=(.*)/;
|
|
$netuser_stat{$user}{hits} = $1;
|
|
$netuser_stat{$user}{bytes} = $2;
|
|
$netuser_stat{$user}{duration} = $3;
|
|
$netuser_stat{$user}{largest_file} = $4;
|
|
$total_hit += $1;
|
|
$total_bytes += $2;
|
|
$total_duration += $3;
|
|
$netuser_stat{$user}{url} = $5;
|
|
}
|
|
$infile->close();
|
|
my $nuser = scalar keys %netuser_stat;
|
|
|
|
print $$out qq{
|
|
<h3>$Translate{'User_number'}: $nuser</h3>
|
|
};
|
|
print $$out qq{
|
|
<table class="sortable stata">
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Users'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
<th>$Translate{'Duration'} (%)</th>
|
|
};
|
|
print $$out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $$out qq{
|
|
<th>$Translate{'Largest'}</th>
|
|
<th style="text-align: left;">$Translate{'Url'}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
foreach my $usr (sort { $netuser_stat{$b}{"$self->{OrderUser}"} <=> $netuser_stat{$a}{"$self->{OrderUser}"} } keys %netuser_stat) {
|
|
my $h_percent = '0.0';
|
|
$h_percent = sprintf("%2.2f", ($netuser_stat{$usr}{hits}/$total_hit) * 100) if ($total_hit);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($netuser_stat{$usr}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $d_percent = '0.0';
|
|
$d_percent = sprintf("%2.2f", ($netuser_stat{$usr}{duration}/$total_duration) * 100) if ($total_duration);
|
|
$netuser_stat{$usr}{duration} = &parse_duration(int($netuser_stat{$usr}{duration}/1000));
|
|
my $total_cost = sprintf("%2.2f", int($netuser_stat{$usr}{bytes}/1000000) * $self->{CostPrice});
|
|
my $show = $usr;
|
|
foreach my $u (keys %{$self->{UserAlias}}) {
|
|
if ( grep($usr =~ /^$_$/, @{$self->{UserAlias}->{$u}}) ) {
|
|
$show = $u;
|
|
last;
|
|
}
|
|
}
|
|
my $url = &escape($usr);
|
|
my $comma_bytes = $self->format_bytes($netuser_stat{$usr}{bytes});
|
|
if ($self->{UrlReport}) {
|
|
print $$out qq{
|
|
<tr>
|
|
<td><a href="../../users/$url/$url.html">$show</a></td>
|
|
};
|
|
} else {
|
|
print $$out qq{
|
|
<tr>
|
|
<td>$show</td>
|
|
};
|
|
}
|
|
print $$out qq{
|
|
<td>$netuser_stat{$usr}{hits} <span class="italicPercent">($h_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
<td>$netuser_stat{$usr}{duration} <span class="italicPercent">($d_percent)</span></td>
|
|
};
|
|
print $$out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
my $comma_largest = $self->format_bytes($netuser_stat{$usr}{largest_file});
|
|
print $$out qq{
|
|
<td>$comma_largest</td>
|
|
<td style="text-align: left;">$netuser_stat{$usr}{url}</td>
|
|
</tr>};
|
|
}
|
|
print $$out qq{
|
|
</tbody>
|
|
</table>
|
|
};
|
|
return $nuser;
|
|
}
|
|
|
|
sub _print_user_detail
|
|
{
|
|
my ($self, $out, $outdir, $usr) = @_;
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_user_url.dat") || return;
|
|
my %url_stat = ();
|
|
my $total_hit = 0;
|
|
my $total_bytes = 0;
|
|
my $total_duration = 0;
|
|
my $ok = 0;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($user, $data) = split(/\s/, $l);
|
|
last if (($user ne $usr) && $ok);
|
|
next if ($user ne $usr);
|
|
$ok = 1;
|
|
if ($data =~ /hits=(\d+);bytes=(\d+);duration=(\d+);url=(.*)/) {
|
|
$url_stat{$4}{hits} = $1;
|
|
$url_stat{$4}{bytes} = $2;
|
|
$url_stat{$4}{duration} = $3;
|
|
$total_hit += $1;
|
|
$total_bytes += $2;
|
|
$total_duration += $3;
|
|
} elsif ($data =~ /hits=(\d+);bytes=(\d+);duration=(\d+);first=([^;]*);last=([^;]*);url=(.*)/) {
|
|
$url_stat{$6}{hits} = $1;
|
|
$url_stat{$6}{bytes} = $2;
|
|
$url_stat{$6}{duration} = $3;
|
|
$url_stat{$6}{firsthit} = $4 if (!$url_stat{$6}{firsthit});
|
|
$url_stat{$6}{lasthit} = $5;
|
|
$total_hit += $1;
|
|
$total_bytes += $2;
|
|
$total_duration += $3;
|
|
}
|
|
}
|
|
$infile->close();
|
|
my $nurl = scalar keys %url_stat;
|
|
|
|
print $$out qq{
|
|
<h3>$Translate{'Url_number'}: $nurl</h3>
|
|
};
|
|
print $$out qq{
|
|
<table class="sortable stata">
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Url'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
<th>$Translate{'Duration'} (%)</th>
|
|
};
|
|
print $$out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $$out qq{
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
|
|
foreach my $url (sort { $url_stat{$b}{"$self->{OrderUrl}"} <=> $url_stat{$a}{"$self->{OrderUrl}"} } keys %url_stat) {
|
|
my $h_percent = '0.0';
|
|
$h_percent = sprintf("%2.2f", ($url_stat{$url}{hits}/$total_hit) * 100) if ($total_hit);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($url_stat{$url}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $d_percent = '0.0';
|
|
$d_percent = sprintf("%2.2f", ($url_stat{$url}{duration}/$total_duration) * 100) if ($total_duration);
|
|
$url_stat{$url}{duration} = &parse_duration(int($url_stat{$url}{duration}/1000));
|
|
my $total_cost = sprintf("%2.2f", int($url_stat{$url}{bytes}/1000000) * $self->{CostPrice});
|
|
my $comma_bytes = $self->format_bytes($url_stat{$url}{bytes});
|
|
print $$out qq{
|
|
<tr>
|
|
<td><a href="http://$url/" target="_blank" class="domainLink">$url</a></td>
|
|
<td>$url_stat{$url}{hits} <span class="italicPercent">($h_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
<td>$url_stat{$url}{duration} <span class="italicPercent">($d_percent)</span></td>
|
|
};
|
|
print $$out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
print $$out qq{
|
|
</tr>};
|
|
}
|
|
print $$out qq{
|
|
</tbody>
|
|
</table>
|
|
};
|
|
|
|
}
|
|
|
|
sub _print_top_url_stat
|
|
{
|
|
my ($self, $outdir, $year, $month, $day) = @_;
|
|
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_user_url.dat") || return;
|
|
my %url_stat = ();
|
|
my $total_hits = 0;
|
|
my $total_bytes = 0;
|
|
my $total_duration = 0;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($user, $data) = split(/\s/, $l);
|
|
if ($data =~ /hits=(\d+);bytes=(\d+);duration=(\d+);url=(.*)/) {
|
|
$url_stat{$4}{hits} = $1;
|
|
$url_stat{$4}{bytes} = $2;
|
|
$url_stat{$4}{duration} = $3;
|
|
$total_hits += $1;
|
|
$total_bytes += $2;
|
|
$total_duration += $3;
|
|
} elsif ($data =~ /hits=(\d+);bytes=(\d+);duration=(\d+);first=([^;]*);last=([^;]*);url=(.*)/) {
|
|
$url_stat{$6}{hits} = $1;
|
|
$url_stat{$6}{bytes} = $2;
|
|
$url_stat{$6}{duration} = $3;
|
|
$url_stat{$6}{firsthit} = $4 if (!$url_stat{$6}{firsthit});
|
|
$url_stat{$6}{lasthit} = $5;
|
|
$total_hits += $1;
|
|
$total_bytes += $2;
|
|
$total_duration += $3;
|
|
}
|
|
}
|
|
$infile->close();
|
|
|
|
my $nurl = scalar keys %url_stat;
|
|
|
|
my $file = $outdir . '/url.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir);
|
|
$self->_print_header(\$out, $self->{menu}, $cal);
|
|
for my $tpe ('Hits', 'Bytes', 'Duration') {
|
|
my $t1 = $Translate{"Url_${tpe}_title"};
|
|
$t1 =~ s/\%d/$self->{TopNumber}/;
|
|
if ($tpe eq 'Hits') {
|
|
print $out $self->_print_title($t1, $stat_date);
|
|
print $out "<h3>$Translate{'Url_number'}: $nurl</h3>\n";
|
|
} else {
|
|
print $out "<h4>$t1 $stat_date</h4><div class=\"line-separator\"></div>\n";
|
|
}
|
|
print $out qq{
|
|
<table class="sortable stata">
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Url'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
<th>$Translate{'Duration'} (%)</th>
|
|
};
|
|
print $out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
<th>$Translate{'First_visit'}</th>
|
|
<th>$Translate{'Last_visit'}</th>
|
|
} if ($day);
|
|
print $out qq{
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
my $i = 0;
|
|
foreach my $u (sort { $url_stat{$b}{"\L$tpe\E"} <=> $url_stat{$a}{"\L$tpe\E"} } keys %url_stat) {
|
|
my $h_percent = '0.0';
|
|
$h_percent = sprintf("%2.2f", ($url_stat{$u}{hits}/$total_hits) * 100) if ($total_hits);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($url_stat{$u}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $d_percent = '0.0';
|
|
$d_percent = sprintf("%2.2f", ($url_stat{$u}{duration}/$total_duration) * 100) if ($total_duration);
|
|
my $total_cost = sprintf("%2.2f", int($url_stat{$u}{bytes}/1000000) * $self->{CostPrice});
|
|
my $duration = &parse_duration(int($url_stat{$u}{duration}/1000));
|
|
my $comma_bytes = $self->format_bytes($url_stat{$u}{bytes});
|
|
my $firsthit = '-';
|
|
if ($url_stat{$u}{firsthit}) {
|
|
$firsthit = ucfirst(strftime("%b %d %T", localtime($url_stat{$u}{firsthit})));
|
|
}
|
|
my $lasthit = '-';
|
|
if ($url_stat{$u}{lasthit}) {
|
|
$lasthit = ucfirst(strftime("%b %d %T", localtime($url_stat{$u}{lasthit})));
|
|
}
|
|
if ($type eq 'hour') {
|
|
if ($url_stat{$u}{firsthit}) {
|
|
$firsthit = ucfirst(strftime("%T", localtime($url_stat{$u}{firsthit})));
|
|
} else {
|
|
$firsthit = '-';
|
|
}
|
|
if ($url_stat{$u}{lasthit}) {
|
|
$lasthit = ucfirst(strftime("%T", localtime($url_stat{$u}{lasthit})));
|
|
} else {
|
|
$firsthit = '-';
|
|
}
|
|
}
|
|
print $out qq{
|
|
<tr>
|
|
<td><a href="http://$u/" target="_blank" class="domainLink">$u</a></td>
|
|
<td>$url_stat{$u}{hits} <span class="italicPercent">($h_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
<td>$duration <span class="italicPercent">($d_percent)</span></td>
|
|
};
|
|
print $out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
<td>$firsthit</td>
|
|
<td>$lasthit</td>
|
|
} if ($day);
|
|
print $out qq{
|
|
</tr>};
|
|
$i++;
|
|
last if ($i > $self->{TopNumber});
|
|
}
|
|
print $out qq{</tbody></table>};
|
|
}
|
|
|
|
print $out qq{
|
|
<div class="uplink">
|
|
<a href="#atop"><span class="iconUpArrow">$Translate{'Up_link'}</span></a>
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
|
|
return $nurl;
|
|
}
|
|
|
|
sub _print_top_domain_stat
|
|
{
|
|
my ($self, $outdir, $year, $month, $day) = @_;
|
|
|
|
my $stat_date = $self->set_date($year, $month, $day);
|
|
|
|
my $type = 'hour';
|
|
if (!$day) {
|
|
$type = 'day';
|
|
}
|
|
if (!$month) {
|
|
$type = 'month';
|
|
}
|
|
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/stat_user_url.dat") || return;
|
|
my %url_stat = ();
|
|
my %domain_stat = ();
|
|
my $total_hits = 0;
|
|
my $total_bytes = 0;
|
|
my $total_duration = 0;
|
|
my %perdomain = ();
|
|
my $url = '';
|
|
my $hits = 0;
|
|
my $bytes = 0;
|
|
my $duration = 0;
|
|
my $first = 0;
|
|
my $last = 0;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($user, $data) = split(/\s/, $l);
|
|
if ($data =~ /hits=(\d+);bytes=(\d+);duration=(\d+);url=(.*)/) {
|
|
$url = $4;
|
|
$hits = $1;
|
|
$bytes = $2;
|
|
$duration = $3;
|
|
} elsif ($data =~ /hits=(\d+);bytes=(\d+);duration=(\d+);first=([^;]*);last=([^;]*);url=(.*)/) {
|
|
$url = lc($6);
|
|
$hits = $1;
|
|
$bytes = $2;
|
|
$duration = $3;
|
|
$first = $4;
|
|
$last = $5;
|
|
}
|
|
$url =~ /(\.[^\.]+)$/;
|
|
if ($url !~ /\.\d+$/) {
|
|
if ($url =~ /([^\.]+)(\.[^\.]+)$/) {
|
|
$perdomain{$2}{hits} += $hits;
|
|
$perdomain{$2}{bytes} += $bytes;
|
|
$domain_stat{"$1$2"}{hits} = $hits;
|
|
$domain_stat{"$1$2"}{bytes} = $bytes;
|
|
$domain_stat{"$1$2"}{duration} = $duration;
|
|
$domain_stat{"$1$2"}{firsthit} = $first if (!$domain_stat{"$1$2"}{firsthit});
|
|
$domain_stat{"$1$2"}{lasthit} = $last;
|
|
}
|
|
} else {
|
|
$perdomain{'other'}{hits} += $hits;
|
|
$perdomain{'other'}{bytes} += $bytes;
|
|
$domain_stat{'unknown'}{hits} = $hits;
|
|
$domain_stat{'unknown'}{bytes} = $bytes;
|
|
$domain_stat{'unknown'}{duration} = $duration;
|
|
$domain_stat{'unknown'}{firsthit} = $first if (!$domain_stat{'unknown'}{firsthit});
|
|
$domain_stat{'unknown'}{lasthit} = $last;
|
|
}
|
|
$total_hits += $hits;
|
|
$total_bytes += $bytes;
|
|
$total_duration += $duration;
|
|
}
|
|
$infile->close();
|
|
|
|
my $nurl = scalar keys %domain_stat;
|
|
|
|
my $file = $outdir . '/domain.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
my $cal = $self->_get_calendar($stat_date, $type, $outdir);
|
|
$self->_print_header(\$out, $self->{menu}, $cal);
|
|
for my $tpe ('Hits', 'Bytes', 'Duration') {
|
|
my $t1 = $Translate{"Domain_${tpe}_title"};
|
|
$t1 =~ s/\%d/$self->{TopNumber}/;
|
|
|
|
if ($tpe eq 'Hits') {
|
|
print $out $self->_print_title($t1, $stat_date);
|
|
print $out "<h3>$Translate{'Domain_number'}: $nurl</h3>\n";
|
|
|
|
my %data = ();
|
|
my $total_hits = 0;
|
|
foreach my $dom (keys %perdomain) {
|
|
$total_hits += $perdomain{$dom}{hits};
|
|
}
|
|
$total_hits ||= 1;
|
|
foreach my $dom (keys %perdomain) {
|
|
if (($perdomain{$dom}{hits}/$total_hits)*100 > $self->{MinPie}) {
|
|
$data{$dom} = $perdomain{$dom}{hits};
|
|
} else {
|
|
$data{'others'} += $perdomain{$dom}{hits};
|
|
}
|
|
}
|
|
my $title = "$Translate{'Domain_graph_hits_title'} $stat_date";
|
|
my $domain_hits = $self->flotr2_piegraph(1, 'domain_hits', $title, $Translate{'Domains_graph'}, '', %data);
|
|
print $out qq{<table class="graphs"><tr><td>$domain_hits</td>};
|
|
$domain_hits = '';
|
|
%data = ();
|
|
my $total_bytes = 0;
|
|
foreach my $dom (keys %perdomain) {
|
|
$total_bytes += $perdomain{$dom}{bytes};
|
|
}
|
|
$total_bytes ||= 1;
|
|
foreach my $dom (keys %perdomain) {
|
|
if (($perdomain{$dom}{bytes}/$total_bytes)*100 > $self->{MinPie}) {
|
|
$data{$dom} = int($perdomain{$dom}{bytes}/1000000);
|
|
} else {
|
|
$data{'others'} += $perdomain{$dom}{bytes};
|
|
}
|
|
}
|
|
$data{'others'} = int($data{'others'}/1000000);
|
|
$title = "$Translate{'Domain_graph_bytes_title'} $stat_date";
|
|
my $domain_bytes = $self->flotr2_piegraph(1, 'domain_bytes', $title, $Translate{'Domains_graph'}, '', %data);
|
|
print $out qq{<td>$domain_bytes</td></tr></table>};
|
|
$domain_bytes = '';
|
|
%data = ();
|
|
} else {
|
|
print $out "<h4>$t1 $stat_date</h4><div class=\"line-separator\"></div>\n";
|
|
}
|
|
print $out qq{
|
|
<table class="sortable stata">
|
|
<thead>
|
|
<tr>
|
|
<th>$Translate{'Url'}</th>
|
|
<th>$Translate{'Requests'} (%)</th>
|
|
<th>$Translate{$self->{TransfertUnit}} (%)</th>
|
|
<th>$Translate{'Duration'} (%)</th>
|
|
};
|
|
print $out qq{
|
|
<th>$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
<th>$Translate{'First_visit'}</th>
|
|
<th>$Translate{'Last_visit'}</th>
|
|
} if ($day);
|
|
print $out qq{
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
my $i = 0;
|
|
foreach (sort { $domain_stat{$b}{"\L$tpe\E"} <=> $domain_stat{$a}{"\L$tpe\E"} } keys %domain_stat) {
|
|
my $h_percent = '0.0';
|
|
$h_percent = sprintf("%2.2f", ($domain_stat{$_}{hits}/$total_hits) * 100) if ($total_hits);
|
|
my $b_percent = '0.0';
|
|
$b_percent = sprintf("%2.2f", ($domain_stat{$_}{bytes}/$total_bytes) * 100) if ($total_bytes);
|
|
my $d_percent = '0.0';
|
|
$d_percent = sprintf("%2.2f", ($domain_stat{$_}{duration}/$total_duration) * 100) if ($total_duration);
|
|
my $total_cost = sprintf("%2.2f", int($domain_stat{$_}{bytes}/1000000) * $self->{CostPrice});
|
|
my $duration = &parse_duration(int($domain_stat{$_}{duration}/1000));
|
|
my $comma_bytes = $self->format_bytes($domain_stat{$_}{bytes});
|
|
my $firsthit = '-';
|
|
if ($domain_stat{$_}{firsthit}) {
|
|
$firsthit = ucfirst(strftime("%b %d %T", localtime($domain_stat{$_}{firsthit})));
|
|
}
|
|
my $lasthit = '-';
|
|
if ($domain_stat{$_}{lasthit}) {
|
|
$lasthit = ucfirst(strftime("%b %d %T", localtime($domain_stat{$_}{lasthit})));
|
|
}
|
|
if ($type eq 'hour') {
|
|
if ($domain_stat{$_}{firsthit}) {
|
|
$firsthit = ucfirst(strftime("%T", localtime($domain_stat{$_}{firsthit})));
|
|
} else {
|
|
$firsthit = '-';
|
|
}
|
|
if ($domain_stat{$_}{lasthit}) {
|
|
$lasthit = ucfirst(strftime("%T", localtime($domain_stat{$_}{lasthit})));
|
|
} else {
|
|
$lasthit = '-';
|
|
}
|
|
}
|
|
print $out qq{
|
|
<tr>
|
|
<td>*.$_</td>
|
|
<td>$domain_stat{$_}{hits} <span class="italicPercent">($h_percent)</span></td>
|
|
<td>$comma_bytes <span class="italicPercent">($b_percent)</span></td>
|
|
<td>$duration <span class="italicPercent">($d_percent)</span></td>
|
|
};
|
|
print $out qq{
|
|
<td>$total_cost</td>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
<td>$firsthit</td>
|
|
<td>$lasthit</td>
|
|
} if ($day);
|
|
print $out qq{
|
|
</tr>};
|
|
$i++;
|
|
last if ($i > $self->{TopNumber});
|
|
}
|
|
print $out qq{</tbody></table>};
|
|
}
|
|
|
|
print $out qq{
|
|
<div class="uplink">
|
|
<a href="#atop"><span class="iconUpArrow">$Translate{'Up_link'}</span></a>
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
|
|
return $nurl;
|
|
}
|
|
|
|
sub _gen_summary
|
|
{
|
|
my ($self, $outdir) = @_;
|
|
|
|
# Get all day subdirectory
|
|
opendir(DIR, "$outdir") or die "ERROR: Can't read directory $outdir, $!\n";
|
|
my @dirs = grep { /^\d{4}$/ && -d "$outdir/$_" } readdir(DIR);
|
|
closedir DIR;
|
|
|
|
my %code_stat = ();
|
|
my %total_request = ();
|
|
my %total_bytes = ();
|
|
foreach my $d (@dirs) {
|
|
# Load code statistics
|
|
my $infile = new IO::File;
|
|
$infile->open("$outdir/$d/stat_code.dat") || return;
|
|
while(my $l = <$infile>) {
|
|
chomp($l);
|
|
my ($code, $data) = split(/\s/, $l);
|
|
$data =~ /hits_month=([^;]+);bytes_month=(.*)/;
|
|
my $hits = $1 || '';
|
|
my $bytes = $2 || '';
|
|
$hits =~ s/,$//;
|
|
$bytes =~ s/,$//;
|
|
my %hits_tmp = split(/[:,]/, $hits);
|
|
foreach my $tmp (sort {$a <=> $b} keys %hits_tmp) {
|
|
$code_stat{$d}{$code}{request} += $hits_tmp{$tmp};
|
|
}
|
|
my %bytes_tmp = split(/[:,]/, $bytes);
|
|
foreach my $tmp (sort {$a <=> $b} keys %bytes_tmp) {
|
|
$code_stat{$d}{$code}{bytes} += $bytes_tmp{$tmp};
|
|
}
|
|
}
|
|
$infile->close();
|
|
$total_request{$d} = $code_stat{$d}{HIT}{request} + $code_stat{$d}{MISS}{request};
|
|
$total_bytes{$d} = $code_stat{$d}{HIT}{bytes} + $code_stat{$d}{MISS}{bytes};
|
|
}
|
|
my $file = $outdir . '/index.html';
|
|
my $out = new IO::File;
|
|
$out->open(">$file") || die "ERROR: Unable to open $file. $!\n";
|
|
# Print the HTML header
|
|
$self->_print_header(\$out);
|
|
my $colspn = 2;
|
|
$colspn = 3 if ($self->{CostPrice});
|
|
print $out qq{
|
|
<h4>$Translate{'Globals_Statistics'}</h4>
|
|
<div class="line-separator"></div>
|
|
<table class="stata">
|
|
<thead>
|
|
<tr>
|
|
<th class="nobg"></th>
|
|
<th colspan="2" scope="col" class="headerBlack">$Translate{'Requests'}</th>
|
|
<th colspan="2" scope="col" class="headerBlack">$Translate{$self->{TransfertUnit}}</th>
|
|
<th colspan="$colspn" scope="col" class="headerBlack">$Translate{'Total'}</th>
|
|
</tr>
|
|
<tr>
|
|
<th scope="col">$Translate{'Years'}</th>
|
|
<th scope="col">$Translate{'Hit'}</th>
|
|
<th scope="col">$Translate{'Miss'}</th>
|
|
<th scope="col">$Translate{'Hit'}</th>
|
|
<th scope="col">$Translate{'Miss'}</th>
|
|
<th scope="col">$Translate{'Requests'}</th>
|
|
<th scope="col">$Translate{$self->{TransfertUnit}}</th>
|
|
};
|
|
print $out qq{
|
|
<th scope="col">$Translate{'Cost'} $self->{Currency}</th>
|
|
} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
};
|
|
foreach my $year (sort {$b <=> $a} keys %code_stat) {
|
|
my $comma_bytes = $self->format_bytes($total_bytes{$year});
|
|
my $hit_bytes = $self->format_bytes($code_stat{$year}{HIT}{bytes});
|
|
my $miss_bytes = $self->format_bytes($code_stat{$year}{MISS}{bytes});
|
|
my $total_cost = sprintf("%2.2f", int($total_bytes{$year}/1000000) * $self->{CostPrice});
|
|
print $out qq{
|
|
<tr>
|
|
<td><a href="$year/index.html">$Translate{'Stat_label'} $year *</a></td>
|
|
<td>$code_stat{$year}{HIT}{request}</td>
|
|
<td>$code_stat{$year}{MISS}{request}</td>
|
|
<td>$hit_bytes</td>
|
|
<td>$miss_bytes</td>
|
|
<td>$total_request{$year}</td>
|
|
<td>$comma_bytes</td>
|
|
};
|
|
print $out qq{<td>$total_cost</td>} if ($self->{CostPrice});
|
|
print $out qq{</tr>};
|
|
}
|
|
print $out qq{
|
|
</tbody>
|
|
</table>
|
|
<blockquote class="notification">(*) $Translate{'Click_year_stat'}</blockquote>
|
|
|
|
<h4>$Translate{'Legend'}</h4>
|
|
<div class="line-separator"></div>
|
|
<div class="displayLegend">
|
|
<span class="legendeTitle">$Translate{'Hit'}</span>: <span class="descLegend">$Translate{'Hit_help'}</span><br/>
|
|
<span class="legendeTitle">$Translate{'Miss'}</span>: <span class="descLegend">$Translate{'Miss_help'}</span><br/>
|
|
};
|
|
print $out qq{<span class="legendeTitle">$Translate{'Cost'}</span>: <span class="descLegend">$Translate{'Cost_help'} $self->{CostPrice} $self->{Currency}</span><br/>} if ($self->{CostPrice});
|
|
print $out qq{
|
|
</div>
|
|
};
|
|
$self->_print_footer(\$out);
|
|
$out->close();
|
|
|
|
}
|
|
|
|
sub parse_config
|
|
{
|
|
my ($file) = @_;
|
|
|
|
die "FATAL: no configuration file!\n" if (!-e $file);
|
|
|
|
my %opt = ();
|
|
open(CONF, $file) or die "ERROR: can't open file $file, $!\n";
|
|
while (my $l = <CONF>) {
|
|
chomp($l);
|
|
next if (!$l || ($l =~ /^[\s\t]*#/));
|
|
my ($key, $val) = split(/[\s\t]+/, $l, 2);
|
|
$opt{$key} = $val;
|
|
}
|
|
close(CONF);
|
|
|
|
# Check config
|
|
if (!exists $opt{Output} || !-d $opt{Output}) {
|
|
print STDERR "Error: you must give a valid output directory. See option: Output\n";
|
|
exit 0;
|
|
}
|
|
if (!exists $opt{LogFile} || !-f $opt{LogFile}) {
|
|
print STDERR "Error: you must give the path to the Squid log file. See option: LogFile\n";
|
|
exit 0;
|
|
}
|
|
if (exists $opt{DateFormat}) {
|
|
if ( ($opt{DateFormat} !~ m#\%y#) || (($opt{DateFormat} !~ m#\%m#) && ($opt{DateFormat} !~ m#\%M#) )|| ($opt{DateFormat} !~ m#\%d#) ) {
|
|
print STDERR "Error: bad date format: $opt{DateFormat}, must have \%y, \%m or \%M, \%d. See DateFormat option.\n";
|
|
exit 0;
|
|
}
|
|
}
|
|
if ($opt{Lang} && !-e $opt{Lang}) {
|
|
print STDERR "Error: can't find translation file $opt{Lang}. See option: Lang\n";
|
|
exit 0;
|
|
}
|
|
if ($opt{ImgFormat} && !grep(/^$opt{ImgFormat}$/, 'png','jpg')) {
|
|
print STDERR "Error: unknown image format. See option: ImgFormat\n";
|
|
exit 0;
|
|
}
|
|
return %opt;
|
|
}
|
|
|
|
sub parse_network_aliases
|
|
{
|
|
my ($file) = @_;
|
|
|
|
return if (!$file || !-f $file);
|
|
|
|
my %alias = ();
|
|
open(ALIAS, $file) or die "ERROR: can't open network aliases file $file, $!\n";
|
|
my $i = 0;
|
|
while (my $l = <ALIAS>) {
|
|
chomp($l);
|
|
$i++;
|
|
next if (!$l || ($l =~ /^[\s\t]*#/));
|
|
$l =~ s/[\s\t]*#.*//;
|
|
my @data = split(/[\t]+/, $l, 2);
|
|
if ($#data == 1) {
|
|
push(@{$alias{$data[0]}}, split(/[\s,;\t]/, $data[1]));
|
|
} else {
|
|
die "ERROR: wrong format in network aliases file $file, line $i\n";
|
|
}
|
|
}
|
|
close(ALIAS);
|
|
|
|
return \%alias;
|
|
}
|
|
|
|
sub parse_user_aliases
|
|
{
|
|
my ($file) = @_;
|
|
|
|
return if (!$file || !-f $file);
|
|
|
|
my %alias = ();
|
|
open(ALIAS, $file) or die "ERROR: can't open user aliases file $file, $!\n";
|
|
my $i = 0;
|
|
while (my $l = <ALIAS>) {
|
|
chomp($l);
|
|
$i++;
|
|
next if (!$l || ($l =~ /^[\s\t]*#/));
|
|
my @data = split(/[\t]+/, $l, 2);
|
|
$data[0] =~ s/\s+/_/g; # Replace space, they are not allowed
|
|
if ($#data == 1) {
|
|
push(@{$alias{$data[0]}}, split(/[\s,;\t]/, $data[1]));
|
|
} else {
|
|
die "ERROR: wrong format in user aliases file $file, line $i\n";
|
|
}
|
|
}
|
|
close(ALIAS);
|
|
|
|
return \%alias;
|
|
}
|
|
|
|
sub parse_exclusion
|
|
{
|
|
my ($file) = @_;
|
|
|
|
return if (!$file || !-f $file);
|
|
|
|
my %exclusion = ();
|
|
open(EXCLUDED, $file) or die "ERROR: can't open exclusion file $file, $!\n";
|
|
while (my $l = <EXCLUDED>) {
|
|
chomp($l);
|
|
next if (!$l || ($l =~ /^[\s\t]*#/));
|
|
if ($l =~ m#^USER[\s\t]+(.*)#) {
|
|
push(@{$exclusion{users}}, split(m#[\s\t]+#, $1));
|
|
} elsif ($l =~ m#^CLIENT[\s\t]+(.*)#) {
|
|
push(@{$exclusion{clients}}, split(m#[\s\t]+#, $1));
|
|
} elsif ($l =~ m#^URI[\s\t]+(.*)#) {
|
|
push(@{$exclusion{uris}}, split(m#[\s\t]+#, $1));
|
|
} else {
|
|
# backward compatibility
|
|
push(@{$exclusion{all}}, $l);
|
|
}
|
|
}
|
|
close(EXCLUDED);
|
|
|
|
return %exclusion;
|
|
}
|
|
|
|
# User URL-encode
|
|
sub escape
|
|
{
|
|
my ($toencode) = @_;
|
|
|
|
return undef unless defined($toencode);
|
|
|
|
$toencode =~ s/[^a-zA-Z0-9_.-]/_/g;
|
|
|
|
return $toencode;
|
|
}
|
|
|
|
# Set date to user format
|
|
sub set_date
|
|
{
|
|
my ($self, $year, $month, $day) = @_;
|
|
|
|
my $date_format = $self->{DateFormat};
|
|
|
|
$date_format =~ s/\%y/$year/;
|
|
$date_format =~ s/\%m/$month/;
|
|
$date_format =~ s/\%d/$day/;
|
|
$date_format =~ s/\%M/$Translate{$month}/;
|
|
|
|
$date_format =~ s/([^\p{Letter}\p{Digit}]){2,3}/$1/;
|
|
$date_format =~ s/^[^\p{Letter}\p{Digit}]+//;
|
|
$date_format =~ s/[^\p{Letter}\p{Digit}]+$//;
|
|
|
|
return $date_format;
|
|
}
|
|
|
|
# Format bytes with comma for better reading
|
|
sub format_bytes
|
|
{
|
|
my ($self, $text) = @_;
|
|
|
|
if ($self->{TransfertUnitValue} > 1) {
|
|
$text = sprintf("%.2f", $text / $self->{TransfertUnitValue});
|
|
}
|
|
$text = reverse $text;
|
|
|
|
$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
|
|
|
|
return scalar reverse $text;
|
|
}
|
|
|
|
sub _print_title
|
|
{
|
|
my ($self, $title, $stat_date) = @_;
|
|
|
|
my $para = qq{
|
|
<h4>$title $stat_date</h4>
|
|
<div class="line-separator"></div>
|
|
};
|
|
|
|
return $para;
|
|
}
|
|
|
|
sub _get_calendar
|
|
{
|
|
my ($self, $stat_date, $type, $outdir, $prefix) = @_;
|
|
|
|
my $para = "<div id=\"calendar\">\n";
|
|
if ($type eq 'day') {
|
|
$para .= "<table><tr><th colspan=\"8\">$stat_date</th></tr>\n";
|
|
for my $i ('01' .. '32') {
|
|
$para .= "<tr>" if (grep(/^$i$/, '01', '09', '17','25'));
|
|
if ($i == 32) {
|
|
$para .= "<td> </td>";
|
|
} elsif (-f "$outdir/$i/index.html") {
|
|
$para .= "<td><a href=\"$prefix$i/index.html\">$i</a></td>";
|
|
} else {
|
|
$para .= "<td>$i</td>";
|
|
}
|
|
$para .= "</tr>\n" if (grep(/^$i$/, '08', '16', '24', '32'));
|
|
}
|
|
$para .= "</table>\n";
|
|
} elsif ($type eq 'month') {
|
|
$para .= "<table><tr><th colspan=\"4\">$stat_date</th></tr>\n";
|
|
for my $i ('01' .. '12') {
|
|
$para .= "<tr>" if (grep(/^$i$/, '01', '05', '09'));
|
|
if (-f "$outdir/$i/index.html") {
|
|
$para .= "<td><a href=\"$prefix$i/index.html\">$Translate{$i}</a></td>";
|
|
} else {
|
|
$para .= "<td>$Translate{$i}</td>";
|
|
}
|
|
$para .= "</tr>\n" if (grep(/^$i$/, '04', '08', '12'));
|
|
}
|
|
$para .= "</table>\n";
|
|
}
|
|
$para .= "</div>\n";
|
|
|
|
return $para;
|
|
}
|
|
|
|
sub anonymize_id
|
|
{
|
|
my $u_id = '';
|
|
while (length($u_id) < 16) {
|
|
my $c = chr(int(rand(127)));
|
|
if ($c =~ /[a-zA-Z0-9]/) {
|
|
$u_id .= $c;
|
|
}
|
|
}
|
|
|
|
return 'Anon' . $u_id;
|
|
|
|
}
|
|
|
|
|
|
sub flotr2_bargraph
|
|
{
|
|
my ($self, $buttonid, $divid, $xtype, $title, $xtitle, $ytitle, $data1, $legend1, $data2, $legend2, $data3, $legend3) = @_;
|
|
|
|
$data1 = "var d1 = [$data1];" if ($data1);
|
|
$data2 = "var d2 = [$data2];";
|
|
$data3 = "var d3 = [$data3];";
|
|
|
|
my $xlabel = '';
|
|
my $numticks = 0;
|
|
if ($xtype eq 'month') {
|
|
$xlabel = qq{var months = [ "$Translate{'01'}", "$Translate{'02'}", "$Translate{'03'}", "$Translate{'04'}", "$Translate{'05'}", "$Translate{'06'}", "$Translate{'07'}", "$Translate{'08'}", "$Translate{'09'}", "$Translate{'10'}", "$Translate{'11'}", "$Translate{'12'}" ];
|
|
return months[(x -1) % 12];
|
|
};
|
|
$numticks = 12;
|
|
} elsif ($xtype eq 'day') {
|
|
$xlabel = qq{var days = [01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31];
|
|
return days[(x - 1) % 31];
|
|
};
|
|
$numticks = 31;
|
|
} else {
|
|
$xlabel = qq{var hours = [00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23];
|
|
return hours[x % 24];
|
|
};
|
|
$numticks = 24;
|
|
}
|
|
return <<EOF;
|
|
<div id="$divid"></div>
|
|
<script type="text/javascript">
|
|
(function mouse_zoom(container) {
|
|
|
|
//document.writeln('<table class="tbbutton"><tr><td><input type="button" class="dldButton" value="To Image" id="toimage$buttonid" onclick="return false;">'+
|
|
// '<input type="button" class="dldButton" value="Download" id="download$buttonid" onclick="return false;">' +
|
|
// '<input type="button" class="dldButton" value="Reset" id="reset$buttonid" onclick="return false;"></td></tr><tr><td> </td></tr></table>'
|
|
// );
|
|
$data1
|
|
$data2
|
|
$data3
|
|
var bars = {
|
|
data: d1,
|
|
label: "$legend1",
|
|
bars: {
|
|
show: true,
|
|
barWidth: 0.8,
|
|
shadowSize: 5,
|
|
lineWidth: 1,
|
|
fillColor: {
|
|
colors: ["#76add2", "#fff"],
|
|
start: "top",
|
|
end: "bottom"
|
|
},
|
|
fillOpacity: 0.8
|
|
}
|
|
},
|
|
lines1 = {
|
|
data: d2,
|
|
label: "$legend2",
|
|
lines: {
|
|
show: true,
|
|
}
|
|
},
|
|
lines2 = {
|
|
data: d3,
|
|
label: "$legend3",
|
|
lines: {
|
|
show: true,
|
|
}
|
|
};
|
|
var options = {
|
|
mouse: {
|
|
track: true,
|
|
relative: true
|
|
},
|
|
yaxis: {
|
|
min: 0,
|
|
autoscaleMargin: 1,
|
|
mode: "normal",
|
|
title: "$ytitle",
|
|
},
|
|
xaxis: {
|
|
mode: "normal",
|
|
noTicks: $numticks,
|
|
tickFormatter: function(x) {
|
|
var x = parseInt(x);
|
|
$xlabel
|
|
},
|
|
title: "$xtitle",
|
|
},
|
|
title: "$title",
|
|
legend: {
|
|
position: "nw",
|
|
backgroundColor: "#D2E8FF",
|
|
backgroundOpacity: 0.4
|
|
},
|
|
HtmlText: false,
|
|
};
|
|
|
|
function drawGraph(opts) {
|
|
var o = Flotr._.extend(Flotr._.clone(options), opts );
|
|
return Flotr.draw(
|
|
container,
|
|
[
|
|
bars,
|
|
lines1,
|
|
lines2
|
|
],
|
|
o
|
|
);
|
|
}
|
|
|
|
var graph = drawGraph();
|
|
Flotr.EventAdapter.observe(container, "flotr:select", function(area) {
|
|
f = drawGraph({
|
|
xaxis: {
|
|
min: area.x1,
|
|
max: area.x2
|
|
},
|
|
yaxis: {
|
|
min: area.y1,
|
|
max: area.y2
|
|
}
|
|
});
|
|
});
|
|
Flotr.EventAdapter.observe(container, "flotr:click", function() {
|
|
drawGraph();
|
|
});
|
|
|
|
/*
|
|
document.getElementById('reset$buttonid').onclick = function() {
|
|
graph.download.restoreCanvas();
|
|
};
|
|
document.getElementById('download$buttonid').onclick = function(){
|
|
if (Flotr.isIE && Flotr.isIE < 9) {
|
|
alert(
|
|
"Your browser doesn't allow you to get a bitmap image from the plot, " +
|
|
"you can only get a VML image that you can use in Microsoft Office.<br />"
|
|
);
|
|
}
|
|
graph.download.saveImage('$self->{ImgFormat}');
|
|
};
|
|
document.getElementById('toimage$buttonid').onclick = function() {
|
|
if (Flotr.isIE && Flotr.isIE < 9) {
|
|
alert(
|
|
"Your browser doesn't allow you to get a bitmap image from the plot, " +
|
|
"you can only get a VML image that you can use in Microsoft Office.<br />"
|
|
);
|
|
}
|
|
graph.download.saveImage('$self->{ImgFormat}', null, null, true);
|
|
};
|
|
*/
|
|
})(document.getElementById("$divid"));
|
|
</script>
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
sub flotr2_piegraph
|
|
{
|
|
my ($self, $buttonid, $divid, $title, $xlabel, $ylabel, %data) = @_;
|
|
|
|
my @datadef = ();
|
|
my @contdef = ();
|
|
my $i = 1;
|
|
foreach my $k (sort keys %data) {
|
|
push(@datadef, "var d$i = [ [0,$data{$k}] ];\n");
|
|
push(@contdef, "{ data: d$i, label: \"$k\" },\n");
|
|
$i++;
|
|
}
|
|
return <<EOF;
|
|
<div id="$divid"></div>
|
|
<script type="text/javascript">
|
|
(function basic_pie(container) {
|
|
|
|
|
|
//document.writeln('<input type="button" class="dldButton" value="To Image" id="toimage$buttonid" onclick="return false;">'+
|
|
// '<input type="button" class="dldButton" value="Download" id="download$buttonid" onclick="return false;">' +
|
|
// '<input type="button" class="dldButton" value="Reset" id="reset$buttonid" onclick="return false;">'
|
|
// );
|
|
|
|
@datadef
|
|
var graph = Flotr.draw(container, [
|
|
@contdef
|
|
], {
|
|
title: "$title",
|
|
HtmlText: false,
|
|
grid: {
|
|
verticalLines: false,
|
|
horizontalLines: false,
|
|
outline: '',
|
|
},
|
|
xaxis: {
|
|
showLabels: false,
|
|
title: "$xlabel"
|
|
},
|
|
yaxis: {
|
|
showLabels: false,
|
|
title: "$ylabel"
|
|
},
|
|
pie: {
|
|
show: true,
|
|
explode: 6
|
|
},
|
|
mouse: {
|
|
track: true,
|
|
trackFormatter: function(obj){ return obj.y },
|
|
},
|
|
legend: {
|
|
position: "sw",
|
|
backgroundColor: "#D2E8FF",
|
|
backgroundOpacity: 0.4
|
|
}
|
|
});
|
|
/*
|
|
document.getElementById('reset$buttonid').onclick = function() {
|
|
graph.download.restoreCanvas();
|
|
};
|
|
document.getElementById('download$buttonid').onclick = function(){
|
|
if (Flotr.isIE && Flotr.isIE < 9) {
|
|
alert(
|
|
"Your browser doesn't allow you to get a bitmap image from the plot, " +
|
|
"you can only get a VML image that you can use in Microsoft Office.<br />"
|
|
);
|
|
}
|
|
graph.download.saveImage('$self->{ImgFormat}');
|
|
};
|
|
document.getElementById('toimage$buttonid').onclick = function() {
|
|
if (Flotr.isIE && Flotr.isIE < 9) {
|
|
alert(
|
|
"Your browser doesn't allow you to get a bitmap image from the plot, " +
|
|
"you can only get a VML image that you can use in Microsoft Office.<br />"
|
|
);
|
|
}
|
|
graph.download.saveImage('$self->{ImgFormat}', null, null, true);
|
|
};
|
|
i*/
|
|
|
|
})(document.getElementById("$divid"));
|
|
</script>
|
|
EOF
|
|
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|
|
|