W zeszłym tygodniu zacząłem serię o narzędziach elastic. Na samym początku jedynie opisałem co będę chciał zrobić oraz jakie narzędzia będą nam potrzebne. W tym tygodniu przechodzę już do opisu pierwszego z nich – Logstash.
Z cyklu elastic, do tej pory ukazały się artykuły:
- elastic – wstęp
- elastic – logstash
- elastic – elasticserach
- elastic – kibana
Logstash to narzędzie umożliwiające łączenie wielu strumieni wejściowych danych, filtrowanie każdego strumienia wypluwanie z mapowanych lub nie danych do strumieni wyjściowych. Nie ma tam zbytnio magii. To co na dziś będziemy potrzebować to przygotowane środowisko z pierwszej części. Zaś tutaj skoncentrujemy się nad tym jak to wszystko skonfigurować.
To co musimy zrobić to stworzyć plik konfiguracyjny zawierający opis tego co chcemy by logstash robił. Plik zaś to DSL stworzony przez firmę elasitc z wykorzystaniem Treetop. W tym celu wystarczy stworzyć plik logstash.conf
i wgrać go do katalogu \bin
w folderze logstash. Następnie w dowolnym edytorze plików tekstowych uzpełńmy go o 3 sekcje: input
, filter
i output
.
input
– definiuje źródła – skąd brać co i jakoutput
– gdzie ma trafić przefiltrowana informacjafilter
– filtruje informacje i uporządkowuje je zgodnie z “naszym” schematem/założeniem
Pusty plik wygląda tak:
input { } filter { } output { }
Teraz zaczniemy go wypełniać. Na początku określmy źródło, w naszym przypadku są to pliki logu IIS.
input
Nasz input
może wyglądać tak:
input { file { type => "IISLog" path => ["C:/inetpub/logs/LogFiles/W3SVC*/*.log"] start_position => "beginning" } }
O co tu c’mon?
file
– określa, że źródłem będą plikitype
– nadaje nazwę, do której potem możemy się odwoływaćpath
– określa wzorzec który ma wychwycić wszystkie możliwe pliku log w IISstart_position
– to skąd ma logstash zacząć czytać pliki – u nas od początku, a to ze względu na to, że pewnie już mamy tam jakieś dane i chcielibyśmy je zaczytać. Jak by nas to nie interesowało i jedynie nowe informacje miałyby wpadać, wystarczy tą opcję pominąć lub ustawić jej wartość domyślnąend
.
Tych sekcji z plikami możemy mieć więcej – ważne by type było unikatowe.
output
Jeżeli byśmy nie chcieli nic filtrować to możemy stworzyć nasz output
:
output { stdout { codec => rubydebug } elasticsearch { hosts => ["localhost:9200"] } }
Gdzie:
stdout
– to po prostu standardowe wyjście, w tym momencie mówimy, wyświetl nam to co otrzymujesz jako output. W tym wypadku mówimy mu jeszcze by użył biblioteki do wyświetlania tych danych, w tym wypadku jest to awesome print.elasticsearch
– tak jak stdout, tylko, że przekaż to co dostajemy do serwerów dostępnych pod kreślonymi adresami.
To co jeszcze możemy zrobić to na przykład w zależności od type zdefiniowanego w input
, przekierować na odpowiednie źródła danych:
output { if [type] == "IISLog" { elasticsearch { hosts => ["localhost:9200"] } } }
Czyli do elasticsearch
zostanie przekazany log tylko i wyłącznie jak type
jest równo IISLog
. Czyli mając różne źródła, możemy w różne miejsca je wrzucać.
filter
To jest najbardziej zaawansowana sekcja i pewnie tutaj będziemy spędzać najwięcej czasu z plikami logów. Na przykład to co możemy zrobić z plikami logów IIS to nie przekazywać linijek rozpoczynających się od komentarza (#
):
filter { # ignore log comments if [message] =~ "^#" { drop {} } }
Z tego fragmentu możemy się zorientować, że:
message
– to jest nasza linijka tekstu.drop
– nie przekazuj dalej#
– komentarze- regex do porównywania
Tutaj w filter
możemy naprawdę dużo rzeczy zrobić, w naszym przypadku, chcielibyśmy w jakiś usystematyzowany sposób przechwycić linijkę treści i zamienić ją na dane które da się analizować. Na przykład, czy response code był 500 czy 200? Kiedy to się wydarzyło? Jakiej strony dotyczył request? To są wszystkie te same pola co wymieniłem w poprzedniej części, tylko że teraz chcemy by nasz logstash wiedział, że się znajdują i nadał im bardziej przyjazne nazwy. W tym celu korzystamy z rozszrzenia gork (jest dostępne domyślnie).
gork
Sposób wykorzystania gork jest prosty, wystarczy:
gork { match => ["message", "WYRAŻENIE_GORK"] }
Dosłownie to mówi, z dopasuj mi linijkę logu do wyrażenia, jak się nie uda, olej, nie przekazuj dalej wiadomości. Dlatego też jak wspominałem, bardzo ważne będzie to by mieć poprawne kolumny – takie jak ja, albo trzeba będzie samemu poprawić gork.
Wyrażenie porównywania jest dość proste, długie, ale proste, dla naszego przykładu wygląda ono tak:
%{TIMESTAMP_ISO8601:log_timestamp} %{IP:serverip} %{NOTSPACE:method} %{URIPATH:page} %{NOTSPACE:querystring} %{NUMBER:port} %{NOTSPACE:username} %{IP:clientip} %{NOTSPACE:useragent} %{NOTSPACE:referer} %{NUMBER:status} %{NUMBER:substatus} %{NUMBER:scstatus} %{NUMBER:timetaken}
Jak można zaobserwować występuje wzorzec: %{CAPITAL_LETTERS:small_letters}
gdzie CAPITAL_LETTERS
oznacza typ danego elementu (liczba, ciąg znaków bez spacji, IP itp.), zaś to co mamy po :
to opcjonalna nazwa pola. Nasz wzorzec możemy zweryfikować wchodząc na stronę GrokDebug i wklejając wyrażenie gork
do Pattern, zaś do Input wklejamy linijkę z naszego pliku logu IIS. Zaznaczmy jeszcze dwie opcje (by wizualnie było ładniej):
Named Captures Only
– czasami niektóre typy tworzą dodatkowe pola – na przykładTIMESTAMP
stworzy namYEAR
,MONTH
etc. Jeżeli tego nie chcemy to zaznaczamyNamed Captures Only
Singles
– powoduje, że każda nazwana wartość będzie traktowana jako wartość pojedyncza a nie tablica
Wynik naszego gork powinien wyglądać tak:
{ "log_timestamp": [ "2016-09-28 12:32:57" ], "serverip": [ "::1" ], "method": [ "GET" ], "page": [ "/" ], "querystring": [ "-" ], "port": [ "80" ], "username": [ "-" ], "clientip": [ "::1" ], "useragent": [ "Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/54.0.2840.34+Safari/537.36" ], "referer": [ "-" ], "status": [ "200" ], "substatus": [ "0" ], "scstatus": [ "0" ], "timetaken": [ "804" ] }
Jeżeli wynik się nie pokazuje, to znaczy, że mamy zły gork. Mamy dwa wyjścia z tej sytuacji: albo zmieniamy gork, albo ustawienia IIS tak by dopasowały do listy pól z poprzedniej części.
Mając gotowego gorka, moglibyśmy zakończyć zabawę z konfiguracją logstasha. Jednak, by mieć coś więcej z tych logów, dodajmy kolejne trzy filtry.
useragent
Jedno z pól gorkowych wykorzystuje następujący wzorzec: %{NOTSPACE:useragent}
i odpowiada on wychwyceniu ciągu znaków:
Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/54.0.2840.34+Safari/537.36
Ten ciąg może wytrawnemu oko coś powie. Mi osobiście za dużo nie mówi. Dlatego też możemy to trochę upiększyć i wyciągnąć z tego informację na temat przeglądarki. By to zrobić wykorzystujemy plugin useragent (domyślnie jest on dostępny):
useragent { source=> "useragent" prefix=> "browser_" }
Gdzie:
source
– to pole zawierające user agent.prefix
– to przedrostek jaki zostanie dodany do wyciągniętej przeglądarki. Dzięki czemu będziemy mieli wszystko ładnie zgrupowane pod jedną nazwą.
date
Plugin jest odpowiedzialny za parsowanie daty i ustawianie tego jako timestamp danego zdarzenia (danego logu). W przeciwnym wypadku, logstash ustawi to za nas – i nie koniecznie ten czas będzie tym który by nas interesował (na przykład czas przeczytania pliku a nie zapisu zdarzenia).
Plugin date jest banalnie prosty i jedynie co musimy podać to:
date { match => [ "log_timestamp", "YYYY-MM-dd HH:mm:ss" ] timezone => "Europe/Warsaw" }
To co robimy, to dopasowujemy wartośc pola log_timestamp
do wzorca po prawej stronie, i jak pasuje, to ustawiamy to jako timestamp w określonej strefie czasowej. Lista wszystkich możliwych wartości strefy czasowej znajduje się tutaj.
mutate
Skoro wykorzystujemy log_timestamp
jako timestamp
naszego zdarzenia, to pole to, nie jest nam już do niczego potrzebne i nie musimy go dublować w wyniku końcowym. Wystarczy więc, że dodamy:
mutate { remove_field => [ "log_timestamp"] }
By usunąć pole z wynikowego loga.
Podsumowanie logstash.config
Skończyliśmy konfigurację logstash. Teraz będziemy mogli zająć się uruchomieniem logstash, a następnie konfiguracją i uruchomienim elasticsearch.
Dla pewności nasz plik logstash.config
powinien wyglądać następująco:
input { file { type => "IISLog" path => ["C:/inetpub/logs/LogFiles/W3SVC*/*.log"] start_position => "beginning" } } filter { # ignore log comments if [message] =~ "^#" { drop {} } grok { match => ["message", "%{TIMESTAMP_ISO8601:log_timestamp} %{IP:serverip} %{NOTSPACE:method} %{URIPATH:page} %{NOTSPACE:querystring} %{NUMBER:port} %{NOTSPACE:username} %{IP:clientip} %{NOTSPACE:useragent} %{NOTSPACE:referer} %{NUMBER:status} %{NUMBER:substatus} %{NUMBER:scstatus} %{NUMBER:timetaken}"] } # https://www.elastic.co/guide/en/logstash/current/plugins-filters-date.html date { match => [ "log_timestamp", "YYYY-MM-dd HH:mm:ss" ] timezone => "Etc/Warsaw" } # https://www.elastic.co/guide/en/logstash/current/plugins-filters-useragent.html useragent { source => "useragent" prefix => "browser_" } mutate { remove_field => [ "log_timestamp"] } } # output logs to console and to elasticsearch output { stdout { codec => rubydebug } if [type] == "IISLog" { elasticsearch { hosts => ["localhost:9200"] } } }
Uruchomienie logstash
By logstash nam zbierało informację wrzucało dane do elasticsearch, musimy logstash odpalić:
logstash.bat -f logstash.conf
Jeżeli dostaniemy jakikolwiek błąd uruchomienia, to trzeba się upewnić czy jest to błąd Javy czy błąd ruby. Jeżeli Javy to pewnie mamy coś nie tak ze ścieżkami. Jeżeli ruby… to powiem tak, ja musiałem wejść do plików które on tam wyświetlał by dowiedzieć się dlaczego to nie działa i to dalej nie było takie proste.
Najlepiej więc by błędu nie było :) a jak jest. To cóż. Dajcie znać w komentarzu a pomogę.
Podsumowanie
Przeszliśmy przez prostą konfigurację logstasha i wytłumaczyliśmy sobie do czego służy input
, output
i filter
. Oraz z jakich filtrów możemy skorzystać by zrobić/napisać taki plik samemu. To wbrew pozorom nie jest takie trudne.
Za tydzień zajmiemy się elasticsearch.
Hej
Ciekawe narzędzie i fajny opis, natomiast z niecierpliwością czekam na elasticsearch. ;)
P.s
Coś się zduplikowało w podsumowaniu :)
a dzięki, z duplikowało się ;) już poprawione.
:) ql, dzięki, fajnie, że się podoba :)
Postanowiłem dzisiaj odświerzyć sobie wiadomości o influxDB i trafiłem na TICK stack https://www.influxdata.com/use-cases/introducing-the-tick-stack/ . Jeszcze się nie wgryzłem, ale wydaje się ciekawą alternatywą dla ELK
dzięki, popatrzę!
[…] elastic – logstash […]
Czytam, czytam, akurat się tym zajmuję, więc tekst trafiony w dychę… mała uwaga spell-ologiczna ;-) nie gork tylko grok?
Pozdrawiam,
Sławek
Grok, moja prawa ręka była szybsza od lewej ;)
Comments are closed.