Perl для системного администрирования

Анализ журналов Некоторые системные


Самый простой подход - обычное «считывание и подсчет». Мы читаем поток данных из журнала, ищем интересующие нас данные и увеличиваем значение счетчика, когда их находим. Вот простой пример, подсчитывающий, сколько раз перегружалась машина, в котором использован файл wtmpx из Solaris 2.6:*

# шаблон для wtmpx из Solaris 2.6, подробности смотрите в

# документации no pack()

Stemplate = "А32 А4 А32 1 s s2 x2 12 1 x20 S A257 x";

# определяем размер записи $recordsize = length(pack($template,())); и открываем файл

open(WTMP,"/var/adm/wtmpx") or die "Невозможно открыть wtmpx:$!\n";

# считываем по одной записи

while (read(WTMP,Srecord,Srecordsize)) {

($ut_user,$ut_id,$ut_line,$ut_pid,$ut_type,$ut_e_termination,

$ut_e_exit,$tv_sec,$tv_usec,$ut_session,

$ut_syslen,$ut_host)= unpack($template,Srecord);

if ($ut_line eq "system boot"){

print "rebooted ".scalar localtime($tv_sec)."\n"; $reboots++;



}

}

clOse(WTMP);

print "Общее число перезагрузок: $reboots\n";

Расширим этот подход и рассмотрим пример сбора статистики при помощи Event Log из Windows NT. Как говорилось раньше, механизм ведения журналов в NT хорошо разработан и довольно сложен. Эта сложность несколько пугает начинающих программистов на Perl. Для получения основной информации из журналов мы будем использовать некоторые подпрограммы из модулей для Win32.

Программы в NT и компоненты операционной системы записывают свои действия, регистрируя «события» в одном из журналов событий. Регистрация событий операционной системой сопровождается записью основной информации, например, времени наступления события, имени программы или функции операционной системы, зарегистрировавших событие, типа наступившего события (просто информативное или что-то более серьезное) и т. д.

В отличие от Unix, само описание события, т. е. сообщение, не хранится вместе с записью о событии. Вместо этого в журнал помещается идентификатор EventlD. Этот идентификатор содержит ссылку на определенное сообщение, хранящееся в библиотеке (.dll). Получить сообщение по идентификатору не просто. Этот процесс требует поиска нужной библиотеки в реестре и ее загрузки вручную. К счастью, этот процесс в текущей версии модуля Win32: : EventLog выполняется автоматически (ищите $Win32:: EventLog: :GetMessageText в первом примере с использованием Win32:: Eventlog).

В следующем примере мы сгенерируем простую статистику по числу записей в журнале System, содержащую сведения о том, откуда они поступили, и об уровне их важности. Мы напишем эту программу несколько иначе, чем первый пример в этой главе.

Первый наш шаг - загрузить модуль Win32: : EventLog, обеспечивающий связь между Perl и программами для работы с журналами событий в Win32. Затем мы инициализируем хэш-таблицу, которая будет использоваться для хранения результатов вызовов программ чтения журналов. Обычно Perl заботится об этом за нас, но иногда стоит добавить подобный код ради тех, кто будет впоследствии читать программу. Наконец, мы определяем небольшой список типов событий, который позже будет использоваться для печати статистики:

use Win32::EventLog;

my %event=('Length', NULL,

'RecordNumber',NULL, TimeGenerateo",

NULL. TimeWritten',NULL, 'EventID',NULL, 'EventType',NULL, 'Category',NULL, 'ClosingRecordNumber' .

NULL, 'Source',NULL, 'Computer',NULL, 'Strings',NULL, 'Data',NULL,):

tt

частичный список типов событий, то есть тип 1 -- "Error"

К 2 -- "Warning" и т. д.

@types = ("","Error","Warning","","Information");

Наш следующий шаг - открытие журнала событий System. Open() помещает дескриптор EventLog в $EventLog, который можно использовать для соединения с этим журналом:

Win32::EventLog::Open($EventLog,'System'.'') or die "Невозможно открыть журнал System:$~E\n";

Получив этот дескриптор, мы можем использовать его для подсчета событий в журнале и получения номера самой старой записи:

$EventLog->Win32::EventLog::GetNumber($numevents);

$EventLog->Win32::EventLog::Get01dest($oldestevent);

Эта информация указывается в первом операторе Read(), позиционирующем нас прямо перед первой записью. Это эквивалентно переходу в начало файла при помощи функции seek():

$EventLog->Win32::EventLog::Read((EVENTLOG_SEEK_READ |

EVENTLOG^FORWARDS_READ). $numevents + $oldestevent, $event);

Теперь в простом цикле прочитываем все записи. Флаг EVENTLOG_SEQ-UENTIAL_READ говорит: «Продолжайте читать с позиции после последней прочитанной записи». Флаг EVENTIOG_FORWARDS_READ перемещает нас вперед в хронологическом порядке. Третий аргумент Read() - смещение, в данном случае равное 0, потому что мы продолжаем с той же позиции, на которой остановились. Считывая каждую запись, мы записываем в хэш-таблицу счетчиков ее источник (Source) и тип события (EventType).

обходим в цикле все события, записывая количество различных

источников (Source) и типов событий

(EventTypes) for ($i=0;$i<$numevents;$i++)

{

$EventLog->Read((EVENTLOG_SEQUENTIAL READ

EVENTLOG_FORWARDS_READ) 0, Sevent):

$source{$event->{Source}}++:

$types{$event->{EventType}}++: }

it

выводим полученные результаты print "-->

Event Log Source Totals:\n"; for (sort keys %source) {

print "$_: $source{$_}\n": }

print "-"x30,"\n";

print "-->Event Log Type Totals:\n"; for (sort keys %types) {

print "$types[$_J: $types{$_}\n"; }

print "-"x30,"\n";

print "Total number of events: $numevents\n";

Мои результаты выглядят так: -->

Event Log Source Totals: Application Popup:

4 BROWSER: 228 DCOM: 12 Dhcp: 12 EventLog:

351 Mouclass: 6 NWCWorkstation: 2 Print: 27 Rdr: 12

RemoteAccess: 108 SNMP: 350 Serial: 175

Service Control Manager: 248 Sparrow: 5 Srv:

201 msbusmou: 162 msi8042: 3 msinport:

162 mssermou: 151 qic117: 2

--> Event Log Type Totals: Error: 493 Warning:

714 Information: 1014

Total number of events: 2220

Как я и обещал, вот пример кода, полагающегося на подобную программу для вывода содержимого журнала событий. В нем используется программа ElDump Джеспера Лоритсена (Jesper Lauritsen), которую можно загрузить с http://www.ibt.ku.dk/jesper/JespersNTtools.htm. ElDump похожа на DumpEl из NT Resource Kit:

Seldump = 'c:\bin\eldump'; # путь к ElDump

И выводим поля данных. оаз,:згяя их т/льдой ("). .

К текста сообщения (быстрее)

open(ELDUMP."Seldump $dumpflagsj") or die "Невозможно загустить $eidump:$!\n";

print STDERR "Считываем системный журнал.1:

while(<ELDUMP>){

($date, $time, $source, $type. Scategory Sevent, $i.ser, Scom.puter) =

split('-');

$$type{$source}+-t;

print STDERR "."; } print STDERR "done.\n";

close(ELDUMP);

# для каждого типа события выводим источники и количество

событий

foreach $type (qw(Error Warning Information

AuditSuccess AuditFailure)){

print "-" x 65,"\n";

print uc($type)."s by source:\n";

for (sort keys %$type){

print "$_ ($$type{$_})\n";

} } print "-" x 65,"\n";

Вот выдержка из получаемых данных:

ERRORS by source:

BROWSER (8)

Cdrom (2)

DOOM (15)

Dhcp (2524)

Disk (1)

EventLog (5)

RemoteAccess (30)

Serial (24)

Service Control Manage1" ("00)

Sparrow (2)

atapi (2)

i8042prt (4i

WARNINGS by soiree:

BROWSER (80)

Cdrom (22)

Dhcp (76)

Print (8)

Srv (82)



Содержание раздела