![]() |
SELFHTML Server Konfiguration:
|
Dieses Kapitel bezieht sich hauptsächlich auf die Auswertung der Apache-Logfiles. Diese Logfiles sind im
combined-Format abgespeichert, da damit so ziemlich alle gängigen Programme klarkommen.
Die Logfiles sollten auf mehrere Gesichtspunkte hin ausgewertet werden: Traffic, Referrer und Suchbegriffe. Die Suchbegriffe sollten dabei einerseits von externen Suchmaschinen ausgewertet werden, sprich, wir wollten wissen, welche Suchbegriffe Hits auf uns liefern und andererseits wollten wir wissen, wie unsere SELFHTML-Suche benutzt wird.
Diese Auswertung war aus mehreren Gründen nötig: wir brauchten eine Auswertung des Gesamt-Traffics, eine Übersichtsseite über alle Subdomains und außerdem brauchten wir eine Auswertung der Suchbegriffe unserer SELFHTML-Suche.
So gibt es insgesamt zwei Scripts: eines um die Übersichtsseite und den Gesamt-Traffic auszuwerten und eines um die Suchbegriffe herauszufiltern. Diese Scripte will ich kurz vorstellen.
Als erstes das Script zur Generierung der Übersichtsseite. Dieses Script hat im Grunde zwei Aufgaben: es soll pro konfigurierter Domain den Webalizer starten (nach Wunsch) und eine Übersichtsseite generieren. Dazu besteht es im Grunde aus vier Teilen:
Die ersten beiden Schritte werden im Grunde direkt nacheinander ausgefuehrt, da sie eng zusammen gehöhren. für jeden weiteren Schritt gibt es eine Funktion, die nacheinander ausgeführt werden:
#!/usr/bin/perl -w
use strict;
use vars qw($VERSION $CONFIG @LIBPATH $TPL $HISTFILE $INDFILE);
BEGIN
{
$VERSION = '0.5';
$CONFIG = '/home/cron/logrolling/config/rolling.xml';
@LIBPATH = qw(/home/www/teamone.de/selfaktuell/cgi-bin/shared);
$TPL = '/home/cron/logrolling/config/templates/';
$HISTFILE= '/home/cron/logrolling/files/histfile.dat';
$INDFILE = '/home/www/teamone.de/webalizer/dokumente/index.html';
}
use lib @LIBPATH;
use XML::Simple; # wegen der Konfiguration
use Newsletter::Template; # Modul zur Generierung des HTMLs
# forward deklarationen
sub stat_it($);
sub get_traffics(%);
sub write_histfile(%);
sub generate_html(%%);
# variablen-initialisierung
my $config = XMLin($CONFIG);
my %href = ();
my %traffics = ();
$traffics{all} = 0;
# jede konfigurierte Domain durchiterieren
foreach my $domain (@{$config->{log}})
{
print `$config->{webalizer} -c $domain->{conf}`; # webalizer starten
my $traffic = stat_it $domain; # logfiles haendisch auswerten
$href{$domain->{domain}} = $domain->{href}; # noetig um das HTML zu generieren
$traffics{all} += $traffic; # Traffic zusammenzaehlen
$traffics{$domain->{domain}} += $traffic; # Traffic fuer die Domain dazuaddieren
}
%traffics = get_traffics(\%traffics); # Traffic aus der History-File dazu
generate_html(\%traffics,\%href); # HTML generieren
write_histfile(\%traffics); # Die History-File schreiben
###########################################
#
# Diese Funktion generiert das HTML.
#
sub generate_html(%%)
{
my $traffics = shift;
my $tpl_index = new Newsletter::Template($TPL.'index.html');
my $tpl_domains = new Newsletter::Template($TPL.'domains.html');
my $traffic_all = delete $traffics->{all};
$traffic_all = 0;
foreach(keys %{$traffics})
{
$traffic_all += $traffics->{$_};
}
$tpl_index->readin;
$tpl_domains->readin;
$tpl_index->assign(DOMAINS => '');
my $aktyear = '';
foreach my $domain (sort keys %{$traffics})
{
my ($dmn) = grep { $domain eq $_->{domain} } @{$config->{log}};
if($dmn->{index} && $dmn->{index} eq "no") {
next;
}
$tpl_domains->assign(DOMAIN => $domain);
$tpl_domains->assign(HREF => $href{$domain});
$tpl_domains->assign(TRAFFIC => sprintf("%.2f",$traffics->{$domain}/1024));
$tpl_domains->parse;
$tpl_index->append(DOMAINS => $tpl_domains->as_string);
}
$tpl_index->assign(DATE => scalar localtime);
$tpl_index->assign(TRAFFIC => sprintf("%.2f",$traffic_all/1024));
$tpl_index->assign(VERSION => $VERSION);
$tpl_index->parse;
open DAT, '>'.$INDFILE or die $INDFILE,': ',$!;
print DAT $tpl_index->as_string;
close DAT;
}
###########################################
#
# Diese Funktion schreibt die History-File zurueck
#
sub write_histfile(%)
{
my $traffic = shift;
local *DAT;
open DAT, '>'.$HISTFILE or die $HISTFILE,': ',$!;
foreach my $domain (keys %{$traffic})
{
print DAT $domain,':',$traffic->{$domain},"\n";
}
close DAT;
}
###########################################
#
# Diese Funktion liest die History-File wieder ein und addiert den Traffic dazu
#
sub get_traffics(%)
{
my %traffics = %{shift @_};
open DAT,'<'.$HISTFILE or die $HISTFILE,': ',$!;
my $aktd = '';
while(<DAT>)
{
next if /^\s*#/ || /^\s*$/; # comments and empty lines
next unless /^\s*([^:\s]+):\s*(\d+)/;
if($traffics{$1})
{
$traffics{$1} += $2;
}
else
{
$traffics{$1} = $2;
}
}
close DAT;
return %traffics;
}
###########################################
#
# Diese Funktion wertet die Logfile aus
#
sub stat_it($)
{
my $entry = shift;
my $traffic = 0;
local *DAT;
open DAT,$entry->{dir}.'/access-log' or die $entry->{dir},': ',$!;
while(<DAT>)
{
next unless m!^\S+ \S+ \S+ \[[^\]]+\] "GET /([^"]+) HTTP/1.[10]" (\d+) (\d+) "[^"]+" "[^"]+"$!;
# next if /\.(?:gif|jpg|png)/;
$traffic += $3 if $3;
}
close DAT;
return $traffic;
}
# eof
Die Konfigurations-Datei ist (der Einfachheit halber) auch eine XML-Datei:
config.xml:
<?xml version="1.0"?>
<!DOCTYPE logs SYSTEM "logs.dtd">
<logs>
<log
href="www"
domain="www.teamone.de"
dir="/home/www/teamone.de/www/logs/"
archive="/usr/backup/logs/www.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/www.conf"
/>
<log
href="alpentouren"
domain="alpentouren.teamone.de"
dir="/home/www/teamone.de/alpentouren/logs/"
archive="/usr/backup/logs/alpentouren.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/alpentouren.conf"
/>
<log
href="rhps"
domain="rhps.teamone.de"
dir="/home/www/teamone.de/rockyhorrorpictureshow/logs/"
archive="/usr/backup/logs/rhps.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/rhps.conf"
/>
<log
href="selfaktuell"
domain="aktuell.de.selfhtml.org"
dir="/home/www/teamone.de/selfaktuell/logs/"
archive="/usr/backup/logs/selfaktuell.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfaktuell.conf"
/>
<log
domain="developer.selfhtml.org"
index="no"
dir="/home/www/teamone.de/selfdeveloper/logs/"
archive="/usr/backup/logs/selfdeveloper.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfdeveloper.conf"
/>
<log
href="selffan"
domain="selffan.teamone.de"
dir="/home/www/teamone.de/selffan/logs/"
archive="/usr/backup/logs/selffan.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selffan.conf"
/>
<log href="selfforum"
domain="forum.de.selfhtml.org"
dir="/home/www/teamone.de/selfforum/logs/"
archive="/usr/backup/logs/selfforum.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfforum.conf"
/>
<log
href="selfhtml"
domain="de.selfhtml.org"
dir="/home/www/teamone.dehttp://de.selfhtml.org/logs/"
archive="/usr/backup/logs/selfhtml.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfhtml.conf"
/>
<log
href="selfsuche"
domain="suche.de.selfhtml.org"
dir="/home/www/teamone.de/selfsuche/logs/"
archive="/usr/backup/logs/selfsuche.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfsuche.conf"
/>
<log
href="webalizer"
domain="stats.teamone.de/webalizer"
dir="/home/www/teamone.de/webalizer/logs/"
archive="/usr/backup/logs/webalizer.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/webalizer.conf"
/>
<log
href="selfhtml.fr"
domain="selfhtml.selfhtml.com.fr"
dir="/home/www/selfhtml.com.frhttp://de.selfhtml.org/logs/"
archive="/usr/backup/logs/selfhtml.fr.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfhtml.fr.conf"
/>
<log
href="selfaktuell.fr"
domain="www.selfhtml.com.fr"
dir="/home/www/selfhtml.com.fr/www/logs/"
archive="/usr/backup/logs/selfaktuell.fr.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfaktuell.fr.conf"
/>
<log
href="selfforum.fr"
domain="selfforum.selfhtml.com.fr"
dir="/home/www/selfhtml.com.fr/selfforum/logs/"
archive="/usr/backup/logs/selfforum.fr.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/selfforum.fr.conf"
/>
<log
href="recherche"
domain="recherche.selfhtml.com.fr"
dir="/home/www/selfhtml.com.fr/recherche/logs/"
archive="/usr/backup/logs/recherche.tgz"
gzip="yes"
conf="/home/cron/logrolling/config/webalizer/recherche.conf"
/>
<apache>/usr/local/sbin/apachectl</apache>
<webalizer>/usr/local/bin/de-webalizer</webalizer>
</logs>
logs.dtd:
<!ELEMENT logs (log+,apache,webalizer)>
<!ELEMENT log EMPTY>
<!ATTLIST log
href CDATA #REQUIRED
domain CDATA #REQUIRED
dir CDATA #REQUIRED
archive CDATA #REQUIRED
gzip CDATA #REQUIRED
conf CDATA #REQUIRED
>
<!ELEMENT apache (#PCDATA)>
<!ELEMENT webalizer (#PCDATA)>
Einige Konfigurations-Elemente sind schon für das Logrolling-Script, das ich später nochmal erwähnen werde. Das Script sollte im Grunde bugfrei sein, ich bin allerdings natürlich froh über jede Verbesserung.
Als nächstes möchte ich das Script vorstellen, das die Suchbegriffe auswertet. Dieses Script hat lediglich zwei Aufgaben: die Logfiles durchgehen und entsprechend die Suchbegriffe in einer History-File festhalten und die 500 am meisten gesuchten Begriffe ins HTML schreiben. Entsprechend einfach ist es auch:
#!/usr/bin/perl -w
use strict;
use vars qw($VERSION @LIBPATH $CONF);
BEGIN
{
$VERSION = '0.1';
@LIBPATH = @LIBPATH = qw(/home/www/teamone.de/selfaktuell/cgi-bin/shared);
$CONF = '/home/cron/logrolling/config/search.xml';
}
use lib @LIBPATH;
use XML::Simple; # Fuer die Konfiguration
use Newsletter::Template; # Zum generieren des HTMLs
use HTML::Entities; # Um die Suchbegriffe korrekt zu escapen
# Forward-Deklarationen
sub generate_html($%%);
sub write_histfile($%);
sub decode($);
my $config = XMLin($CONF);
my %ausdruecke = ();
# alle Suchmaschinen durchgehen
foreach my $searcher (keys %{$config->{searcher}})
{
# History-File einlesen
open DAT, '<'.$config->{searcher}->{$searcher}->{histfile} or die $!;
while(<DAT>)
{
next unless /^\s*(.+):(\d+)\s*$/;
$ausdruecke{$1} = $2;
}
close DAT;
# Logfile auswerten
open DAT, '<'.$config->{searcher}->{$searcher}->{logfile} or die $!;
while(<DAT>)
{
next unless m!/cgi-bin/such.pl\?suchausdruck=([^&]+)!;
$ausdruecke{decode($1)}++;
}
close DAT;
generate_html($searcher,$config,\%ausdruecke); # HTML generieren
write_histfile($config->{searcher}->{$searcher}->{histfile},\%ausdruecke); # History-Files schreiben
%ausdruecke = (); # Ausdruecke resetten
}
###########################################
#
# Diese Funktion generiert das HTML.
#
sub generate_html($%%)
{
my $name = shift;
my $conf = shift;
my $ausdruecke = shift;
my $tpl_index = new Newsletter::Template($conf->{templatedir}.'/in_se.html');
my $tpl_row = new Newsletter::Template($conf->{templatedir}.'/row.html');
$tpl_index->readin;
$tpl_row->readin;
$tpl_index->assign(ROWS => '');
my $i = 0;
foreach my $ausdruck (sort { $ausdruecke->{$b} <=> $ausdruecke->{$a} } keys %{$ausdruecke})
{
last if $i == ($ARGV[0] ? $ARGV[0] : 500);
$i++;
$tpl_row->assign(TERM => encode_entities($ausdruck));
$tpl_row->assign(NUM => encode_entities($ausdruecke->{$ausdruck}));
$tpl_row->parse;
$tpl_index->append(ROWS => $tpl_row->as_string);
}
$tpl_index->assign(NAME => $name);
$tpl_index->assign(VERSION => $VERSION);
$tpl_index->assign(DATE => scalar localtime);
$tpl_index->parse;
open DAT,'>'.$conf->{searcher}->{$name}->{out} or die $!;
print DAT $tpl_index->as_string;
close DAT;
}
###########################################
#
# Diese Funktion schreibt die History-Files zurueck
#
sub write_histfile($%)
{
my $fname = shift;
my $ausdruecke = shift;
open DAT, '>'.$fname or die $!;
my $i = 0;
foreach my $ausdruck (sort { $ausdruecke->{$a} <=> $ausdruecke->{$b} } keys %{$ausdruecke})
{
print DAT $ausdruck,':',$ausdruecke->{$ausdruck},"\n";
}
close DAT;
}
###########################################
#
# Diese dekodiert die URL-encodierten Ausdruecke
#
sub decode($)
{
my $txt = shift;
$txt =~ tr/+/ /;
$txt =~ s/%([a-f0-9]{2})/chr(hex($1))/ieg;
return $txt;
}
# eof
Natürlich ist die Konfigurations-Datei auch eine XML-Datei:
search.xml:
<?xml version="1.0"?>
<!DOCTYPE searchers SYSTEM "searchers.dtd">
<searchers>
<templatedir>/home/cron/logrolling/config/templates/</templatedir>
<searcher name="selfsuche">
<logfile>/home/www/teamone.de/selfsuche/logs/access-log</logfile>
<histfile>/home/cron/logrolling/files/searchers_hist_selfsuche.dat</histfile>
<out>/home/www/teamone.de/webalizer/dokumente/selfsuche/ausdruecke.html</out>
</searcher>
<searcher name="recherche">
<logfile>/home/www/selfhtml.com.fr/recherche/logs/access-log</logfile>
<histfile>/home/cron/logrolling/files/searchers_hist_recherche.dat</histfile>
<out>/home/www/teamone.de/webalizer/dokumente/recherche/ausdruecke.html</out>
</searcher>
</searchers>
searchers.dtd:
<!ELEMENT searchers (templatedir,searcher+)>
<!ELEMENT templatedir (#PCDATA)>
<!ELEMENT searcher (logfile,histfile,out)>
<!ATTLIST searcher
name CDATA #REQUIRED
>
<!ELEMENT logfile (#PCDATA)>
<!ELEMENT histfile (#PCDATA)>
<!ELEMENT out (#PCDATA)>
Auch dieses Script sollte theoretisch bugfrei sein, doch auch hier freue ich mich über Verbesserungen.
Die Webalizer-Auswertung ist im Grunde eine leicht modifizierte Standard-Konfiguration. Wichtig ist nur zu beachten,
das die Option Incremental auf yes gesetzt wird, da sonst keine inkrementellen
Auswertungen gefahren werden können.