#!/usr/bin/perl
#
# Based on loganalyzer (Sergey N. Yatskevich <syatskevich@altlinux.ru>)
#
# Обработка лога Squid'а и запись результатов в таблицу proxy_log
#
# При обработке лога извлекается следующая информация :
#     1) время (с детализацией по дням) обращения
#     2) машина, с которой произошло обращение
#     3) хост, к которому произошло обращение (только доменное имя)
#     4) метод обращения (GET или POST)
#     5) Взят-ли объект из кэша или произошла его загрузка из сети
#     6) объем загруженной информации
#
# Интересует "объем информации"/"количество обращений" в разрезе
# 1, 2, 3, 4
#

use URI;
use DBI;

$dsn	= "dbi:InterBase:dbname=billing;host=firebird;ib_dialect=3";
$user   = "sysdba";
$passwd = "system";

sub parseLogLine
{
	($time, $duration, $client_address, $result_codes, $bytes, $request_method, $url, $rfc931, $hierarhy_code, $type) = split (/\s+/);

	# Пропускаем сообщения об ошибках выдаваемых Squid пользователям
	# и запросы к самому Squid'у
	if ($result_codes =~ /^NONE/ || $url =~ /^cache_object:/)
	{
		return ("SKIP_LINE", "", "", "", "", 0, 0);
	}

	# Squid не проставляет при типе соединения CONNECT (по крайней
	# мере для ICQ) используемый протокол, что вызывает ошибку
	# в пакете URI. Поэтому добавляем протокол сами.
	if ($request_method eq "CONNECT" && $url !~ /^https:/i)
	{
		$url = "https://$url";
	}

	$uri  = URI->new ($url);
	$uri  = $uri->canonical;
	$host = $uri->host;

	($day, $month, $year) = (localtime ($time)) [3, 4, 5];

	$month++;
	$year += 1900;

	$hit = ($result_codes =~ /_HIT/) ? 1 : 0;

	return ("INSERT", "$year/$month/$day", $client_address, lc $host, $request_method, $hit, $bytes);
}

while (<STDIN>)
{
	($state, $date, $client_address, $host, $request_method, $hit, $bytes) = parseLogLine ($_);

	if ($state eq "INSERT")
	{
		$total_count{"$date $client_address $host $request_method"} ++;
		$total_bytes{"$date $client_address $host $request_method"} += $bytes;

		if ($hit == 1)
		{
			$hit_count{"$date $client_address $host $request_method"} ++;
			$hit_bytes{"$date $client_address $host $request_method"} += $bytes;
		}
	}
}

$dbh = DBI->connect ($dsn, $user, $passwd, {AutoCommit => 0});

$sth = $dbh->prepare ("INSERT INTO proxy_log VALUES (?, ?, ?, ?, ?, ?, ?, ?)");

foreach $key (keys %total_count)
{
	if ($total_bytes{$key} > 0)
	{
		($date, $client_address, $host, $request_method) = split (/ /, $key);

		($hcount, $hbytes) = (exists $hit_count{$key}) ? ($hit_count{$key}, $hit_bytes{$key}) : (0, 0);
		
		$sth->execute ($date, $client_address, $host, $request_method, $total_count{$key}, $total_bytes{$key}, $hcount, $hbytes);
		if ($sth->state)
		{
			$dbh->rollback;
			$dbh->disconnect;

			die "Ошибка вставки записи, откат в начальное состояние\n";
		}
	}
}

$dbh->commit;
$dbh->disconnect;
