
ELK
Analysez, recherchez et visualisez vos données
Amenez moi à la partie technique
Votre application produit des données qui peuvent rapidement devenir conséquentes. Plus elles sont nombreuses et plus l'analyse que vous pourrez en faire sera lente. ELK va vous permettre de :
Pensez à bien formater vos logs, la construction de vos filtres n'en sera que plus simple.
Vous pouvez aussi ajouter un identifiant de suivi au sein de votre application. Il vous permettra, par exemple,
de suivre les actions associées à une requête. En effet, grâce à Elasticsearch et Kibana, vous pourrez afficher les logs contenant cet id.
A l'intérieur de la bulle de votre projet se trouvent les machines et les services. Chaque service produit des logs qui sont transférés vers un concentrateur.
Un shipper va envoyer les logs vers la plateforme ELK. Dans notre cas, nous utiliserons Rsyslog.
Imaginons une requête produisant ce log :
127.0.0.1 - - [02/Jul/2015:00:13:48 +0200] "VboEgQpaiRAAAB0bBJgAAACD" "GET /sthing/ HTTP/1.0" 200 3175 17173 "-" "-"
"VboEgQpaiRAAAB0bBJgAAACD" correspond à un identifiant unique généré pour chaque requête entrante et permettant de suivre les actions correspondantes
qui sont effectuées
Le serveur reçoit cette requête et produit le log suivant :
127.0.0.1 - - [02/Jul/2015:00:13:48 +0200] "VboEgQpaiRAAAB0bBJgAAACD" "GET /sthing/2 HTTP/1.1" 200 35
Enfin le logger de l'application produit ces 2 logs :
2015-07-02 00:13:48,804 - [VboEgQpaiRAAAB0bBJgAAACD] - [INFO] [Class.java:55] - Content
2015-07-02 00:13:48,885 - [VboEgQpaiRAAAB0bBJgAAACD] - [INFO] [Class.java:55] - Content
"VboEgQpaiRAAAB0bBJgAAACD" correspond à l'identifiant unique récupéré
Maintenant imaginons une configuration Rsyslog permettant d'attribuer une étiquette à chaque log envoyé.
Les logs étant tous envoyés sur un même port, cette étiquette va nous servir plus tard à différencier les logs.
Il est conseillé d'utiliser un fichier de configuration différent pour chaque filtre. J'utilise, par exemple, cette convention :
Voici le contenu de notre premier fichier de configuration. Il définit simplement un port sur lequel Logstash va recevoir les logs via Rsyslog
input {
syslog {
port => 5000
}
}
Ce premier filtre sert de prétraitement. Il va attribuer à chaque logs de nouveaux tags afin de simplifier la suite du travail avec Logstash et les recherches de logs dans Elasticsearch.
filter {
if [program] == "front-access" {
mutate {
add_tag=> ["front","access"]
}
}
if [program] == "middle-access" {
mutate {
add_tag=> ["middle","access"]
}
}
if [program] == "middle-applicative" {
mutate {
add_tag=> ["middle","applicative"]
}
}
}
Ce second filtre va s'occuper des logs access. L'intérêt d'avoir défini des tags précédemment est de pouvoir les groks similaires en un seul filtre. Par exemple ici, les logs access ont relativement le même format. Il est possible de définir notre filtre grok pour qu'il accepte plusieurs patterns.
filter{
if "access" in [tags]{
grok {
match => { "message" => ["<pattern1> ","<pattern2>"]}
}
date {
locale => "en"
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
timezone => "Europe/Paris"
}
mutate {
remove_field => ["timestamp"]
}
}
}
La plupart des logs de production est en locale EN, tandis que nos postes sont en locale FR.
Si l'horodatage est uniquement en numérique, que le format soit YYYY-MM-dd, MM/dd/YY ou autre ne pose pas de problème majeur, il suffit de bien créer sa regex.
En revanche, pour des logs type Apache par exemple (date : 29/Aug/2014), le mois n'est pas au format numérique. La regex serait donc dd/MMM/YYYY, avec "MMM" interprétés selon la locale du poste. "Aug" ne correspond à aucun mois en locale FR, les logs sont donc datés arbitrairement par logstash à la date courante lors de l'évaluation.
Pour remédier à cela, il faut utiliser l'attribut locale lors du parsing de la date.
De plus, il peut être intéressant faire correspondre le champ @timestamp avec la date figurant sur le logs.
Ainsi, sur Kibana, le log apparaît bien à l'heure à laquelle il a été produit et non pas celle de son analyse par logstash.
"%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] \"%{DATA:requestId}\" \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:response} (?:%{NUMBER:bytes}|-) %{NUMBER:responseTime:float} %{QS:referrer} %{QS:agent}"
"%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] \"%{DATA:requestId}\" \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:response} (?:%{NUMBER:bytes}|-)"
Ce dernier filtre va permettre de parser nos logs applicatifs
filter{
if "applicative" in [tags]{
grok {
match => { "message" =>"<pattern3>"}
}
date {
match => ["timestampiso", "yyyy-MM-dd HH:mm:ss,SSS"]
target => "@timestamp"
timezone => "Europe/Paris"
}
mutate {
remove_field => ["timestampiso"]
}
}
}
"%{TIMESTAMP_ISO8601:timestampiso} - \[%{DATA:requestId}\] - \[%{LOGLEVEL:loglevel}\] \[%{WORD:class}.%{WORD}:%{NUMBER:line:float}\] - (?m)%{GREEDYDATA:content}"
Comme dans cet exemple :
2015-07-02 04:21:44,600 - [INFO] [Class.java:523] - Content [
line1
...]
Vous pouvez utiliser les codec multilignes en input :
codec => multiline {
pattern => "<pattern>"
what => "previous"
negate => true
}
Ou le filtre multiligne
multiline {
pattern => "<pattern>"
what => "previous"
negate => true
}
Ceci est en exemple, la configuration de l'output dépend essentiellement de l'architecture utilisée
output {
elasticsearch { host => "elasticsearch" }
stdout { codec => rubydebug }
}
Lors du découpage des logs par regex grok, il est possible d'avoir des patterns tels que : %{INT:responseTime}.
Attention ! Cela ne veut pas dire que la colonne "responseTime" dans ES sera typée "integer". Elle sera typée "string", comme la quasi-totalité des autres colonnes.
Pour pallier à ceci, deux solutions s'offre à vous :
Directement lors du parsing
grok {
match => { "message" =>"...%{NUMBER:responseTime:integer}..."}
}
Ou dans un mutate
mutate {
convert => [ "responseTime", "integer" ]
}
Après le lancement de la stack ELK, vous devriez pouvoir accéder à Kibana.
La première page sur laquelle vous allez tomber est la partie "Settings".
Il s'agit simplement de dire à Kibana le format d'index qu'il doit chercher.
Si des logs ont correctement été parsés, un simple rafraichissement de la page devrait vous permettre de créer l'index.
Vous aurez ainsi accès aux différents champs créés, automatiquement ou via vos filtres, par logstash lors du parsing des logs.
Vous pouvez désormais vous rendre dans la partie "Discover".
Pour pouvoir visualiser vos logs, choisir en haut à droite de la page, la plage horaire de recherche en fonction de la date de vos logs.
Vous devriez alors normalement voir vos logs. Il est possible d'effectuer de recherche directment via des requêtes, ou en utilisant la table de gauche.
La partie "Visualisation" vous permet de créer des graphes en fonction d'une ou plusieurs recherches. La partie "Dashboard" permet de rassembler et organiser plusieurs visualisations sur un même tableau de bord.
Vous pouvez également, par exemple, recentrer votre recherche sur les logs contenant un id précis :
Vous verrez ainsi tous les logs correspondant à une requête.
Afin d'effectuer une recherche dans Kibana, il est possible
Lors de la création de visualisations dans Kibana 4 vous avez la possibilité d'utiliser des scripts dans le champ "Json input ".
Pour ce faire, vous aurez sans doute besoin de rajouter cette ligne dans le fichier de config Elasticsearch.yml :
script.disable_dynamic: false
De cette façon vous pourrez alors utiliser des scripts de la forme suivante :
{"script":"log(_value)/100"}
Je m'appelle Alan DAMOTTE. Je suis un étudiant ingénieur de 5ème année en Réseaux Informatiques et Communication Multimédia (Polytech Grenoble).