Matteo Valoriani

storie di un folle che non sapeva di esserlo

C# e un log file leggibile!!!

  • Comments 1

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

  using System.Diagnostics;
e ogni volta che si vuole srivere il log:
  Debug.WriteLine(errorMessage);
  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:

<configuration>
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add name="debugListener"
                 type="System.Diagnostics.TextWriterTraceListener"
                 initializeData="debug.txt" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

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:

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

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:

 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<ServiceEndpoint> 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      
    }       
}

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

Ciao,

Matteo

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Post
  • Complimenti.

    Ottimo

    Stavo appunto cercando qualcosa di sensato e usabile per loggare.

    ora provo nella pratica ad usare i tuoi suggerimenti ma mi sembrano molto buoni!

    Grazie

Page 1 of 1 (1 items)