Archive for August, 2009

Proste statystyki wydruków z bazą sqlite

Friday, August 21st, 2009

Potrzebowałem na szybko statystyk wydruków. Do tego skonstruowałem taki oto twór:

perl -ne 'print "$1 $2\n" if /(\d+\/[a-zA-z]+\/20\d{2}).*for job (\d+)\./' /var/log/cups/error_log|sort |uniq | awk '{print "insert into printouts values(\""$1"\","$2");"}'| sqlite3 printouts.db

Ten z pozoru niezgrabny jednolinijkowiec parsuje plik logów cupsa wyciągając z niego numer zadania i datę, a następnie wkłada to do prostej bazy sqlite`a.

Dzięki temu mogłem szybko wydobyć ilość wydruków na dzień. Przy niewielkiej modyfikacji można dołączyć inne pola i już grupować sobie w sqlu co tylko dusza zapragnie. Nie jest to może wyszukany przypadek – raczej ciekawostka. To samo można uzyskać również za pomocą uniq -c, ale perspektywy tego rozwiązania są o wiele większe.

Na koniec tylko polecenie tworzące trywialną strukturę bazy printouts.db:

sqlite3 printouts.db 'create table printouts(date text,job int)'

Ujarzmione logi apache

Wednesday, August 12th, 2009

Na co dzień pracuję z aplikacją, która wyrzuca bardzo dużo “śmieci” do logów. Tym samym zapycha ona error log, który z nazwy powinien zawierać błędy, a nie informacje o niezainicjalizowanej zmiennej X. Postanowiłem ukrócić ten proceder i filtrować wychodzące z procesów apache`a komunikaty.

Wykorzystałem do tego perla, gdyż dość sprawnie radzi on sobie z obróbką tekstu i wyrażeniami regularnymi. Poniżej zawartość skryptu apache-logfilter.pl

#!/usr/bin/perl -w

use strict;

$|=1;   # use unbuffered output

my $logfile;

# check if logfile was passed as 1st argument
if ($ARGV[0])   {
$logfile=$ARGV[0];
}

# assign /dev/null as logfile unless it is already defined
$logfile="/dev/null" unless ($logfile);

# set umask
umask 0022;
my $loghandler=open(LOGFILE,'>>',$logfile);

# autoflush file handler
select((select(LOGFILE), $| = 1)[0]);

# inifinite loop over input from STDIN
while (<STDIN>) {

# omit unwanted lines matching the following patterns
next if (/^Use of uninitialized value/);
next if (/^"my" variable/);
next if (/^Subroutine \w+ redefined/);
next if (/^Variable ".*?" will not stay shared/);
next if (/^Odd number of elements in hash/);
next if (/^Argument ".*?" isn't numeric/);
next if (/^Useless use of .*? in void context/);
next if (/^substr outside of string at/);
next if (/^Unquoted string ".*?" may clash/);
next if (/^Scalar value @.*? better written as/);
next if (/^Parentheses missing around/);

# print to our logfile
print LOGFILE "$_";
}

Posiadając już taki skrypt należy przekazać w konfiguracji apache`a jego wywołanie. Odbywa się to za pomocą pipe`a przez który przekazywane są komunikaty do skrypty. W tym przypadku jako parametr podana jest ścieżka do właściwego pliku logu, do którego dopisywane są wiadomości (nie należy zapomnieć o właściwiej konfiguracji logrotate!).
Przykładowa część konfiguracji:


ErrorLog "|/usr/local/bin/apache-logfilter.pl /var/log/apache/error_log"

Ciekawą rzeczą jaką napotkałem tutaj był problem buforowanego wyjścia. Otóż do logu nie docierały żadne nowe wiadomości. Wielokrotnie sprawdzałem poprawność skryptu i za każdym razem działał on poprawnie zapuszczony z palca. Po krótkich poszukiwaniach znalazłem zmienną $|, której to niezerowe ustawienie powinno mój problem rozwiązać. A jednak nie rozwiązało. Dopiero wymuszenie tego na uchwycie otwartego pliku dało pożądane rezultaty. Odpowiada za to następujące wywołanie:


select((select(LOGFILE), $| = 1)[0]);

Teraz wszystko śmiga jak należy – czytelność logów zwiększyła się znacząco, a ich rozmiar zmniejszył się kilkukrotnie.

Na tropach procesu

Tuesday, August 4th, 2009

Obecnie nie potrafię sobie wyobrazić porządnego debugowania/troubleshootingu bez strace`a. Dzięki niemu jestem w stanie śledzić wywołania systemowe i wychwytywać wszelkie błędy w oprogramowaniu w sytuacjach gdy logi już nie wystarczają. Dodatkowo zawsze jest to ciekawie obejrzeć działanie “od kuchni”.

Jak używać strace`a? Najłatwiej poprzedzić wywołanie programu poleceniem strace. Częściej jednak będziemy chcieli sprawdzić co robi dany proces uruchomiony już w tle. Wówczas należy wywołać:

strace -p PID_PROCESU

Jeśli program tworzy forki to również warto dodać parametr -f śledzący jego procesy potomne.