Серверное администрирование / [Из песочницы] Централизованный syslog

в 14:49, , рубрики: Новости, метки:

Привет. В виду растущего количества серверов которые админю, и всяких других девайсов (ротуреры, 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» в браузере при большой БД.
Дальше, создаем веб-интерфейс, у меня получилось вот что:

#!/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/htmlnn"; 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 "ttFrom: ".$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 "ttTo: ".$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 "ttLevel: ".$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 

Всё, у нас должно получиться что-нибудь такое:

Piccy.info - Free Image Hosting

На данный момент, за период 7 месяцев, БД у меня занимает 6.5 GB.

Автор: romanmelko


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js