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

#!/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


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

Piccy.info - Free Image Hosting

На данный момент, за период 7 месяцев, БД у меня занимает 6.5 GB.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 51

    +4
    Очень прошу вас — переделайте веб-интерфейс на Twitter bootstrap.

    Это займет всего полчаса, но пользоваться будет гораздо приятнее.
      +1
      Делалось для себя, и главное было — функционал. Но тем не менее спасибо, попробую разобраться потом.
        0
        Да я понимаю. Просто вы же на Хабр картинку постите, всему земному шару на обозрение.
        Надо соответствовать.
      0
      посмотрите в сторону шаблонов
      это уменьшит ваш скрипт в размерах и увеличит его читаемость
        0
        Спасибо, в будущем попробую
        0
        Есть еще log.io
          0
          Стоит посмотреть на splunk
          Интересная штука, есть фришная лицензия.
            0
            Мне понравилось что можно анализировать и соотносить события между собой. И поиск неплохой.
            0
            Не пробовали ли Вы в качестве базы данных использовать что-нибудь типа mongodb (в ветке syslog-ng 3.3)?

            По идее, первый и основной плюс — отсутствие необходимости в logrotate при использовании capped collections.

            Плюс ещё и шардинг из коробки — удобно при необходимости держать и анализировать большое количество логов. У меня это пока что это только на стадии планирования, так что по поводу быстродействия ничего не скажу.
              0
              Пока что не приходилось работать с noSQL БД, но интерес вызывает.
                +1
                graylog2.org/ здесь mongodb пробовали, решили в итоге отказаться. Медленно и нестабильно при неприличном потреблении ресурсов памяти.
                  0
                  Мммм… Здесь, скорее, речь идёт о стабильности модуля afmongodb для syslog-ng. Сама-то монга работает достаточно шустро и стабильна.
                    0
                    500млн capped collection и все, не едет больше ))
                      0
                      Интересно. Сегодня тестировал — получил в среднем 15-20 тысяч записей в секунду (пишем в facility local5 фразу «This is test»), но при этом получил проблему в монге — writelock наступает уже на 50-70 тысячах, и некоторое количество записей дропается.
                      По 500 млн — проверю обязательно, спасибо. У меня всего 2G capped, должно хватить.
                  0
                  Как бы базе плохо не стало при большом количестве машин в сети. Есть идея возможности складывания в Redis с последующим выгружением в любую бд кроном, например.
                    0
                    А ты знаешь, сколько геморроя содержит в себе перекидывание из syslog-ng в redis? По-моему, вполне глупое занятие, пока что mongo справляется. Из тех логов, которые нам нужны, у них всего 200-400 записей в секунду в штатном режиме. Это немного.
                  +1
                  Везет вам, 6.5 Гб за 7 месяцев. У меня зачастую столько в сутки бывает на центральном сислоге.
                    0
                    Логи сыпятся не все, а только нужных сервисов
                      0
                      Эт точно. Только что архивил и отсылал на анализ лог 19Гб от одного сервиса за один день, а центральный сислог впахивает как проклятый так как на него не один сервис такой пишет. Уже нужно рассматривать другие альтернативы или распределенные сервера.
                        0
                        Да, мучают аналогичные проблемы.
                        А в случае каких-то нештатных ситуаций бывает вообще до 90 Гб логов за сутки от одного сервера.
                        0
                        Везет вам, 6.5 Гб за 1 сутки. У меня в сутки собирается около 200+ гигов логов.
                          0
                          от чего такое, и зачем такая детализация, интересно…
                        +2
                        Второй пост за квартал на тему 'как я складировал логи в mysql'? Тогда вам сюда -> graylog2 -> graylog2.org/
                          0
                          Грейлог загибается под нагрузками только в путь.
                            0
                            Возможно. У меня порядка 20 сообщений в секунду, сервер 2xXeon E5440 16GB ram, LA на уровне 0.03. Есть еще Splunk на примерно такой же нагрузке и таком же сервере — LA анаологично. Поделитесь вашим решением и нагрузками.
                              0
                              Нет не загибается. Наш грейлог тащит 100-120 rps по пику в минуту, а всего в нем сейчас лежит 500000000, половина из которых в GELF.
                              Проблемы есть, конечно, но больше в логике. Баги пофиксят, а связка прет как танк. Ни одно другое решение вам такой производительности не даст при схожем функционале.
                                0
                                недописал)) в минуту в среднем 6000.
                                  0
                                  Это мало. У меня 6000 прилетает за 4 секунды:

                                  # time /usr/sbin/tcpdump -n port syslog -c 6000 > /dev/null
                                  tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
                                  listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
                                  6000 packets captured
                                  6000 packets received by filter
                                  0 packets dropped by kernel

                                  real 0m4.047s
                                  user 0m0.052s
                                  sys 0m0.023s
                                    0
                                    Packets captured? syn ack тоже пакеты.
                                      0
                                      Откуда сины, если syslog работает по udp?
                                        0
                                        по TCP тоже работаeт, вы же не уточнили )))
                            –1
                            Спасибо, полезно, опробую.
                              0
                              А нет ли чего еще попроще, чтобы логи разных хостов тупо в разные подпапки писать, но на центральном сервере?
                                0
                                Сначала у меня так и было, писалось в разные файлы, но ето неудобно при чтении и поиске
                                  0
                                  Ну мне пока grep + less хватает. А вот деление по папкам было бы неплохо…
                                  0
                                  Так различные destination у syslog-ng — самое простое. Описанное Вами делается в 1.5 строки.
                                    0
                                    Хм, надо еще разок попробовать. А то в один файл писать получалось, а вот в разные для каждого хоста — нет.
                                      0
                                      Ставим разные sources (на каждый хост свой), и далее различные destination для каждого source. Логично, ИМХО.
                                        0
                                        Это полторы строки на каждый хост? (В моем случае хостов несколько сотен).
                                          0
                                          Несколько сотен — уже сложнее. Не приходилось применять. Так да, на каждый хост.
                                            0
                                            Хмм, а не накладно ли будет лазить по папкам и логи грепать?
                                          0
                                          /etc/rsyslog.d/50-default.conf:

                                          $template DynFile,"/var/log/%HOSTNAME%/%programname%.log"
                                          $template MyTemplate,"%timegenerated% %HOSTNAME% %syslogtag% %msg%\n"
                                          *.* ?DynFile;MyTemplate
                                          &~


                                          (только вот не смог заставить СОЗДАВАТЬ папки при появлении новых хостов, пришлось забить имена всех существующих)
                                            0
                                            У нас вот так создает. Плюс надо права на каталог проверить:
                                            $Umask 0022
                                            $DirOwner root
                                            $DirGroup syslog
                                            $dircreatemode 0755
                                            $FileOwner root
                                            $FileGroup syslog
                                            $filecreatemode 0644
                                            $template RemLogs,"/var/log/remote/%$YEAR%/%$MONTH%/%$DAY%/%FROMHOST%.log"
                                            *.* ?RemLogs
                                      0
                                      Кто-нибудь вообще думал о защите от подделки логов?!

                                      Концепт:
                                      злоумышленник (зная о централизованном сборе) заваливает сервер UDP-сообщениями об попытках SSH-логина с разных хостов, тем самым маскируя свои попытки-> Админ бросает тщетные попытки найти в мусоре истину.
                                        0
                                        Как видно из етой таблицы, фришная версия syslog-ng поддержывает TCP протокол и криптование, правда как оно там реализовано не углублялся
                                          0
                                          если злоумышленник имеет доступ к внутренней сети, или если файервол не режет udp с внешних сетей
                                            0
                                            А зачем разрешать слать на сислог сообщения всем кому ни попадя? Только доверенной сети, где и живет инфраструктура.
                                              0
                                              инсайдеры, внутренние злоумышленники.
                                                0
                                                А внутри своей сети с помощью функционала reverse path check на сетевом оборудовании можно почти полностью исключить возможность спуфинга IP-адресов.
                                            0
                                            Посмотрите в сторону бесплатных систем мониторинга. Тот же, zenoss, например. Кроме централизованного syslog умеет ещё много чего полезного. Мониторинг ещё никому не мешал, тем более если оборудования много.
                                              0
                                              Мониторинг успешно рабоет на базе nagios, zenoss не пробовал.

                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                            Самое читаемое