Пора поговорить про удобную работу с логами, тем более что в Windows есть масса неочевидных инструментов для этого. Например, Log Parser, который порой просто незаменим.


В статье не будет про серьезные вещи вроде Splunk и ELK (Elasticsearch + Logstash + Kibana). Сфокусируемся на простом и бесплатном.


Журналы и командная строка


До появления PowerShell можно было использовать такие утилиты cmd как find и findstr. Они вполне подходят для простой автоматизации. Например, когда мне понадобилось отлавливать ошибки в обмене 1С 7.7 я использовал в скриптах обмена простую команду:


findstr "Fail" *.log >> fail.txt

Она позволяла получить в файле fail.txt все ошибки обмена. Но если было нужно что-то большее, вроде получения информации о предшествующей ошибке, то приходилось создавать монструозные скрипты с циклами for или использовать сторонние утилиты. По счастью, с появлением PowerShell эти проблемы ушли в прошлое.


Основным инструментом для работы с текстовыми журналами является командлет Get-Content, предназначенный для отображения содержимого текстового файла. Например, для вывода журнала сервиса WSUS в консоль можно использовать команду:


Get-Content -Path 'C:\Program Files\Update Services\LogFiles\SoftwareDistribution.log' | Out-Host -Paging

Для вывода последних строк журнала существует параметр Tail, который в паре с параметром Wait позволит смотреть за журналом в режиме онлайн. Посмотрим, как идет обновление системы командой:


>Get-Content -Path "C:\Windows\WindowsUpdate.log" -Tail 5 -Wait


Смотрим за ходом обновления Windows.


Если же нам нужно отловить в журналах определенные события, то поможет командлет Select-String, который позволяет отобразить только строки, подходящие под маску поиска. Посмотрим на последние блокировки Windows Firewall:


Select-String -Path "C:\Windows\System32\LogFiles\Firewall\pfirewall.log" -Pattern 'Drop' | Select-Object -Last 20 | Format-Table Line


Смотрим, кто пытается пролезть на наш дедик.


При необходимости посмотреть в журнале строки перед и после нужной, можно использовать параметр Context. Например, для вывода трех строк после и трех строк перед ошибкой можно использовать команду:


Select-String 'C:\Windows\Cluster\Reports\Cluster.log' -Pattern ' err ' ?Context 3

Оба полезных командлета можно объединить. Например, для вывода строк с 45 по 75 из netlogon.log поможет команда:


Get-Content 'C:\Windows\debug\netlogon.log' | Select-Object -First 30 -Skip 45

Журналы системы ведутся в формате .evtx, и для работы с ними существуют отдельные командлеты. Для работы с классическими журналами («Приложение», «Система», и т.д.) используется Get-Eventlog. Этот командлет удобен, но не позволяет работать с остальными журналами приложений и служб. Для работы с любыми журналами, включая классические, существует более универсальный вариант ? Get-WinEvent. Остановимся на нем подробнее.


Для получения списка доступных системных журналов можно выполнить следующую команду:


Get-WinEvent -ListLog *


Вывод доступных журналов и информации о них.


Для просмотра какого-то конкретного журнала нужно лишь добавить его имя. Для примера получим последние 20 записей из журнала System командой:


Get-WinEvent -LogName 'System' -MaxEvents 20


Последние записи в журнале System.


Для получения определенных событий удобнее всего использовать хэш-таблицы. Подробнее о работе с хэш-таблицами в PowerShell можно прочитать в материале Technet about_Hash_Tables.


Для примера получим все события из журнала System с кодом события 1 и 6013.


Get-WinEvent -FilterHashTable @{LogName='System';ID='1','6013'}

В случае если надо получить события определенного типа ? предупреждения или ошибки, ? нужно использовать фильтр по важности (Level). Возможны следующие значения:


  • 0 ? всегда записывать;
  • 1 ? критический;
  • 2 ? ошибка;
  • 3 ? предупреждение;
  • 4 ? информация;
  • 5 ? подробный (Verbose).

Собрать хэш-таблицу с несколькими значениями важности одной командой так просто не получится. Если мы хотим получить ошибки и предупреждения из системного журнала, можно воспользоваться дополнительной фильтрацией при помощи Where-Object:


Get-WinEvent -FilterHashtable @{LogName='system'} | Where-Object -FilterScript {($_.Level -eq 2) -or ($_.Level -eq 3)}


Ошибки и предупреждения журнала System.


Аналогичным образом можно собирать таблицу, фильтруя непосредственно по тексту события и по времени.


Подробнее почитать про работу обоих командлетов для работы с системными журналами можно в документации PowerShell:



PowerShell ? механизм удобный и гибкий, но требует знания синтаксиса и для сложных условий и обработки большого количества файлов потребует написания полноценных скриптов. Но есть вариант обойтись всего-лишь SQL-запросами при помощи замечательного Log Parser.


Работаем с журналами посредством запросов SQL


Утилита Log Parser появилась на свет в начале «нулевых» и с тех пор успела обзавестись официальной графической оболочкой. Тем не менее актуальности своей она не потеряла и до сих пор остается для меня одним из самых любимых инструментов для анализа логов. Загрузить утилиту можно в Центре Загрузок Microsoft, графический интерфейс к ней ? в галерее Technet. О графическом интерфейсе чуть позже, начнем с самой утилиты.


О возможностях Log Parser уже рассказывалось в материале «LogParser — привычный взгляд на непривычные вещи», поэтому я начну с конкретных примеров.


Для начала разберемся с текстовыми файлами ? например, получим список подключений по RDP, заблокированных нашим фаерволом. Для получения такой информации вполне подойдет следующий SQL-запрос:


SELECT 
 extract_token(text, 0, ' ') as date, 
 extract_token(text, 1, ' ') as time,
 extract_token(text, 2, ' ') as action, 
 extract_token(text, 4, ' ') as src-ip,  
 extract_token(text, 7, ' ') as port 
FROM 'C:\Windows\System32\LogFiles\Firewall\pfirewall.log' 
WHERE action='DROP' AND port='3389'
ORDER BY date,time DESC

Посмотрим на результат:



Смотрим журнал Windows Firewall.


Разумеется, с полученной таблицей можно делать все что угодно ? сортировать, группировать. Насколько хватит фантазии и знания SQL.


Log Parser также прекрасно работает с множеством других источников. Например, посмотрим откуда пользователи подключались к нашему серверу по RDP.


Работать будем с журналом TerminalServices-LocalSessionManager\Operational.


Не со всеми журналами Log Parser работает просто так ? к некоторым он не может получить доступ. В нашем случае просто скопируем журнал из %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx в %temp%\test.evtx.

Данные будем получать таким запросом:


SELECT
 timegenerated as Date, 
 extract_token(strings, 0, '|') as user,
 extract_token(strings, 2, '|') as sourceip 
FROM '%temp%\test.evtx'
WHERE EventID = 21
ORDER BY Date DESC


Смотрим, кто и когда подключался к нашему серверу терминалов.


Особенно удобно использовать Log Parser для работы с большим количеством файлов журналов ? например, в IIS или Exchange. Благодаря возможностям SQL можно получать самую разную аналитическую информацию, вплоть до статистики версий IOS и Android, которые подключаются к вашему серверу.


В качестве примера посмотрим статистику количества писем по дням таким запросом:


SELECT
 TO_LOCALTIME(TO_TIMESTAMP(EXTRACT_PREFIX(TO_STRING([#Fields: date-time]),0,'T'), 'yyyy-MM-dd')) AS Date,
 COUNT(*) AS [Daily Email Traffic] 
FROM 'C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\MessageTracking\*.LOG'
WHERE (event-id='RECEIVE') GROUP BY Date ORDER BY Date ASC

Если в системе установлены Office Web Components, загрузить которые можно в Центре загрузки Microsoft, то на выходе можно получить красивую диаграмму.



Выполняем запрос и открываем получившуюся картинку…



Любуемся результатом.


Следует отметить, что после установки Log Parser в системе регистрируется COM-компонент MSUtil.LogQuery. Он позволяет делать запросы к движку утилиты не только через вызов LogParser.exe, но и при помощи любого другого привычного языка. В качестве примера приведу простой скрипт PowerShell, который выведет 20 наиболее объемных файлов на диске С.


$LogQuery = New-Object -ComObject "MSUtil.LogQuery"
$InputFormat = New-Object -ComObject "MSUtil.LogQuery.FileSystemInputFormat"
$InputFormat.Recurse = -1
$OutputFormat = New-Object -ComObject "MSUtil.LogQuery.CSVOutputFormat"
$SQLQuery = "SELECT Top 20 Path, Size INTO '%temp%\output.csv' FROM 'C:\*.*' ORDER BY Size DESC"
$LogQuery.ExecuteBatch($SQLQuery, $InputFormat, $OutputFormat)
$CSV = Import-Csv  $env:TEMP'\output.csv'
$CSV | fl 
Remove-Item $env:TEMP'\output.csv'
$LogQuery=$null
$InputFormat=$null
$OutputFormat=$null

Ознакомиться с документацией о работе компонента можно в материале Log Parser COM API Overview на портале SystemManager.ru.


Благодаря этой возможности для облегчения работы существует несколько утилит, представляющих из себя графическую оболочку для Log Parser. Платные рассматривать не буду, а вот бесплатную Log Parser Studio покажу.



Интерфейс Log Parser Studio.


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


Вторая особенность ? возможность экспорта запроса в скрипт PowerShell.


В качестве примера посмотрим, как будет работать выборка ящиков, отправляющих больше всего писем:



Выборка наиболее активных ящиков.


При этом можно выбрать куда больше типов журналов. Например, в «чистом» Log Parser существуют ограничения по типам входных данных, и отдельного типа для Exchange нет ? нужно самостоятельно вводить описания полей и пропуск заголовков. В Log Parser Studio нужные форматы уже готовы к использованию.


Помимо Log Parser, с логами можно работать и при помощи возможностей MS Excel, которые упоминались в материале «Excel вместо PowerShell». Но максимального удобства можно достичь, подготавливая первичный материал при помощи Log Parser с последующей обработкой его через Power Query в Excel.


Приходилось ли вам использовать какие-либо инструменты для перелопачивания логов? Поделитесь в комментариях.

Комментарии (6)


  1. ildarz
    20.03.2018 14:11

    Если мы хотим получить ошибки и предупреждения из системного журнала, можно воспользоваться дополнительной фильтрацией при помощи Where-Object
    Get-WinEvent… | Where-Object

    Это, в целом, отвратительный способ. Через powershell в принципе очень удобно разбирать логи, но есть одна большая проблема. ОЧЕНЬ. МЕДЛЕННО. Основной вопрос там обычно — не как в логе что-то найти, а как сделать так, чтобы запросы отрабатывали за вменяемое время. А комбинация Get-WinEvent для получения большого массива данных с последующей фильтрацией через where — самый медленный способ, который можно придумать. Get-Eventlog работает в разы быстрее, если надо получить большой кусок данных. Get-Winevent — наоборот, если нужно сделать максимально узкую выборку. В итоге либо используем Get-Eventlog… | where-object, либо Get-Winevent с максимально ограничивающим фильтром.


    1. avelor
      20.03.2018 17:26

      вот всем я люблю PowerShell, да вот только скоростью работы далеко не всегда отличается. местами из-за того что тянет за собой дотнетовские фреймворки…
      поэтому до сих пор люблю использовать cmd, vbs, всякий AutoIT с его dllcall (до сей и питона недорос) и в случае логов — Logparser. называйте меня ретроградом, но… :)


      1. rbobot
        20.03.2018 22:24

        Куда он «тянет» и какие такие «фреймворки»?
        PS удобен, быстр и прекрасен, нужно лишь соблюдать условия:
        — любую фильтрацию делать параметрами коммандлета, будь-то лдап запросы, будь-то эвентлоги. Коммандлеты по работе с большим количеством данных всегда имеют фильтры для выьорки на стороне сервера. Глупо ведь жаловаться на SELECT * при запросе к DB? С PS такая же ситуация;
        — по-минимуму использовать пайплайны, всегда использовать foreach() {} вместо Foreach-Object — каждый пайп — это кастинг любого типа в PSCustomObject, а зачем он для строк, например?
        — при необходимости формирования больших строковых массивов и любых других массовых операций со строками использовать StringBuilder — строки неизменяемы и любая операция со строкой пораждает в памяти новую строку;
        — использовать легкие потоки — runspace и параллелить свои задачи;
        — использовать workflow без ScriptBlock и параллелить свои задачи;
        — использовать нативные бинарники. PS — в первую очередь шелл и он прекрасно отправляет на вход любой утилите любые данные пайпом (пайп на бинарник не приводит к кастингу) и отлично забирает выхлоп. Для сложных и больших копирований/синхронизаций использую свой класс-обвязку для робокопи и быстрее врядли что-то может быть;
        — хотеть профилировать свои скрипты, само быстрее оно не станет;
        — не бояться писать свои коммандлеты на шарпе, если иначе ну совсем никак не справиться;
        — отбросить ксенофобию.


        1. avelor
          20.03.2018 23:07

          В своё время стояла задача — получать инфу о сеансах с терминальной фермы. Нашёл подходящие командлеты в галерее технета и получил время опроса около минуты. Полез внутрь — там использовались объекты .net (ну и не просто так PS требует .net framework).
          В итоге переписал на автоите с использованием wtsapi32.dll, получил скорость опроса менее секунды. Поскольку чукча ну совсем не программер, и вообще довольно ленив — отлаживать решение не стал, сделал как есть (подозреваю что на ps можно было сделать тоже самое). Благо получившийся бинарник не требовал дополнительной установки .net и славно работал на win7
          В последствии сталкивался с подобным поведением, когда да, быстрее и эффективнее написать обвязку для бинарника, если его не хватает, чем использовать PS. Ну и сложилось в итоге так (у меня), что совсем что-то простое — cmd, относительно сложные одно-двух строчники — PS, для всего остального vbs и autoit.
          В шарп потыкаться чуть глубже — в планах.
          За советы спасибо большое, записал, пригодится, кармы нет плюсануть.


  1. ymv57
    21.03.2018 09:12

    Для просмотра логов в режиме реального времени последнее время использую Configuration Manager Trace Log (cmtrace, ex trace32) из SCCM. Весьма удобно. Но, конечно, решение не для автоматизации и полноценного анализа


  1. Chumicheff
    21.03.2018 14:37

    Я ковыряю логи уже постфактум, когда идет расследование. Уже лет пять пользуюсь Event Log Explorer. Функционала за глаза.