Accueil Le blog ebiznext
Exploiter vos logs .NET avec ELK

Objet

Chaque application produit des logs qui sont souvent peu ou pas exploités en tant que sources d’informations potentielles. Pourtant, il est possible avec peu d’effort de construire une plateforme de collecte et d’analyse de logs permettant d’extraire des informations importantes comme des indicateurs sur la santé de l’application, mais aussi sur le comportement des utilisateurs, ou encore des données métiers.

Cet article a pour objectif d’expliquer comment mettre en place une infrastructure permettant de collecter, analyser, stocker et restituer les informations contenues dans les logs. Cette infrastructure s’appuie sur la suite ELK proposée par ElasticSearch BV:

  • LogStash : Il collecte et transforme les logs en information et les injecte ensuite dans ElasticSearch.
  • ElasticSearch : Il prend en charge le stockage et l’indexation des informations
  • Kibana : Il gère la restitution sous forme de tableau de bord des données contenues dans ElasticSearch.

Dans cet article, seuls les logs produits par IIS et par les applicatifs .NET seront exploités. Deux outils supplémentaires seront nécessaires pour récupérer les logs et les transférer à LogStash :

  • nxLog : Il gère la récupération des logs produit par IIS et la publication vers le serveur LogStash
  • NLog.SysLog : Il s’agit d’une extension de Framework de log NLog qui permet de publier les logs au format Syslog vers le serveur LogStash.

Le schéma suivant présente l’architecture de la solution.

ELKArchitecture

LogStash

LogStash est un ETL (Extract-Transform-Load). Il permet nativement de :

  • Récupérer les logs provenant de sources variées,
  • Transformer les logs vers de multiples formats,
  • Sauvegarder le résultat de la transformation vers différents systèmes de stockage.

LogStash propose par défaut :

  • 41 entrées : dont tcp, zeromq, twitter, file, collectd, file, eventlog, etc.
  • 20 codecs : dont json, json_lines, multiline, etc.
  • 50 filtres : dont grok, date, geoip, mutate, etc.
  • 55 sorties : dont elasticsearch, stdout, rabbitmq, tcp, etc.

ElasticSearch

ElasticSearch est un support de stockage haute performance permettant d’effectuer des recherches full-text en un éclair. ElasticSearch est :

  • Orienté Document
  • Sans schéma (pas de définition stricte du contenu des index)
  • Distribué sur plusieurs nœuds
  • Hautement disponible
  • Basé sur le moteur de recherche full-text Lucene
  • Accessible à partir d’une API REST
  • Open source

Kibana

Kibana est un outil web de visualisation des données qui s’appuie sur ElasticSearch. Il permet de construire des tableaux de bord exploitant l’API REST d’ElasticSearch.
kibana-dashboard

Installation

Installation d’ElasticSearch

Prérequis : JDK 1.7+
Téléchargez la dernière version d’ElasticSearch ici et décompressez le dans un répertoire. ElasticSearch sera installé en mode service Windows afin de créer une dépendance entre LogStash et ElasticSearch. Cela permettra de démarrer automatiquement ElasticSearch lorsque LogStash démarre.
Exécutez la commande :

bin/service install

puis

bin/service manager

Passer le démarrage en mode automatique, et démarrer le service.
Le résultat suivant est obtenu en exécutant la commande :

curl -X GET http://localhost:9200/
{
status: 200,
name: "Stingray",
version: {
number: "1.3.4",
build_hash: "a70f3ccb52200f8f2c87e9c370c6597448eb3e45",
build_timestamp: "2014-09-30T09:07:17Z",
build_snapshot: false,
lucene_version: "4.9"
},
tagline: "You Know, for Search"
}

Installation de LogStash

L’installation de LogStash se résume à la décompression du fichier zip et à la création d’un fichier bat contenant la commande suivante:

logstash.bat agent -f logstash.conf

Afin de démarrer automatiquement LogStash au démarrage du serveur, LogStash sera installé en tant que service Windows et lié à celui d’ElasticSearch. La création du service Windows sera effectuée grâce à NSSM (Non-Sucking Service Manager téléchargeable ici) qui facilite la création et la configuration de service Windows.
Copier l’exécutable win64 de NSSM dans le répertoire bin de LogStash, puis exécutez la commande suivante :

nssm install logstash

La boite de dialogue de configuration s’ouvre. Les onglets application et détails sont renseignés en fonction de votre environnement. Dans l’onglet Dépendances, ajoutez le service Windows ElasticSearch précédemment créé, puis cliquez sur « Installer le service »

nssm

Installation de Kibana

Téléchargez Kibana et décompressez-le dans le répertoire de votre choix. Kibana nécessite un serveur web capable de servir des fichiers statiques. IIS sera utilisé, mais il est possible d’utiliser tout autre serveur web tels qu’Apache ou Nginx.
Ouvrir la console d’administration d’IIS, et créez un site web mappé sur le répertoire contenant les sources de Kibana. Et c’est fini.

Collecte des logs IIS

Quelles informations va-t-on pouvoir extraire des logs IIS ?

  • Des informations sur les navigateurs et os des utilisateurs
  • Les urls les plus fréquemment demandées
  • Les informations de géolocalisation des utilisateurs à partir de l’adresse IP*
  • Le taux de requête en erreur

(étoile) Attention, la géolocalisation ne fonctionnera pas si le serveur web se trouve derrière un reverse proxy. En effet, IIS considèrera que l’IP cliente est celle du reverse proxy. Pour éviter cet écueil, il faut installer l’extension IIS : Advanced Logging.
Advanced Logging est un module supplémentaire permettant de récupérer des informations stockées dans le header HTTP. L’IP d’origine du client est de manière standard transférer dans le header  « X-Forwarded-For ».

Configuration d’Advanced Logging

  • Une fois le composant installer, redémarrez le serveur Windows.
  • Ouvrir IIS Manager
  • Sélectionner le site web pour lequel on souhaite créer un fichier de log
  • Double cliquer sur Advanced Logging
    worddav9e936117a9a601a5b356dd9ba7c735ad
  • Dans le panel Actions, cliquer sur “Modifier les champs de journalisation” (“Edit Logging Fields”)
  • Dans la boite de dialogue, cliquer sur “Ajouter un champ”(“Add Field”) et remplisser comme suit:

    • ID Champ : X-Forwarded-For
    • Category : Default
    • Source Type : Request Header
    • Source Name : X-Forwarded-For
  • Valider la création du champ et cliquez sur OK dans la boite de dialogue.
  • Sélectionner la définition de journalisation %COMPUTERNAME%-Server
  • Cliquer sur “Dupliquer la définition”, et donner le nom %COMPUTERNAME%-NomDuSite
  • Dans le panel Actions, cliquer sur Modifier la “définition de journalisation”(“Edit Log Definition”)
  • Cliquer sur “Sélectionnez les champs”, puis ajouter les champs suivants dans cet ordre(attention, l’ordre est important):

    • Date-UTC
    • Time-UTC
    • Server-IP
    • Method
    • URI-Stem
    • URI-Querystring
    • Server Port
    • UserName
    • Client-IP
    • X-Forwarded-For
    • User Agent
    • Referrer
    • Status
    • Substatus
    • Win32Status
    • TimeTakenMS
  • Cocher la case “Publier les événements en temps réel”
  • Cliquer sur Modifier le filtre
  • Ajouter l’expression suivante: (‘Site Name’ Égal ‘Nom du site web’)
  • Cliquer sur Appliquer dans le panel d’actions
  • Cliquer sur “Revenir à Advanced Logging”
  • Cliquer sur “Activer Advanced Logging”. Effectuer la même opération pour chaque site
  • Redémarrer IIS

Configuration de nxLog

nxLog est un collecteur de logs. Il sera utilisé pour collecter les logs d’IIS et les transmettre à LogStash. Cet outil devra être déployé sur chacun des serveurs Web pour lesquels on souhaite obtenir les logs.
Nous allons maintenant éditer le fichier de configuration de nxLog situé dans C:\Program Files (x86)\nxlog\conf et ajouter les extensions suivantes :

<Extension json>
Module xm_json
</Extension>
<Extension w3c>
Module xm_csv
Fields $date, $time, $s-ip, $cs-method, $cs-uri-stem, $cs-uri-query, $s-port, $cs-username, $c-ip, $csUser-Agent, $cs-Referer, $sc-status, $sc-substatus, $sc-win32-status, $time-taken
FieldTypes string, string, string, string, string, string, integer, string, string, string, string, integer, integer, integer, integer
Delimiter ' '
QuoteChar  '"'
EscapeControl FALSE
UndefValue -
</Extension>

Ces extensions permettent de parser le fichier de log au format csv, puis de transformer le tout en objet json. L’extension w3c permet de parser le fichier IIS au format CSV. Il est donc important de faire correspondre l’ordre des champs à celui du fichier de log.
Ensuite, il faut définir les « input » en utilisant les extensions précitées. Sachant qu’IIS produit autant de fichiers de log que de site Web, il faut définir autant d’ « input » que de sites web. Voici un exemple de définition d’ « input »

<Input iis_1>
Module im_file
File "C:\inetpub\logs\AdvancedLogs\Default Web Site*DefaultWebSite*.log"
ReadFromLast True
SavePos True
Exec if $raw_event =~ /^#/ drop(); \
else \
{ \
w3c->parse_csv(); \
$SourceName = "IIS-REC01-DefaultWebSite" \
$EventTime = parsedate($date +  " " + $time); \
to_json(); \
}
</Input>

Le module im_file est utilisé pour lire le fichier défini par la commande File à partir de la dernière ligne lue. Une transformation est ensuite effectuée pour passer du fichier CSV au format défini dans l’extension, c’est à dire un objet JSON auquel auront été ajoutées 2 informations supplémentaires :

  • le serveur source de l’événement,
  • la date complète de l’événement.

Ensuite, le flux est ensuite envoyé vers notre serveur LogStash par tcp:

<Output out_iis>
Module om_tcp
Host 172.168.42.61 #Adresse IP du serveur LogStash
Port 3516 #Port sur lequel sera redirigé le flux
OutputType LineBased
</Output>

Notez le port sur lequel seront publiés les logs car c’est sur ce port que LogStash va écouter les messages en provenance des différents serveurs web.
Au final, nous obtenons le fichier de configuration suivant :

<Extension json>
Module xm_json
</Extension>
<Extension w3c>
Module xm_csv
Fields $date, $time, $s-ip, $cs-method, $cs-uri-stem, $cs-uri-query, $s-port, $cs-username, $c-ip, $csUser-Agent, $cs-Referer, $sc-status, $sc-substatus, $sc-win32-status, $time-taken
FieldTypes string, string, string, string, string, string, integer, string, string, string, string, integer, integer, integer, integer
Delimiter ' '
QuoteChar '"'
EscapeControl FALSE
UndefValue -
</Extension>
<Input iis_1>
Module im_file
File "C:\inetpub\logs\AdvancedLogs\Default Web Site*DefaultWebSite*.log"
ReadFromLast True
SavePos True
Exec if $raw_event =~ /^#/ drop(); \
else \
{ \
w3c->parse_csv(); \
$SourceName = "IIS-REC01-DefaultWebSite"; \
$EventTime = parsedate($date + " " + $time); \
to_json(); \
}
</Input>
<Output out_iis>
Module om_tcp
Host 172.16.4.61 #Adresse IP du serveur LogStash
Port 3516 #Port sur lequel sera redirigé le flux
OutputType LineBased
</Output>

Configuration de LogStash

Maintenant que nos serveurs Web envoient des logs vers le serveur LogStash, il faut configurer LogStash pour lui indiquer quel port écouter et avec quel protocole.
Pour cela, ouvrir le fichier de configuration de LogStash : logstash.conf

  • Ajouter un input de tcp pour récupérer les logs publiés par nxLog
    port => 3517 //Port sur lequel logstash écoute
    type => "iis" //type de log
    codec => json_lines //Codec pour décoder le flux soumis. Ici json.
    tags => "rec" //Tags permettant de distinguer le flux
    
  • La notion de type permettra de mettre en place une transformation des logs, tandis que la notion de tags sera utilisée pour distinguer les index dans lesquels seront stockés les logs
  • Ajouter un filtre permettant de transformer le log brut et de l’enrichir avant de le stocker dans ElasticSearch
    if [type] == "iis" {
    mutate {
    add_field => { "requesturl" => "%{cs-uri-stem}?%{cs-uri-query}" }
    }
    useragent {
    source => "csUser-Agent"
    }
    geoip {
    source => "c-ip"
    }
    }
    

L’exemple ci-dessus permet :

  • D’ajouter un nouveau champ, contenant la concaténation de l’url et de la querystring
  • De décoder le useragent pour obtenir les informations sur l’OS, le nom du browser, la version, etc.
  • De déterminer la géolocalisation de l’utilisateur à partir de l’adresse IP du client

Ajouter une sortie qui sera chargée de stocker dans ElasticSearch

output {
elasticsearch {
host => "localhost" #localisation de l'instance elasticsearch
protocol => "http" #protocol utilisé pour communiquer avec elasticsearch
manage_template => false
index => "%{type}%{tags}%{+YYYY.MM}" #template de l'index dans lequel sera stocké le log
}
}

Configuration d’ElasticSearch

Bien qu’Elasticsearch ne nécessite aucune configuration particulière, son fonctionnement peut poser quelques problèmes au moment de la composition des tableaux de bord. Par défaut, lorsque l’on stocke un flux json dans Elasticsearch, celui-ci décompose chaque champ afin de pouvoir effectuer les recherches full-text. Mais cette fonctionnalité peut poser quelques problèmes lors de l’utilisation des panels terms de Kibana. Par exemple, l’url sera décomposée ce qui empêche ensuite d’obtenir un top 10 des urls. Pour éviter cela, il faut créer un template qui s’applique à l’index ElasticSearch, et qui définit comment les champs seront traités par Elasticsearch. Nous utiliserons le template suivant qui indique à ElasticSearch de stocker la donnée brute.


curl -XPUT http://localhost:9200/_template/iis_log -d
{
"template" : "iis-*",
"mappings" : {
"default" : {
"_all" : {"enabled" : false},
"properties" : {
"@timestamp": { "type": "date", "index": "not_analyzed" },
"X-Forwarded-For": { "type" : "ip", "index": "not_analyzed" },
"cs-uri-stem": { "type" : "string", "index": "not_analyzed" },
"cs-UserAgent": { "type" : "string", "index": "not_analyzed" }
}
}
}
}

Cette requête permet de désactiver l’analyseur pour les champs timestamp, x-forwarded-for, cs-uri-stem, cs-user-agent, ce qui nous permettra de restituer correctement le top des urls.

Restitution dans Kibana

Maintenant que les logs sont collectées, puis transformées et enfin stockées dans Elasticsearch, il ne reste plus qu’à les exploiter pour construire des tableaux de bord. Le tableau de bord que nous allons construire affichera les informations de géolocalisation sur une carte, le top des urls, et l’activité sur le site. Kibana a été installé à la racine du site web par défaut. Il est donc accessible à l’url suivante :

http://localhost ou http://<nomduserveur>

Dans un premier temps, nous allons configurer dans quel index Elasticsearch nous allons récupérer nos logs. Rappelez-vous, nous avons stocké nos logs IIS dans un index dont le pattern était : « iis-YYYY.MM » ce qui veut dire que Logstash créera un nouvel index chaque mois. Nous allons reporter ce pattern dans Kibana en ouvrant les options du tableau de bord. Une fois l’index enregistré, nous pouvons commencer à créer des panels de restitution. Pour cela, nous avons besoin de créer des lignes (« rows ») qui vont contenir nos panels. Une fois la ligne créée, nous allons lui ajouter des panels.

worddave54c26681fac564bcb7dfc01358d949b
Cliquer sur le « + » à gauche de la ligne. Le panel de création de panel s’affiche en haut de l’écran. Pour notre premier graphique, nous allons restituer l’activité sur le site IIS. Cliquer sur « + » de la ligne, puis sélectionner « histogram », puis enregistrer, et voici le résultat :

worddaved6ae0c5a880f0f0532a730ae9846f6b
Maintenant, nous allons ajouter un panel permettant de connaitre la position de nos utilisateurs. Pour cela, nous allons utiliser le panel better-map qui s’appuie sur les coordonnées produites par le plugin geoip. Ajoutez le champ geoip.location dans le champ field et enregistrer le panel. La carte s’affiche avec les points de connexion indiquant le nombre de requêtes reçues.

worddave6f508f0cabd1084318b8f660d2145b9
Pour notre dernier panel, nous allons utiliser le panel « terms » pour afficher le top des urls.

  • Sélectionner le champ cs-uri-stem.
  • Limitez le nombre d’url à 10,
  • Appliquer un Order « count » et cliquez sur « Créer ».

Kibana nous affiche un histogramme contenant les 10 urls les plus accédés sur la période sélectionnée.
worddavb0ec713020344d2e81e9b542241c6407 Maintenant que nous avons configuré notre tableau de bord, nous allons pouvoir lui donner un nom et l’enregistrer dans ElasticSearch. Kibana permet ainsi de créer autant de tableau de bord que l’on souhaite.

Utilisation des logs applicatifs

Les applications peuvent générer différents types de logs. Ils peuvent contenir seulement les exceptions, mais ils peuvent aussi contenir d’autres informations métiers. Dans notre exemple, nous intégrerons uniquement les logs d’exceptions.
NLog qui est un des Framework de Log les plus utilisés dans le monde .NET. Il sera utilisé pour créer ces logs et les soumettre au serveur LogStash. Les informations seront ensuite restituées dans Kibana pratiquement en temps réel et permettront d’améliorer la réactivité de prise en compte des anomalies.

Configuration de NLog

Le format de message Syslog sera utilisé pour communiquer avec Logstash. Par défaut, NLog ne gère pas ce format, et il faut lui ajouter une extension qui lui permettra d’écrire ses logs au format syslog. Pour cela, nous utilisons Nuget Package Manager afin de récupérer NLog et l’extension :

Install-Package NLog
Install-Package NLog.Targets.Syslog

Une fois les packages installés, la « target » doit être configuré afin de préciser vers quel serveur le message doit être publié. Pour cela, le fichier de configuration de NLog doit être modifier pour contenir les informations suivantes :

<extensions>
<add assembly="NLog.Targets.Syslog" />
</extensions>
<targets>
<target name="syslog"
xsi:type="Syslog"
syslogserver="172.16.4.61"
port="515"
facility="Local7"
sender="MyProgram"
layout="${level} ${message}"></target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="syslog" />
</rules>

Le code reste inchangé :

Logger.Info(message);

Configuration de Logstash

Afin de récupérer les logs dans logstash, l’input suivant est ajouté dans le fichier de configuration de logstash:

tcp {
port => 515
type => syslog
}
udp {
port => 515
type => syslog
}

Ensuite, nous ajoutons un filtre afin de transformer le log en log exploitable par Elasticsearch et Kibana :

if [type] == "syslog" {
grok {
match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program} %{WORD:LogLevel} % {GREEDYDATA:syslog_message}" }
add_field => [ "received_from", "%{host}" ]
}
date {
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss " ]
}
}

Nous utilisons grok afin de modifier le contenu du message pour le découper et le stocker dans Elasticsearch. Il ne reste plus qu’à stocker le résultat dans ElasticSearch.

output {
elasticsearch {
host => "localhost"
protocol => "http"
manage_template => false
index => "%{type}-%{+YYYY.MM}"
}
}

Nous disposons maintenant d’une plateforme de log centralisée, capable d’exploiter les logs produits par notre serveur web, mais aussi par nos applicatifs. Il est tout à fait possible d’aller encore plus loin et de proposer des tableaux de bords métiers ou marketing en produisant de nouveaux logs.