Gestion d’erreurs centralisée avec Elmah – Partie 1
Dans un premier temps, il faut que les applications puissent envoyer leurs erreurs à un serveur central. La méthode qui combine le mieux simplicité et souplesse est d’utiliser HTTP pour transmettre les erreurs en POST.
Elmah ne comporte pas de HttpErrorLog, on doit donc créer notre propre implémentation. Facile ! Il suffit d’hériter de la classe abstraite ErrorLog.
using System;
namespace Elmah
{
public class HttpErrorLog : ErrorLog
{
public override ErrorLogEntry GetError(string id)
{
throw new NotImplementedException();
}
public override int GetErrors(int pageIndex, int pageSize, System.Collections.IList errorEntryList)
{
throw new NotImplementedException();
}
public override string Log(Error error)
{
throw new NotImplementedException();
}
}
}
Il y a seulement 3 méthodes à redéfinir : GetError, GetErrors et LogError. Les deux premières sont utilisées pour la consultations des erreurs et la troisième pour la journalisation des erreurs.
Comme on veut pouvoir consulter les erreurs localement, le HttpErrorLog encapsulera un MemoryErrorLog qui stocke les erreurs en mémoire pour consultation locale.
Elmah possède un mécanisme pour encoder une erreur en Xml. C’est ce que nous utiliserons pour transmettre en HTTP les informations relatives à l’erreur.
L’url à laquelle sont transmises les erreurs est définie dans le web.config. Il est également important de définir l’attribut applicationName pour distinguer les erreurs d’applications différentes.
Enfin, Elmah est compatible avec les versions 1.1 à 3.5 du framework .NET donc il faut faire attention à ce que le code compile sur toutes ces versions.
Voici la classe résultante :
using System;
using System.Web;
using System.Text;
using System.IO;
using System.Net;
using System.Globalization;
using System.Collections;
using System.Collections.Specialized;
namespace Elmah
{
public class HttpErrorLog : ErrorLog
{
private MemoryErrorLog _innerMemoryErrorLog;
private string _loggingUrl;
public HttpErrorLog(IDictionary config)
{
_innerMemoryErrorLog = new MemoryErrorLog(config);
_loggingUrl = (string)config["url"];
string appName = (string)config["applicationName"];
ApplicationName = appName == null
? string.Empty
: appName;
}
public override ErrorLogEntry GetError(string id)
{
return _innerMemoryErrorLog.GetError(id);
}
public override int GetErrors(int pageIndex, int pageSize, System.Collections.IList errorEntryList)
{
return _innerMemoryErrorLog.GetErrors(pageIndex, pageSize, errorEntryList);
}
public override string Log(Error error)
{
error.ApplicationName = ApplicationName;
Guid id = new Guid(_innerMemoryErrorLog.Log(error));
NameValueCollection nameValuePairs = new NameValueCollection();
nameValuePairs.Add("errorId", id.ToString());
nameValuePairs.Add("allXml", ErrorXml.EncodeString(error));
StringBuilder queryString = new StringBuilder();
foreach (string key in nameValuePairs.AllKeys)
{
queryString.Append(key + "=" + HttpUtility.UrlEncode(nameValuePairs[key]) + "&");
}
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_loggingUrl);
request.Method = WebRequestMethods.Http.Post;
request.ContentType = "application/x-www-form-urlencoded";
byte[] byteData = Encoding.ASCII.GetBytes(queryString.ToString());
request.ContentLength = byteData.Length;
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
postStream.Close();
}
return id.ToString();
}
}
}
Dans la méthode Log on utilise la classe HttpWebRequest pour envoyer les données Xml dans une requète HTTP. On envoie aussi l’Id que nous a retourné le MemoryErrorLog. Ce sont les seules informations dont nous avons besoin.
Comme Elmah est un projet libre, on peut ajouter directement cette classe à la source du projet et le recompiler selon nos besoins.
Voici comment l’utiliser dans le web.config de l’application à surveiller :
<elmah>
<errorlog applicationname="MyApplication" type="Elmah.HttpErrorLog, Elmah" url="http://logurl/LogError.axd" />
<errorfilter>
<test>
<equal type="Int32" value="404" binding="HttpStatusCode" />
</test>
</errorfilter>
</elmah>
Ici on a rajouté un filtre pour ne pas envoyer les erreurs 404. Pour un exemple de configuration complète d’Elmah, voir cet article.
Dans la troisième partie nous verrons comment récupérer ces erreurs et les enregistrer dans la base de données centralisée.