ASP.NET HealthMonitoring e le informazioni aggiuntive delle eccezioni

L’HealthMonitoring di ASP.NET è sicuramente uno strumento potentissimo per tenere traccia dello stato di salute della nostra applicazione web. Uno dei suoi principali vantaggi è che dalla versione 2.0 di ASP.NET è già presente con un nutrito insieme di provider che permettono di loggare informazioni in svariati repository.
Entranto più nello specifico si hanno a disposizione i seguenti provider:

  • EventLogWebEventProvider (memorizza nell’Event Log di Windows);
  • SqlWebEventProvider (Database Sql Server 2000/05/08);
  • WmiWebEventProvider (sfrutta il Windows Management Instrumentation per gestire le notifiche);
  • SimpleMailWebEventProvider (Invia una Email);
  • TemplatedMailWebEventProvider (Invia una Email in base ad un template);
  • TraceWebEventProvider (sfrutta il trace di ASP.NET);

Ovviamente, essendo basato a provider, l’healthmonitorig offre la possibilità di realizzare ed utilizzare delle implementazioni custom, nel caso quelli precedentemente elencati non soddisfini le esigenze dello sviluppatore. Per far ciò è necessario realizzare una classe che implementi una delle apposite classi astratte messe a disposizione dal .NET Framework -WebEventProvider o BufferedWebEventProvider - ed aggiungere al loro interno propria logica.

Personalmente ritengo più che sufficienti i provider messi a disposizione e non sentirei la necessità di realizzarne uno custom se quelli messi a disposizione memorizzarsero anche le informazioni presente nella collection Data della classe Exception.
In attensa del Historical Debugger, per poter capire cosa non va nella nostra applicazione e riprodurlo, è necessario riempire il codice di logging per poter catturare e riprodurre una specifica situazione che scatena un’errore.
Purtroppo alcune volte un Logger che memorizza soltanto l’eccezione può non bastare in quanto il problema può riscontrarsi solo con alcuni paramentri e, in questi casi, è necessario memorizzare anche loro insieme all’eccezzione.

Per far ciò una buona pratica è quella di inserire questi valori nell’apposito Dictionary Data presente nell’oggetto Exception in modo da avere in un’unico oggetto sia lo stack che le informazioni legate all’eccezione.
Lo snippet seguente mostra come sfruttarlo:

public static void Send(Uri sourceUrl, Uri targetUrl)
{
    try
    {
        //do something
    }
    catch (Exception ex)
    {
        ex.Data.Add("Source Url: ", sourceUrl);
        ex.Data.Add("Target Url: ", targetUrl);

        Log.Log.General.Error("Send PingBack Error", ex);
    }
}

Purtroppo i provider dell’HealthMonitoring messi a disposizione dal .NET Framework non memorizzano questo tipo di informazioni, il che ci obbliga (se siamo interessati a tener traccia di queste informazioni) a scriverci un provider custom che dovrebbe essere più o meno così:

public class HealthMonitorDataBaseProvider : BufferedWebEventProvider
{
    public override void ProcessEvent(WebBaseEvent eventRaised)
    {
        if (UseBuffering)
            base.ProcessEvent(eventRaised);
        else
            Save(new[] { eventRaised });
    }

    public override void ProcessEventFlush(WebEventBufferFlushInfo flushInfo)
    {
        Save(flushInfo.Events);
    }

    private static void Save(ICollection eventCollection)
    {
        WebRequestInformation requestInformation = null;
        Exception errorException = null;
        MyLog item = new MyLog();

        foreach (WebBaseEvent eventRaised in eventCollection)
        {
            //popolo myLog 
            
            if (eventRaised is WebBaseErrorEvent)
                errorException = ((WebBaseErrorEvent)eventRaised).ErrorException;

            if (errorException != null)
            {
                StringBuilder sb = new StringBuilder(errorException.ToString());

                if (errorException.Data != null && errorException.Data.Count > 0)
                {
                    sb.Append(Environment.NewLine);

                    foreach (DictionaryEntry de in errorException.Data)
                    {
                        sb.Append(de.Key.ToString());
                        sb.Append(": ");
                        sb.Append(de.Value.ToString());
                        sb.Append(Environment.NewLine);
                    }
                }

                logInstance.Details = sb.ToString();
            }

            new LogService().Save(item);
        }
    }
}

stay connected!


Comments