Песочница →
Централизованный syslog
Привет. В виду растущего количества серверов которые админю, и всяких других девайсов (ротуреры, IP телефоны и тд.) в своей IT инфраструктуре, появилась необходимость собирать логи в одном месте и иметь более-менее читабельный интерфейс для их вывода. Возможно я изобретаю велосипед, но в то время не удалось нагуглить по-быстрому чего нибудь подходящего, да и самому захотелось что нибудь сотворить.
Для коллекционирования логов была выбрана связка syslog-ng + PostgreSQL, для выборки с БД и визуализации был написан простой web-интерфейс на perl. Все это успешно виртуализируется в FreeBSD Jails, но это неважно, будет работать на любой UNIX-like ОС. Я не буду покрывать здесь установку и настройку всего софта (в сети есть много материала), а только необходимое.
Итак, чбобы заставить syslog-ng складывать удаленные логи в БД, в конфигурационный файл syslog-ng вписываем такой destination:
destination d_pgsql {
sql(type(pgsql) host("<ваш БД сервер>") port («5432») username("<ваш username>") password("<ваш password>") database("<има вашей БД>") table("<таблица>") columns(«host»,«facility»,«priority»,«level»,«tag»,«date»,«time»,«program»,«msg») values('$HOST', '$FACILITY', '$PRIORITY', '$LEVEL', '$TAG', '$YEAR-$MONTH-$DAY', '$HOUR:$MIN:$SEC', '$PROGRAM', '$MSG') indexes(«host»,«level»,«date»,«time»,«msg») );
};
Связываем source с новым destination, перезапускаем syslog-ng (вы же не забыли перед этим создать БД и таблицу?). Всё, логи должны теперь сыпаться в БД. Один важный момент — обязательно создайте индексы в БД, иначе будете лицезреть что-то вроде «Request time out» в браузере при большой БД.
Дальше, создаем веб-интерфейс, у меня получилось вот что:
А также скрипт get_hosts.pl, который должен запускаться по крону. Он создает список хостов, которые шлют логи. Его задачу можно встроить и в основной веб-интерфейс, но тогда он будет долго выполняться и может привести также к «Request time out».
Всё, у нас должно получиться что-нибудь такое:
На данный момент, за период 7 месяцев, БД у меня занимает 6.5 GB.
Для коллекционирования логов была выбрана связка syslog-ng + PostgreSQL, для выборки с БД и визуализации был написан простой web-интерфейс на perl. Все это успешно виртуализируется в FreeBSD Jails, но это неважно, будет работать на любой UNIX-like ОС. Я не буду покрывать здесь установку и настройку всего софта (в сети есть много материала), а только необходимое.
Итак, чбобы заставить syslog-ng складывать удаленные логи в БД, в конфигурационный файл syslog-ng вписываем такой destination:
destination d_pgsql {
sql(type(pgsql) host("<ваш БД сервер>") port («5432») username("<ваш username>") password("<ваш password>") database("<има вашей БД>") table("<таблица>") columns(«host»,«facility»,«priority»,«level»,«tag»,«date»,«time»,«program»,«msg») values('$HOST', '$FACILITY', '$PRIORITY', '$LEVEL', '$TAG', '$YEAR-$MONTH-$DAY', '$HOUR:$MIN:$SEC', '$PROGRAM', '$MSG') indexes(«host»,«level»,«date»,«time»,«msg») );
};
Связываем source с новым destination, перезапускаем syslog-ng (вы же не забыли перед этим создать БД и таблицу?). Всё, логи должны теперь сыпаться в БД. Один важный момент — обязательно создайте индексы в БД, иначе будете лицезреть что-то вроде «Request time out» в браузере при большой БД.
Дальше, создаем веб-интерфейс, у меня получилось вот что:
#!/usr/local/bin/perl
#
# syslog-stat (c) Roman Melko <romanmelko@gmail.com>
# Description: Simple web-interface for querying syslog-ng records stored in PostgreSQL database
# Requirements:
get_hosts.pl script should be runned periodically to have an up-to-date hosts file
# Version: 2011122001
# License: BSD
#
print "Content-type:text/html\n\n";
print <<EndOfHTML;
<html><head><title>Syslog</title></head>
<body bgcolor='lightgrey'>
EndOfHTML
use strict;
use DBI;
use CGI;
use Socket;
use feature qw/switch/;
my $cgi = CGI->new();
my $host;
my $level;
my $fromdate;
my $todate;
my $server = "<ваш БД сервер>"; # DB server
my $user = "<ваш username>"; # DB user
my $password = "<ваш password>"; # DB password
my $dbname = "<има вашей БД>"; # DB name
my $table = "<таблица>"; # DB table
my $hosts_file = "/usr/local/www/syslog-stat/cache/hosts.db";
my $count = 100; # max count of servers
my $i = 0;
my $sth;
my $dbh;
my $color = "black";
my @hosts = ();
my @levels = (
"",
"info",
"warning",
"error",
);
my @years = ("2011" .. "2030");
my @months = ("01" .. "12");
my @days = ("01" .. "31");
my ($fromsec,$frommin,$fromhour,$frommday,$frommon,$fromyear,$fromwday,$fromyday,$fromisdst) = localtime(time);
my ($tosec,$tomin,$tohour,$tomday,$tomon,$toyear,$towday,$toyday,$toisdst) = localtime(time);
my $where;
$dbh = DBI->connect("DBI:Pg:dbname=$dbname;host=$server", "$user", "$password", {'RaiseError' => 1});
# Getting list of hosts, sending logs to syslog server
sub get_hosts_ext {
open(HOSTS,"<",$hosts_file) or die "open: $!\n";
my $count = 0;
while (<HOSTS>)
{
$hosts[$count] = substr($_,0,length($_)-1);
$count++;
}
close HOSTS or die "close: $!";
}
sub query() {
$host = $cgi->param('host');
$level = $cgi->param('level');
$fromdate = join("",$cgi->param('fromyear'),$cgi->param('frommonth'),$cgi->param('fromday'));
$todate = join("",$cgi->param('toyear'),$cgi->param('tomonth'),$cgi->param('today'));
if($fromdate > $todate) {
$fromdate = $todate = join("-",$cgi->param('toyear'),$cgi->param('tomonth'),$cgi->param('today'));
}
else {
$fromdate = join("-",$cgi->param('fromyear'),$cgi->param('frommonth'),$cgi->param('fromday'));
$todate = join("-",$cgi->param('toyear'),$cgi->param('tomonth'),$cgi->param('today'));
}
$where = "";
if ($level) {
given($level) {
when("error") {
$where = "and level='error' or host='$host' and level='err'";
}
when("info") {
$where = "and level='info' or host='$host' and level='notice'";
}
default {
$where = "and level='$level'";
}
}
}
$sth = $dbh->prepare("SELECT * FROM logs where host='$host' and date>='$fromdate' and date<='$todate' $where order by date desc, time desc");
$sth->execute();
print('
<table border=1 cellpadding="1" cellspacing="0">
<tr>
<th>DATE</th>
<th>LEVEL</th>
<th>PROGRAM</th>
<th>MESSAGE</th>
</tr>
');
while(my $ref = $sth->fetchrow_hashref()) {
$color = "black";
given($ref->{'level'}) {
when("warning") {
$color = "yellow";
}
when(($ref->{'level'} eq "error") || ($ref->{'level'} eq "err")) {
$color = "red";
}
default {
$color = "black";
}
}
print "<p><tr>";
print("
<td> $ref->{'date'} $ref->{'time'} </td>
<td> <font color=$color>$ref->{'level'}</font> </td>
<td> $ref->{'program'} </td>
<td> $ref->{'msg'} </td>
");
print "</tr></p>";
}
print "</table>";
}
$fromyear += 1900;
$frommon++;
$toyear += 1900;
$tomon++;
get_hosts_ext;
if(length($frommon) == 1) {
$frommon = join("","0",$frommon);
}
if(length($frommday) == 1) {
$frommday = join("","0",$frommday);
}
if(length($tomon) == 1) {
$tomon = join("","0",$tomon);
}
if(length($tomday) == 1) {
$tomday = join("","0",$tomday);
}
print $cgi->start_form();
print "Host: ".$cgi->popup_menu(-name=>"host", -values=>[@hosts]);
print "\t\tFrom: ".$cgi->popup_menu(-name=>"fromyear", -values=>[@years], -default=>$fromyear);
print $cgi->popup_menu(-name=>"frommonth", -values=>[@months], -default=>$frommon);
print $cgi->popup_menu(-name=>"fromday", -values=>[@days], -default=>$frommday);
print "\t\tTo: ".$cgi->popup_menu(-name=>"toyear", -values=>[@years], -default=>$toyear);
print $cgi->popup_menu(-name=>"tomonth", -values=>[@months], -default=>$tomon);
print $cgi->popup_menu(-name=>"today", -values=>[@days], -default=>$tomday);
print "\t\tLevel: ".$cgi->popup_menu(-name=>"level", -values=>[@levels]);
print ($cgi->submit (-name=>'Query',-value=>'Query'));
print("<div align='right'>syslog-stat (c) Roman Melko</div>");
print $cgi->end_form();
if(($cgi->param('host')) && ($cgi->param('fromyear') && ($cgi->param('frommonth')) && ($cgi->param('fromday')))) {
query();
$cgi->param('host') = ();
$cgi->param('fromyear') = ();
$cgi->param('frommonth') = ();
$cgi->param('fromday') = ();
$cgi->param('toyear') = ();
$cgi->param('tomonth') = ();
$cgi->param('today') = ();
$cgi->param('level') = ();
}
$dbh->disconnect();
print "</body></html>";
А также скрипт get_hosts.pl, который должен запускаться по крону. Он создает список хостов, которые шлют логи. Его задачу можно встроить и в основной веб-интерфейс, но тогда он будет долго выполняться и может привести также к «Request time out».
#!/usr/local/bin/perl
#
# syslog-stat (c) Roman Melko <romanmelko@gmail.com>
# Description: Get hosts from syslog-ng records stored in PostgreSQL database
# Requirements: Should run periodically
# Version: 2011121901
# License: BSD
#
use strict;
use DBI;
my $host;
my $level;
my $fromdate;
my $todate;
my $server = "<ваш БД сервер>"; # DB server
my $user = "<ваш username>"; # DB user
my $password = "<ваш password>"; # DB password
my $dbname = "<има вашей БД>"; # DB name
my $table = "<таблица>"; # DB table
my $hosts_file = "/usr/local/www/syslog-stat/cache/hosts.db";
my $sth;
my $dbh;
my @hosts = ();
sub get_hosts {
$dbh = DBI->connect("DBI:Pg:dbname=$dbname;host=$server", "$user", "$password", {'RaiseError' => 1});
$sth = $dbh->prepare("SELECT DISTINCT host FROM logs order by host asc");
$sth->execute();
my $count = 0;
while(my $ref = $sth->fetchrow_hashref()) {
$hosts[$count] = $ref->{'host'};
$count++;
}
$dbh->disconnect();
}
sub dump_hosts {
open(HOSTS,">",$hosts_file) or die "Dump failed: $!\n";
my $count = @hosts;
my $i = 0;
while(<@hosts>) {
print HOSTS $_."\n";
}
close(HOSTS);
}
get_hosts;
dump_hosts;
exit 0
Всё, у нас должно получиться что-нибудь такое:
На данный момент, за период 7 месяцев, БД у меня занимает 6.5 GB.
28.02.2012 19:49+0400