9

Dec

C# e un log file leggibile!!!

A chiunque sviluppa applicazione medio – grandi è capitato di scontrarsi con la lettura di log file incomprensibili, spesso generati manualmente senza perderci troppo tempo che però portano a una conseguenza: è impossibile capire dove è l’errore Sad smile.

Oggi è uno di questi giorni e stufo di perdere tempo a leggere log interminabili ho finalmente trovato una soluzione che mi soddisfa.

Ricapitoliamo velocemente quali sono le tecniche solitamente usate “per far presto”…

  1. Console2File: certo è veloce, basta redirigere l’output di console su un file, ma il risultato è quello di generare file lunghissimi e inutili.
  2. Scrittura diretta su file: anche questo tutto sommato veloce, ma ti costringe a riempire il programma di codice inutile e in caso programma multi-thread devi stare anche attendo all’accesso concorrente al file: troppo codice da inserire nel programma.
  3. Classe Statica: versione più elegante della sempplice scrittura su file, sicuramente è una buona soluzione, ma necessita di tempo per essere implementata bene. In rete si trovano migliaia di esempi, molti di dubbia fattura, ma il problema è che richiedo comunque tempo per essere adattati.

Veniamo ora alla soluzione a cui sono giunto, spesso mi ero domandato se in c# ci fossero interfacce predefinite adibite al logging e come utilizzarle anche in applicazione WCF. Andiamo con ordine.

La prima cosa è importare [code language="csharp"]  using System.Diagnostics; [/code] e ogni volta che si vuole srivere il log: [code language="csharp"]  Debug.WriteLine(errorMessage); [/code]  semplicissomo e ricalca il metodo della classe statica, ma è tutto fatto con le classi standar del framework. In questo caso però il log non è salvato su file, ma scritto in output della console. Per scrivere il log su file ci sono vari metodi, ma il più semplice e pulito che ho trovato è quello di inserire un file di configurazione(in visual sutudio: File –> add –> new item –> Application Configuration File) con queste previ righe di codice:


 
   
     
                         type="System.Diagnostics.TextWriterTraceListener"
                 initializeData="debug.txt" />
     

   

 

In automatico la stringa errorMessage passata come parametro sarà scritta anche nel file debug.txt(Cartella di progetto /bin / Debug). In questo modo avrai un log file con solo quello che ti interessa e nel formato che preferisci, si può infatti generare anche file XML.

La soluzione che ho sviluppato per l’utilizzo con WCF è ancora un po’ più raffinata. Sfrutta l’interfaccia IErrorHandler contenuta in System.ServiceModel.Dispatcher che espone i metodi:

[code language="csharp"]

public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault);

public bool HandleError(Exception error);

[/code]

Il primo metodo viene chimata all’interno del' thread che solleva l’eccezione. Questo metodo può essere usato per fare una gestione centralizzata delle eccezioni, che sinceramente non mi convince a fondo, ma sicuramente ha dei vantaggi, o comunque può essere utlizzata per catturare le eccezioni non gestite o modificare l’eccezione generata automaticamente. Ogni eccezione invoca automaticamente questo metodo prima di ritornare al chiamante.

Il secondo metodo è invece eseguito da in un thred separato rispetto a quello in cui viene sollevata l’eccezione e può essere usto per gestire il logging dell’applicazione.

Ora il codice completo:

[code language="csharp"]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace WcfSecureService
{

    class SSErrorHandler : Attribute, IErrorHandler, IServiceBehavior
    {
        #region IErrorHandler Members

        public bool HandleError(Exception error)
        {

           
            //Error logging.
            Debug.WriteLine("Eccezione Sollevata:");
            string errorMessage = string.Format("Application:{0},method:{1},StackTrace:{2}",
                error.Source, error.TargetSite.Name, error.StackTrace);

            Debug.WriteLine(errorMessage);
            return true;
        }

        public void ProvideFault(Exception error,
                                          System.ServiceModel.Channels.MessageVersion version,
                                          ref System.ServiceModel.Channels.Message fault)
        {
            // Gestione centralizzata delle eccezioni. Tutte le eccezioni passano di qui e possono essere modificate
            // prima della spedizione.

            //if (error is NullReferenceException) {

            //    // Creates a message fault.
            //    MessageFault messageFault = exception.CreateMessageFault();
            //    fault = Message.CreateMessage(version, messageFault, exception.Action);
            //}
        }

        #endregion

        #region IServiceBehavior Members

        public void AddBindingParameters(ServiceDescription serviceDescription,
                                 ServiceHostBase serviceHostBase,
                                 System.Collections.ObjectModel.Collection endpoints,
                                 BindingParameterCollection bindingParameters)
        {
        }


        public void ApplyDispatchBehavior(ServiceDescription serviceDescription,

                                ServiceHostBase serviceHostBase)
        {
            foreach (var channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(new SSErrorHandler());
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }
}

[/code]

La seconda parte può non essere implementata, sfruttando così le impostazioni di default di WCF.

Ciao,

Matteo

 

by Matteo Valoriani on 12/9/2010