ASP.NET : monitorer vos applications avec ELK

Julien LO PRESTI

De nos jours, les logs et traces font partie d'une étape importante dans le développement d'une application. La nécessité de détecter des anomalies et de réagir rapidement requiert la mise en place d'une solution de monitoring robuste, capable de stocker, d'analyser et de lire ces logs. Aujourd'hui, nous allons voir comment mettre en place une plateforme de logging basée sur la stack ELK pour monitorer nos applications ASP.NET.

Commentez

- Titre : ASP.NET : monitorer vos applications avec ELK

- Auteur : Julien LO PRESTI

- Parution : 8 juin 2016

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2016 Julien LO PRESTI. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. ELK : kézako

- II. Installation de l'environnement

- III. Configuration

- IV. Intégration dans notre application

- V. Création du dashboard

- V-A. Ajout des indices

- V-B. Création des recherches

- V-C. Création des visualisations

- V-D. Création du dashboard

- VI. Remerciements

I - ELK : kézako

ELK est l'acronyme des trois produits phares de la société Elastic.

- Elasticsearch servira de support de stockage. Il est reconnu pour sa capacité à ingérer des volumes de données conséquents.

- Logstash est la brique centrale de notre solution. On peut la voir comme un routeur de messages. Ici, elle s'occupera de récupérer les logs, de les normaliser pour ensuite les déverser dans Elasticsearch.

- Kibana est la dernière brique de la solution. Elle permet de visualiser les logs collectés,
de créer des visualisations pour ensuite les agréger dans des dashboards.

La combinaison de ces trois produits va nous permettre de mettre en place une solution de logging performante.

Le code source et les configurations associées à la démo sont disponibles à l'adresse suivante : Github Soat.

II - Installation de l'environnement

La première étape avant de commencer à installer ELK est l'installation du JDK.

Une fois celui-ci installé, ajoutez une variable d'environnement JAVA_HOME pointant sur l'installation du JDK.

Nous pouvons maintenant commencer l'installation d'ELK !
Récupérez les install sur les URL suivantes : Elasticsearch, Logstash et Kibana.

Commencez par installer Elasticsearch : pour cela, exécutez service.bat. Un nouveau service va être installé et sera disponible dans les services Windows (services.msc).

Pour installer Logstash et Kibana, nous allons utiliser un logiciel nommé NSSM, qui permet d'exécuter n'importe quel exécutable comme un service Windows.

Rendez-vous sur NSSM pour récupérer le logiciel, puis ajoutez le dossier dans la variable PATH

Pour installer Kibana, lancez la commande suivante :

nssm install kibana

Sur la fenêtre qui apparaît, faites pointer le chemin sur le fichier kibana.bat

Pour logstash, il faut créer deux fichiers dans le répertoire bin après avoir extrait le zip :

- logstash.config qui contiendra la config de logstash ;

- service.bat qui sera la commande exécutée lors du démarrage du service Windows. Ce fichier contient la commande suivante :

1. logstash.bat agent -f logstash.config

On spécifie seulement à logstash de charger le fichier de config que l'on vient de créer.

Une fois les installations terminées, nous pouvons passer à la configuration de logstash.

III - Configuration

Passons maintenant à la configuration de logstash. Un fichier de configuration logstash contient trois types de sections :

- input : cette section permet de configurer comment logstash va récupérer les logs. Il existe une multitude de connecteurs déjà disponibles avec l'installation de logstash, qui répondra sûrement à votre demande ;

- filter : cette section permet d'agir sur les logs récupérés en input. On va pouvoir, par exemple, parser le log pour ajouter un ou plusieurs champs, ou encore supprimer certains logs selon une condition, ajouter des données géographiques selon l'ip contenue dans un champ etc. C'est assez puissant et tout comme les inputs, il existe déjà des plugins disponibles !

- output : la dernière partie, comme vous pouvez l'imaginer, permet de configurer la sortie. C'est-à-dire l'endroit où l'on souhaite déverser nos logs que logstash aura reçus en entrée puis modifier selon le besoin. En sortie de logstash, on pourrait par exemple, produire un fichier au format CSV, appeler une URL en HTTP, écrire un message sur IRC ou HipChat. Bref, il existe différentes solutions pour stocker nos logs. Pour voir un aperçu des plugins disponibles : https://www.elastic.co/guide/en/logstash/current/input-plugins.html.

Dans notre application ASP.NET, en plus de vouloir stocker les logs dans Elasticsearch, nous allons également séparer les différents types de logs dans des index différents. Sur notre site, nous allons avoir :

- des logs techniques, utiles pour faire remonter les exceptions et utilisés par l'équipe support en cas d'anomalie ;

- des logs de performances, pour monitorer et détecter des latences anormales ;

- des logs business, pour créer des dashboards sur l'usage des fonctionnalités de notre site.

L'intérêt de séparer les types de logs dans des index différents va nous permettre d'appliquer une stratégie différente sur la rétention des données et donc gagner en performance lors des recherches dans kibana.
On pourrait ainsi appliquer la stratégie suivante :

- les logs techniques pourraient être conservés un mois ;

- les logs de performance, 15 jours ;

- les logs business, un an, voire plus.

Nous allons définir une configuration pour l'input, qui sera un listener UDP, un filter, qui analysera la chaîne du useragent, et une configuration pour l'output qui déversera les logs dans Elasticsearch, selon le type de log (technical, performance, business).

Le fichier logstash.config ressemble donc à ceci :

input {
udp {
port => 12300 //port du listener
codec => json //le format des logs que l'on reçoit
type => "logs"
add_field => ["env", "DEV"] //on ajoute un champ 'env' à tous nos logs
}
}
filter {
useragent {
source => "[properties][s-useragent]" //proprieté contenant la chaîne du useragent (ce champs sera fourni par l'application ASP.NET)
target => "browserInfo" // propriété ou sera stocké l'extraction des informations concernant le navigateur
}
}
output {
elasticsearch {
hosts => ["192.168.1.64:9200"] // IP:PORT du elasticsearch (9200 port par défaut)
index => "%{[properties][type]}-%{+YYYY.MM.dd}" //on spécifie l'index de notre log. On concatene le type du log + la date
document_type => "%{[properties][type]}" //on type le log
workers => 1
}
}

Une fois terminé, vous pouvez démarrer les trois services. Si tout fonctionne, vous devriez pouvoir accéder à Elasticsearch via http://localhost:9200 et à kibana via http://localhost:5601.

Nous pouvons maintenant passer à la dernière partie, l'intégration avec notre application ASP.NET !

IV - Intégration dans notre application

La partie logging coté ASP.NET sera gérée via log4net qui est une librairie assez répandue dans le monde .NET.

Au niveau des librairies, nous allons donc avoir besoin de log4net et log4net.Ext.Json, afin de pouvoir sérialiser le log dans un format JSON pour le transmettre à logstash. La sérialisation n'est pas obligatoire, car on pourrait très bien avoir un filtre coté logstash qui analyse le log.
Pour l'installation des deux packages, il suffit d'ouvrir sa console nuget et d'exécuter les deux commandes suivantes :

1. Install-Package log4net

2. Install-Package log4net.Ext.Json

Une fois installé, il ne manque plus qu'à configurer log4net. Rappelez-vous, nous avons configuré logstash avec un listener UDP. On a donc juste besoin de configurer une appender UDP log4net !

La configuration log4net est la suivante :

1. <log4net>

2. <appender name="DebugAppender" type="log4net.Appender.TraceAppender">

3. <layout type="log4net.Layout.SerializedLayout, log4net.Ext.Json">

4. <default />

5. <member value="properties" />

6. </layout>

7. </appender>

8. <appender name="UdpAppender" type="log4net.Appender.UdpAppender">

9. <param name="RemoteAddress" value="192.168.1.64" /> //on fait pointer vers l'ip ou est installer logstash

10. <param name="RemotePort" value="12300" /> //on spécifie le port que l'on a mis dans la config logstash

11. <layout type="log4net.Layout.SerializedLayout, log4net.Ext.Json">

12. <default />

13. <member value="properties" />

14. </layout>

15. </appender>

16. <root>

17. <level value="INFO" />

18. <appender-ref ref="DebugAppender" />

19. <appender-ref ref="UdpAppender" />

20. </root>

21. </log4net>

Il ne reste plus qu'à ajouter des logs dans notre application. On peut se faire une classe d'extension pour logger dans le bon index. Rappelez-vous, dans la config logstash, nous avons précisé que l'index se basait sur la propriété : properties["type"] !

1. public static class LogExtensions

2. {

3. private static void LogEvent(this ILog log, string message, IDictionary<string, object> properties, Level level, Exception ex = null)

4. {

5. var logEvent = new LoggingEvent(log.GetType(), log.Logger.Repository, log.Logger.Name, level, message, ex);

6. foreach (var property in properties)

7. {

8. string key = property.Key;

9. logEvent.Properties[key] = property.Value;

10. }

11. log.Logger.Log(logEvent);

12. }

14. public static void LogTechnical(this ILog log, string message, IDictionary<string, object> properties, Level level, Exception ex = null)

15. {

16. properties["type"] = "technical";

17. log.LogEvent(message, properties, level, ex);

18. }

20. public static void LogBusiness(this ILog log, string message, IDictionary<string, object> properties)

21. {

22. var level = Level.Info;

23. properties["type"] = "business";

24. log.LogEvent(message, properties, level);

25. }

27. public static void LogPerf(this ILog log, long duration, IDictionary<string, object> properties)

28. {

29. var level = Level.Info;

30. if (duration > 1000)

31. level = Level.Warn;

32. properties["duration"] = duration;

33. properties["type"] = "performance";

34. log.LogEvent($"Action took {duration} ms to be executed", properties, level);

35. }

36. }

V - Création du dashboard

L'installation et l'intégration sont terminées, nous pouvons passer à la partie sympathique : la création de nos dashboards, rendez-vous sur Kibana !

V-A - Ajout des indices

La première chose à effectuer, c'est de rajouter les sources de données.
Il faut se rendre dans les settings puis indices. Ensuite on spécifie le pattern de l'index, ici ça sera technical-*, business-* et performance-*, et le champ contenant la date qui sera utilisée par kibana pour filtrer dans le temps.

V-B - Création des recherches

La création d'un dashboard passe par trois étapes :

- une recherche : qui permet de filtrer et de sélectionner un scope de logs ;

- une visualisation : qui affichera sous forme de graphe/tableau/compteur la représentation de la recherche sélectionnée ;

- un dashboard : qui agrège un ensemble de visualisation sur un même écran.

Dans notre exemple, nous allons créer un dashboard pour visualiser le temps moyen de chargement des pages selon le navigateur.

Nous allons récupérer tous les logs de performances qui ont la propriété browser existante.

Pour savoir si une propriété existe sur un document, on peut utiliser la méthode exists:"property_name".

Dans notre cas, la recherche devient :

_exists_:"browserInfo.name"

On sauvegarde la recherche et on peut passer à l'étape 2, les visualisations.

V-C - Création des visualisations

À partir de la recherche sauvegardée, nous allons construire deux graphes :

- un pour représenter la répartition des navigateurs ;

- un autre pour visualiser le temps moyen de chargement par URL.

Allez dans l'onglet Visualize, choisissez Pie Chart puis From a save search et sélectionnez la recherche faite précédemment.

Enfin, configurez le graphe de façon à le séparer en fonction du nom du navigateur.

Une fois fini, n'oubliez pas de sauvegarder le graphe.

Refaites un nouveau graphe, mais cette fois-ci, il s'agira d'un Area chart. Il nous permettra de visualiser facilement le temps moyen de chargement des pages par navigateur.

Une fois terminé, nous allons pouvoir passer à la dernière étape en allant sur l'onglet dashboard !

V-D - Création du dashboard

Cette étape est la plus simple, car il suffit juste de sélectionner les graphes que l'on a créés juste avant. Une fois ajoutés, vous devriez arriver au résultat suivant :

Grâce au dashboard, nous pouvons remarquer que le temps moyen de chargement de la page Performance est anormalement long sur Chrome et Edge (~500 ms), alors que sur les autres navigateurs nous sommes plus vers ~50 ms.
Si nous regardons le code du projet, on peut remarquer que j'ai introduit de la latence volontairement pour Chrome (et qu'Edge est également reconnu comme un « Chrome »).

Cet article donne une introduction sur l'intégration de la stack ELK avec une application ASP.NET pour rendre le débogage et le monitoring plus sympathique. Vous n'avez plus d'excuse pour ne pas l'intégrer dans votre projet ! Évidemment, la partie logging représente la première étape importante dans le monitoring de vos applications. On peut imaginer des scénarios plus complexes en ajoutant une brique pour gérer la rétention des logs ou encore la partie alerting qui permettra de rajouter de l'intelligence dans vos logs afin de pouvoir agir avant que les problèmes ne surviennent ! Nous aborderons ces deux sujets lors d'un prochain article afin de devenir une légende des logs !

VI - Remerciements

Ce tutoriel a été publié avec l'aimable autorisation de la société Soat.

Nous remercions Laethy pour la gabarisation et Malick SECK pour la relecture orthographique.