Logstash grok patterns: logų parserinimas

Kas tie grok patterns ir kodėl jie tokie svarbūs

Kai pradedi dirbti su Logstash, vienas pirmųjų iššūkių – kaip iš chaotiškų logų ištraukti struktūrizuotą informaciją. Čia ir ateina į pagalbą grok patterns. Tai tarsi šveicariškas peilis logų apdorojimo pasaulyje – galingas įrankis, kuris leidžia transformuoti nestruktūrizuotą tekstą į suprantamus, indeksuojamus laukus.

Grok veikia kaip regex (reguliariųjų išraiškų) abstrakcija. Jei kada bandėte rašyti sudėtingas regex išraiškas, žinote, kad tai gali virsti košmaru. Grok patterns suteikia jums iš anksto paruoštų šablonų biblioteką, kurią galite kombinuoti ir kurti savo parserius be galvos skausmo. Pavyzdžiui, vietoj sudėtingos regex IP adreso atpažinimui, tiesiog naudojate %{IP}.

Logstash naudoja grok filter plugin’ą, kuris yra vienas populiariausių būdų logams parsinti. Jis ypač naudingas, kai turite reikalą su Apache logais, nginx access logais, Java stack trace’ais ar bet kokiais kitais standartiniais formatais. Tačiau tikrasis grok galios atsiskleidimas – kai pradedate kurti custom patterns savo specifiniams logams.

Kaip veikia grok sintaksė

Grok pattern’o bazinė struktūra atrodo taip: %{PATTERN:field_name}. Čia PATTERN yra iš anksto apibrėžtas grok pattern’as, o field_name – kaip norite pavadinti ištrauktą reikšmę jūsų dokumente.

Paimkime paprastą pavyzdį. Tarkime, turite tokį log įrašą:

2024-01-15 14:32:18 INFO User john.doe logged in from 192.168.1.100

Grok pattern’as galėtų atrodyti taip:

%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:log_level} User %{USERNAME:username} logged in from %{IP:ip_address}

Po šio parserinimo gausite struktūrizuotą dokumentą su atskirais laukais: timestamp, log_level, username ir ip_address. Kiekvienas iš šių laukų bus prieinamas atskirai Elasticsearch’e, galėsite juos filtruoti, agregavoti, vizualizuoti Kibana’je.

Svarbu suprasti, kad grok bando atitikti visą eilutę. Jei jūsų pattern’as neatitinka viso log įrašo, gausite _grokparsefailure tag’ą. Tai viena dažniausių problemų pradedantiesiems – pattern’as veikia dalinai, bet ne visiškai.

Populiariausi built-in patterns ir jų panaudojimas

Logstash ateina su daugybe iš anksto apibrėžtų pattern’ų. Štai keletas, kuriuos naudosite nuolat:

NUMBER ir INT – skaičių atpažinimui. NUMBER gaudys ir sveikus, ir su kableliu skaičius, o INT tik sveikus.

WORD – bet kokį žodį (raidės, skaičiai, pabraukimai).

DATA ir GREEDYDATA – DATA gaudys bet kokius simbolius kol sutiks kitą pattern’ą, o GREEDYDATA ėda viską iki eilutės pabaigos. Būkite atsargūs su GREEDYDATA – jis tikrai godūs.

QUOTEDSTRING – tekstas tarp kabučių, super naudingas JSON ar CSV logams.

TIMESTAMP_ISO8601, HTTPDATE – įvairūs datos formatai. Timestamp’ai logų parserinime – atskirą tema, nes jų formatų egzistuoja dešimtys.

LOGLEVEL – atpažįsta standartines log lygio reikšmes (ERROR, WARN, INFO, DEBUG ir pan.).

IP, IPV6 – IP adresų atpažinimui.

URI, URIPATH, URIHOST – URL komponentų parserinimui.

Praktikoje dažnai matysite tokius pattern’us kaip %{COMBINEDAPACHELOG}, kuris iš karto parsina visą standartinį Apache access log formatą. Tai kompozitinis pattern’as, sudarytas iš daugelio mažesnių pattern’ų. Galite pažiūrėti jo apibrėžimą Logstash grok-patterns faile – tai puiki mokymosi medžiaga.

Custom pattern’ų kūrimas realiam gyvenimui

Realybėje jūsų aplikacijos logai retai atitinka standartines schemas. Čia prasideda tikrasis darbas – custom pattern’ų kūrimas.

Yra du būdai apibrėžti custom pattern’us. Pirmas – inline, tiesiai Logstash konfigūracijoje:

filter {
  grok {
    match => { "message" => "%{WORD:action} completed in %{NUMBER:duration:float} seconds" }
  }
}

Antras būdas – sukurti atskirą pattern’ų failą. Tai geriau organizacijai, ypač kai turite daug custom pattern’ų. Sukuriate failą, pavyzdžiui, /etc/logstash/patterns/myapp_patterns:

TRANSACTION_ID [A-Z]{3}-[0-9]{6}
CUSTOMER_CODE CUST[0-9]{8}

Tada Logstash konfigūracijoje nurodote:

filter {
  grok {
    patterns_dir => ["/etc/logstash/patterns"]
    match => { "message" => "Transaction %{TRANSACTION_ID:transaction_id} for %{CUSTOMER_CODE:customer_code}" }
  }
}

Vienas patarimas iš patirties – pradėkite nuo paprastų pattern’ų ir testuokite kiekvieną žingsnį. Grok Debugger (http://grokdebug.herokuapp.com arba Kibana Dev Tools) yra jūsų geriausias draugas. Įklijuokite savo log eilutę ir pattern’ą, iš karto pamatysite, ar veikia.

Duomenų tipų konvertavimas ir field’ų transformacijos

Pagal nutylėjimą grok viską ištraukia kaip string’us. Bet jūs tikriausiai norite, kad skaičiai būtų skaičiai, o ne tekstas. Čia naudojamas type coercion:

%{NUMBER:response_time:int} arba %{NUMBER:price:float}

Palaikomi tipai: int, float, boolean. Tai svarbu, nes Elasticsearch’e norite teisingų tipų – kitaip negalėsite daryti matematinių operacijų ar teisingai rūšiuoti.

Kartais reikia ištraukti tik dalį informacijos. Pavyzdžiui, iš URL norite tik path’ą, be query parametrų. Galite kombinuoti grok su kitais filter’iais:

filter {
  grok {
    match => { "message" => "%{COMBINEDAPACHELOG}" }
  }
  
  if [request] {
    grok {
      match => { "request" => "%{WORD:http_method} %{URIPATH:uri_path}" }
      break_on_match => false
    }
  }
}

Pastebėjote break_on_match => false? Tai leidžia taikyti kelis pattern’us tam pačiam laukui. Pagal nutylėjimą Logstash sustoja po pirmo sėkmingo match’o.

Performance optimizacija ir dažniausios klaidos

Grok yra galingas, bet gali būti lėtas, ypač su sudėtingais pattern’ais ir dideliais logų kiekiais. Keletas optimizavimo triukų:

Naudokite anchor’ius. Jei žinote, kad jūsų pattern’as prasideda eilutės pradžioje, naudokite ^. Tai gerokai pagreitina matching’ą.

Būkite specifiniai. Vietoj %{DATA:something} geriau naudoti konkretesnį pattern’ą kaip %{WORD:something}, jei žinote, kad tikitės tik žodžio.

Venkite GREEDYDATA viduryje pattern’o. Jis turėtų būti tik pabaigoje, kitaip regex engine’as turi daug backtrack’inti.

Testuokite pattern’ų tvarką. Jei turite kelis galimus pattern’us, sudėkite dažniausius pirmiausia.

Dažniausia klaida – per daug sudėtingi pattern’ai viename grok statement’e. Geriau suskaidyti į kelis etapus:

filter {
  grok {
    match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{GREEDYDATA:syslog_message}" }
  }
  
  grok {
    match => { "syslog_message" => "%{WORD:program}\[%{NUMBER:pid:int}\]: %{GREEDYDATA:message}" }
  }
}

Kita dažna problema – neatsižvelgimas į escape simbolius. Jei jūsų logai turi specialių simbolių kaip skliaustai, taškai, klaustukų ženklai, juos reikia escape’inti: \[, \., \?.

Multiline logų apdorojimas

Stack trace’ai, JSON objektai, multi-line error pranešimai – visa tai reikalauja specialaus požiūrio. Čia grok dirba kartu su multiline codec’u.

Pavyzdžiui, Java stack trace:

input {
  file {
    path => "/var/log/myapp.log"
    codec => multiline {
      pattern => "^%{TIMESTAMP_ISO8601}"
      negate => true
      what => "previous"
    }
  }
}

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] %{LOGLEVEL:level} %{DATA:logger} - %{GREEDYDATA:message}" }
  }
}

Multiline codec’as sujungia eilutes, kol sutinka naują timestamp’ą. Tada grok parsina jau sujungtą message.

Svarbu: multiline codec’as veikia input lygyje, ne filter lygyje. Tai dažna painiava. Jei gaunate logus per beats ar kitą šaltinį, multiline konfigūracija turi būti ten, ne Logstash’e.

Debugging ir troubleshooting strategijos

Kai jūsų grok pattern’as neveikia, štai kaip diagnozuoti problemą:

1. Naudokite stdout output’ą. Laikinai pridėkite:

output {
  stdout { codec => rubydebug }
}

Tai parodys, kaip atrodo jūsų dokumentai po parserinimo.

2. Pridėkite tag_on_failure:

filter {
  grok {
    match => { "message" => "..." }
    tag_on_failure => ["_grokparsefailure_myapp"]
  }
}

Dabar galite filtruoti Kibana’je pagal šį tag’ą ir pamatyti, kurie logai neparsėjo.

3. Naudokite add_field debug’inimui:

filter {
  grok {
    match => { "message" => "..." }
    add_field => { "debug_pattern" => "pattern_v1" }
  }
}

Tai padeda, kai testuojate kelis pattern’us ir norite žinoti, kuris suveikė.

4. Išjunkite grok laikinai. Kartais problema ne grok, o kažkas kita pipeline’e. Užkomentuokite grok filter’į ir pažiūrėkite, ar logai bent jau patenka į Elasticsearch’ą.

5. Testuokite su vienu log įrašu. Sukurkite test failą su vienu probleminiu log įrašu ir testuokite tik su juo. Taip daug greičiau iteruosite.

Vienas neakivaizdus patarimas – naudokite version control savo Logstash konfigūracijoms. Git repository su grok pattern’ais ir konfigūracijomis išgelbės jus ne kartą, kai kas nors „pataisys” ir sulaužys veikiančią konfigūraciją.

Kai grok nebeužtenka: alternatyvos ir hibridiniai sprendimai

Grok nėra vienintelis būdas parsinti logus. Kartais kiti metodai yra efektyvesni:

JSON filter – jei jūsų logai jau JSON formatu, naudokite json filter’į. Jis daug greitesnis nei grok:

filter {
  json {
    source => "message"
  }
}

Dissect filter – jei jūsų logai turi fiksuotą struktūrą su delimiters, dissect yra 5-10 kartų greitesnis nei grok:

filter {
  dissect {
    mapping => {
      "message" => "%{timestamp} %{+timestamp} [%{thread}] %{level} %{message}"
    }
  }
}

CSV filter – CSV formatui.

KV (key-value) filter – kai turite key=value poras.

Praktikoje dažnai naudojama kombinacija. Pavyzdžiui, dissect pagrindinei struktūrai, o grok specifiniems laukams:

filter {
  dissect {
    mapping => { "message" => "%{timestamp} %{level} %{rest}" }
  }
  
  grok {
    match => { "rest" => "User %{USERNAME:user} action: %{WORD:action}" }
  }
}

Kitas hibridinis požiūris – parsinti dalį informacijos aplikacijos pusėje. Jei kontroliuojate aplikaciją, kuri generuoja logus, kartais lengviau išspausdinti JSON ar struktūrizuotą formatą, nei vėliau parsinti su grok. Structured logging bibliotekos (kaip logback-json, winston JSON format) gali sutaupyti daug vargo.

Tačiau realybėje dažnai turite reikalą su legacy sistemomis, trečiųjų šalių aplikacijomis ar logais, kurių formato negalite pakeisti. Čia grok išlieka nepakeičiamas įrankis. Svarbu žinoti, kada jį naudoti, o kada pasirinkti paprastesnį sprendimą.

Grok patterns – tai skill’as, kuris tobulėja su praktika. Pradėkite nuo paprastų pattern’ų, naudokite debugger’ius, testuokite su realiais duomenimis. Su laiku sukursite savo pattern’ų biblioteką, kuri veiks kaip Lego kaladėlės – kombinuosite jas naujoms užduotims. Ir nepamirškite dokumentuoti savo custom pattern’us – po trijų mėnesių net jūs patys neprisiminsite, ką reiškia tas sudėtingas regex’as.

Daugiau

Python generators: efektyvi atmintis su yield