PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : postfix log nach E-Mail-Adresse durchsuchen



chris_h
14.07.08, 08:27
Hallo,

ich stehe vor einem Problem, dessen Lösung (vermutlich komplex nach O2) ist und wo ich eure Ideen benötige.

Ziel: ich (nein, genauer der Chef) möchte zu einer E-Mail-Adresse wissen, ob Mails an eine Person durch gingen.

Wenn ich das mail.log von Postfix nach Mails durchsuche, ist der Suchparameter die E-Mail-Adresse. In einer Zeile finde ich dann z.B. eine Eintrag wie


Jul 14 08:12:31 mail postfix/smtp[30256]: AB58D1042D3: to=<email@adresse.de>, relay=xx.yy.de[x.x.x.x]:25, delay=0.77, delays=0.06/0.04/0.32/0.36, dsn=2.1.5, status=sent (250 2.1.5 OK mail delivered)

Nachdem in der Zeile der Absender nicht vermerkt ist, muss ich an Hand der ID den Absender suchen. Ist die mail.log groß, macht sich der Aufwand stark bemerkbar. Also muss ich die Datei 2 mal durchsuchen, einmal mit der E-Mailadresse und ein zweites mal nach der ID.

Wie kann ich die Suche vereinfachen? Gibt es hier einfache Lösungen?

Danke,
Chris

marce
14.07.08, 08:45
ohne es schon mal selbst so verwendet zu haben - evtl. kann Dir AWStats helfen.

zyrusthc
14.07.08, 09:17
Das könnte dir nützlich sein:

#!/bin/sh

if [[ $1 != '' ]]; then
log=$1
else
log=/var/log/mail.log
fi

echo '<html>'
echo '<body>'
echo '<H2>mail.log</H2>'
echo 'letze Aktualisierung:'$(date)
echo '<table border="1">'
echo '<tr>'
echo '<th>Zeitpunkt</th><th>Vorgangsnr.</th><th>IP</th><th>SMTPAuth</th><th>von</th><th>an</th><th>status</th><th>Kommentar</th>'
echo '</tr>'

for i in $(grep 'to=<' $log | awk '{print $6}'); do
all=$(grep $i $log)
vgn=$(echo $all | awk '{print $6}')
an=$(echo $all | grep 'to=' | sed -e 's#.*to=<##' -e 's#>.*##' | cut -b 0-50 )
zeit=$(echo $all | head -1 | awk '{print $1" "$2" "$3}')

kommentar=$(echo $all | grep 'status=' | sed -e 's#.*status=##' -e 's#(##' -e 's#).*##' )
status=$(echo $kommentar | sed -e 's#\ .*##')

von=$(echo $all | grep 'from=' | grep -v 'from=<>' | sed -e 's#.*from=<\([a-zA-Z0-9._-]*@[a-zA-Z0-9._-]*\)>.*#\1#' | cut -b 0-50)
ip=$(echo $all | grep client | sed -e 's#.*client=[A-Za-z0-9._-]*\[\([^]]*\)\].*#\1#' )
username=$(echo $all | grep sasl_username | sed -e 's#.*sasl_username=\([A-Za-z0-9._-]*\).*#\1#' )

if [ -z $username ]; then
username="<i>-RELAY-</i>"
fi

if [ -z $ip ]; then
ip="localhost"
fi


if [[ $status == sent ]]; then
col="#00ff00"
elif [[ $warning != "" ]]; then
col="#ff0000"
elif [[ $status == deferred ]]; then
col="#ff0000"
elif [[ $status == bounced ]]; then
col="#ff0000"
else
col="#ffff00"
fi

echo '<tr bgcolor="'"$col"'"><td><font size=2>'"$zeit"'</td><td><font size=2>'"$vgn"'</td><td><font size=2>'"$ip"'</td><td align=center><font size=2>'"$username"'</td><td><font size=2>'"$von"'</td><td><font size=2>'"$an"'</td><td><font size=2>'"$status"'</td><td><font size=2>'$kommentar'</td>'

done

echo '</table>'
echo '</body>'
echo '</html>'

Erzeugt eine HTML Datei, lässt sich gut lesen. Leider bei grossen Logs sehr lamm und rechenintensiv.


Greeez Oli

Roger Wilco
14.07.08, 11:11
Wie kann ich die Suche vereinfachen? Gibt es hier einfache Lösungen?
Einfach die Informationen (in deinem Fall das Tripel Message-ID, Absender, Empfänger; evtl. noch Datum dazu) iterativ aufbauen.

Das klappt mit einem relativ einfachen Perl/Python/Ruby/Whatever-Skript. Du gehst das Log Zeilenweise durch und schreibst den Absender und den Empfänger in eine kleine Hashtabelle mit der Message-ID als Schlüssel. Beim ersten Auftreten der Message-ID erhältst du den Absender und beim zweiten Auftreten der Message-ID den Empfänger. Damit musst du die Logdatei nur einmal durchlaufen und kannst das Ergebnis (die Hashtabelle) für spätere Suchen wiederverwenden.
Außerdem lässt sich die Liste inkrementell erweitern, d. h. du musst einmal angefasste Logzeilen nicht nochmal durchlaufen sondern kannst nach dem letzten Eintrag anfangen, der in deiner Liste steht.


Erzeugt eine HTML Datei, lässt sich gut lesen. Leider bei grossen Logs sehr lamm und rechenintensiv.
Implementiere das nochmal in Perl oder Python. Dann merkst du, welchen Overhead die Bash bzw. das Forken der ganzen grep-Prozesse erzeugt...

chris_h
14.07.08, 14:59
Hi all,

danke soweit für die Infos.
AWStats sieht wirklich vielversprechend aus. Muss mich mal damit beschäftigen.

@Oli:
das Problem ist, dass in dem Script 2 ineinander verschachtelte Schleifen sind. Steigt die Loggröße linear an, so haben wir quadratischen Aufwand (n hoch 2), grep "to=<" log und grep $i log. Du hast das auch richtig erkannt, dass bei großer Logdatei dies laaaange dauert, weil die Komplexität eben quadratisch zur Eingangsgröße ist.
(Mehr Info zu Komplexität von Algorithmen: z.B.
http://www.fh-muenchen.de/home/fb/fb06/professoren/hermann/web/informatik/vl_2007_10_08.pdf)

@Roger: ja so eine Lösung die immer mitloggt hätte schon etwas. Womöglich macht AVStats genau das. Damit habe ich nur eine geringe Eingagsgröße und die Laufzeit ist minimal.

Habe soetwas ähnliches einmal in Perl implementiert, aber bei 700 MB Logs geht auch einem Core 2 Duo, USCSI Rechner schnell die Puste aus. Eine "kontinuierliche" Lösung ist sicher vom Ansatz her besser.