Log4Net ed un buon logging
Il logging è una parte fondamentale dell’applicazione; se implementato correttamente ed inserito nei posti giusti può salvare da lunghi down applicativi e molto altro ancora.
Pur potendo una persona essere bravissima a scrivere del codice, purtroppo (o per fortuna, dipende dai punti di vista) la stabilità e le problematiche dell’applicazione non dipendono soltanto dallo sviluppatore. Di fatto può cadere la connessione verso il database, possono esserci problemi di rete verso la SAN, un update può destabilizzare l’applicativo, ma per l’utente finale la colpa sarà sempre dell’applicazione; del resto è un punto di vista comprensibile, in quanto ciò che interessa è usare l’applicazione e non le eventuali problematiche esterne che ne possono causare un crash.
Appreso il fatto che, qualsiasi sia il problema, sta a noi trovarlo, passando poi la palla a chi di dovere (sistemista, tecnico di rete, etc), è comunque utile sapere che proprio in questi casi il logging può aiutare a ridurre al minimo il tempo necessario per poter identificare un bug applicativo e/o un problema di infrastruttura.
Purtroppo io sono abituato ad aggiungere al Dictionary Data delle eccezioni alcune informazioni riguardo al contenuto della chiamata, come dei parametri o delle variabili calcolate (vedi qui); inoltre, per tutte le applicazioni web, mi piace essere a conoscenza dell’esatta richiesta che è stata effettuata, quindi, per entrare un po’ più nel dettaglio, per ogni errore voglio sapere:
- Url della richiesta;
- RawURL nel caso esista un sistema di routing o rewriting;
- UrlReferrer, se esiste;
- UserAgent, se esiste;
- UserHostName;
- Tutte le ServerVariables;
- Tutti i valori provenienti dalla Form;
- ServerName, nel caso mi trovi in un sistema di load balancing;
- ThreadLanguage;
Ovviamente tutte queste informazioni sono prensenti nel HttpContext, ma inserirle a mano ogni volta non è il massimo della comodità, così come non lo è crearsi una classe di helper perchè spesso il Logger può essere instanziato tramite un Framework di Inversion Of Control come Castle (che offre già una serie di facilities a riguardo). Per concludere, il Logger deve aggiungere da solo tutto ciò che è ripetitivo.
Purtroppo il Logger di default non salva queste informazioni (né le informazioni aggiuntive della collection data, né il contesto web), ma è comunque possibile realizzare un layout base che aggiunga all’output del Logger le informazioni necessarie.
Personalmente utilizzo come output un file xml (il discorso rimane lo stesso anche se si opta per un’altra soluzione) perchè è compatibile con il mio log viewer preferito, Log4View, che offre moltissime opzioni, tra cui la possibilità di effettuare logging tramite nettcp su un client remoto.
Chi segue il codice di Dexter può già trovare questa implementazione - che qui riporto solo per le parti relative all’argomento - al suo interno:
protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
{
if (DexterEnvironment.Context.IsSafe)
{
try
{
loggingEvent.Properties["Url"] = DexterEnvironment.Context.CurrentRequestUri;
loggingEvent.Properties["UrlReferrer"] = DexterEnvironment.Context.CurrentUrlReferrer;
loggingEvent.Properties["UserAgent"] = DexterEnvironment.Context.CurrentUserAgent;
loggingEvent.Properties["UserHostName"] = DexterEnvironment.Context.CurrentUserHostName;
loggingEvent.Properties["ServerVariables"] = ServerVariables(DexterEnvironment.Context.CurrentServerVariables);
loggingEvent.Properties["Form"] = ServerVariables(DexterEnvironment.Context.CurrentForm);
loggingEvent.Properties["RawURL"] = DexterEnvironment.Context.CurrentRawUrl;//ctx.Request.RawUrl;
loggingEvent.Properties["ServerName"] = DexterEnvironment.Context.CurrentServerMachineName;
loggingEvent.Properties["ThreadLanguage"] = Thread.CurrentThread.CurrentCulture.DisplayName;
}
catch
{
//needed because the logging called from the appliction start che throw a new exception
//more info here:http://mvolo.com/blogs/serverside/archive/2007/11/10/Integrated-mode-Request-is-not-available-in-this-context-in-Application_5F00_Start.aspx
}
}
writer.WriteStartElement(mElmEvent);
writer.WriteAttributeString("logger", loggingEvent.LoggerName);
writer.WriteAttributeString("timestamp", XmlConvert.ToString(loggingEvent.TimeStamp, XmlDateTimeSerializationMode.Local));
writer.WriteAttributeString("level", loggingEvent.Level.DisplayName);
writer.WriteAttributeString("thread", loggingEvent.ThreadName);
if (!string.IsNullOrEmpty(loggingEvent.Domain))
writer.WriteAttributeString("domain", loggingEvent.Domain);
if (!string.IsNullOrEmpty(loggingEvent.Identity))
writer.WriteAttributeString("identity", loggingEvent.Identity);
if (!string.IsNullOrEmpty(loggingEvent.UserName))
writer.WriteAttributeString("username", loggingEvent.UserName);
writer.WriteStartElement(mElmMessage);
if (!Base64EncodeMessage)
Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage, InvalidCharReplacement);
else
{
byte[] bytes = Encoding.UTF8.GetBytes(loggingEvent.RenderedMessage);
string textData = Convert.ToBase64String(bytes, 0, bytes.Length);
Transform.WriteEscapedXmlString(writer, textData, InvalidCharReplacement);
}
writer.WriteEndElement();
PropertiesDictionary properties = loggingEvent.GetProperties();
if (properties.Count > 0)
{
writer.WriteStartElement(mElmProperties);
foreach (DictionaryEntry entry in properties)
{
writer.WriteStartElement(mElmData);
writer.WriteAttributeString("name", Transform.MaskXmlInvalidCharacters((string) entry.Key, InvalidCharReplacement));
string str2;
if (!Base64EncodeProperties)
str2 = Transform.MaskXmlInvalidCharacters(loggingEvent.Repository.RendererMap.FindAndRender(entry.Value), InvalidCharReplacement);
else
{
byte[] inArray = Encoding.UTF8.GetBytes(loggingEvent.Repository.RendererMap.FindAndRender(entry.Value));
str2 = Convert.ToBase64String(inArray, 0, inArray.Length);
}
writer.WriteAttributeString("value", str2);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
if (loggingEvent.ExceptionObject != null)
{
StringBuilder exceptionString = new StringBuilder();
exceptionString.Append(loggingEvent.ExceptionObject);
exceptionString.Append(Environment.NewLine);
foreach (DictionaryEntry de in loggingEvent.ExceptionObject.Data)
{
exceptionString.Append(de.Key.ToString());
exceptionString.Append(": ");
exceptionString.Append(de.Value.ToString());
exceptionString.Append(Environment.NewLine);
}
if ((exceptionString.Length > 0))
{
writer.WriteStartElement(mElmException);
Transform.WriteEscapedXmlString(writer, exceptionString.ToString(), InvalidCharReplacement);
writer.WriteEndElement();
}
}
if (LocationInfo)
{
LocationInfo locationInformation = loggingEvent.LocationInformation;
writer.WriteStartElement(mElmLocation);
writer.WriteAttributeString("class", locationInformation.ClassName);
writer.WriteAttributeString("method", locationInformation.MethodName);
writer.WriteAttributeString("file", locationInformation.FileName);
writer.WriteAttributeString("line", locationInformation.LineNumber);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
Come potete vedere tutto si svolge nel metodo FormatXml, dove è possibile aggiungere, rimuovere e leggere alcune informazioni. Nella prima parte vado ad inserire le informazioni inerenti il contesto web, quindi la url richiesta, il rawurl, useragent, etc, mentre più avanti renderizzo tutte le informazioni del Dictionary Data dell’eccezione.
Per poter utilizzare questa customizzazione è sufficiente specificare nel file di log4net la classe per il rendering delle informazioni, come mostrato di seguito:
<appender name="NHibernateFileLog" type="log4net.Appender.RollingFileAppender">
<file value="Logs/nHibernate.txt" />
<maximumFileSize value="1000KB" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<layout type="Dexter.Shared.Log.Log4Net.XmlLayout">
</layout>
</appender>
Chi è interessato all’implementazione completa del XmlLayout può guardare qui.
Ciauz
Autoconfigurare Windows Live Writer
Quando il tempo necessario richiesto a fornire una determinata informazione eguaglia il tempo necessario allo sviluppo di un sistema di risposta automatico, forse è il caso di fare qualcosa :).
Da tale situazione è nata l’esigenza di creare un sistema che configurasse in automatico Windows Live Writer.
In quest’ ultimo periodo, insieme agli altri ragazzi, abbiamo rilasciato diverse versioni di Dexter ed abbiamo lavorato in particolar modo con l’integrazione su Windows Live Writer. Questo ha causato diversi reset della configurazione di WLW da parte di tutti gli utilizzatori (in realtà ora sono solo i beta tester) che, giustamente, mi chiedevano quale API Dexter implementasse e quale fosse la sua url.
Da qui è nata la decisione di creare un qualcosa che fornisse automaticamente a WLW le informazioni necessarie per autoconfigurarsi.
Grazie ad una dritta del buon Alessandro ho scoperto che è possibile fare ciò tramite un file RSD (Really Simple Discoverability 1.0), che non fa altro che esporre le informazioni necessarie dei providers implementati tramite una struttura xml.
La struttura seguente mostra il file del mio blog:
<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
<service>
<engineName>Dexter Blog Engine</engineName>
<engineLink>http://dexterblogengine.codeplex.com/</engineLink>
<homePageLink>http://tostring.it/</homePageLink>
<apis>
<api name="MetaWeblog" blogID="1" preferred="false" apiLink="http://tostring.it/metaweblog.axd" />
</apis>
</service>
</rsd>
Come potete vedere è tutto piuttosto semplice, l’unica nota (oltre al dexter blog engine :P) è il nodo APIs, dove è possibile specificare le varie API esposte dal blog engine (nel mio caso solo i metaweblog API).
Nella home page del sito basta inserire il seguente tag ed il gioco è fatto:
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://tostring.it/MetaWeblogRsd.axd" />
Per quanto riguarda WLW, basta configurarlo specificando la Home Page: lui effettuerà il parser del markup in ricerca dell’apposito tag e si autoconfigurerà.
Di Seguito alcuni screenshots sulla procedura di configurazione
Enjoy your favorite blog engine (dexter)
Ciauz
Verificare se l’applicazione Silverlight in esecuzione è in FullTrust
Posted by imperugo in Silverlight on Tuesday 02 March 2010 at 9:00 AM
Silverlight 4 ha introdotto la possibilità di eseguire operazioni in OutOfBrowser anche in FullTrustMode. Inutile dire che questa è una delle features più interessanti della prossima release di SIlverlight, in quanto ci permette di poter accedere a risorse precedentemente non accessibili come Fotocamere, Hard Drives, Office Documents, etc.
Ovviamente però, oltre a specificare che l’applicazione richiede permessi elevati, è necessario che l’utente autorizzi l’applicazione ad eseguire operazioni anche fuori dalla propria sandbox. Dato che non c’è certezza che l’utente approvi o no tali operazioni è sicuaramente utile capire da codice se ciò è avvenuto e, nel caso, cambiare il comportamento dell’applicazione e/o gestirne eventuali errori.
Come prima cosa è necessario specificare che l’applicazione richiede permessi elevati; questo è possibile dalla finestra delle proprietà del progetto, come mostrato dallo screenshot seguente:
Per la parte codice ci basta verificare tramite l’apposità proprietà se l’utente ha acconsentito il FullTrust mode, come mostrato nello snippet seguente:
if(!App.Current.HasElevatedPermissions)
MessageBox.Show("You have to accept the Full Trust Mode!");
Ciauz
Installare un’applicazione Silverlight sul client via C#
Posted by imperugo in Silverlight on Monday 01 March 2010 at 9:00 AM
Nel post precedente avevo mostrato come verificare se l’applicaizone Silverlight fosse installata o meno sul client. L’idea di questo post è di mostrare come installare l’applicazione tramite un pulsante custom presente all’interno della pagina ed eseguire la procedura di installazione via C#.
Lo snippet seguente mostra l’evento OnClick del button per l’installazione:
private void InstallButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (!App.Current.IsRunningOutOfBrowser && App.Current.InstallState == InstallState.NotInstalled)
{
App.Current.Install();
}
}
Come avrete potuto notare, fin’ora tutte le informazioni necessarie per poter lavorare sono state esposte tramite la classe System.Windows.Application; grazie a quest’ultima, nei prossimi post si vedrà come verificare se l’applicazione è in esecuzione in fulltrust mode e, nel caso ne esista una più recente, come aggiornarla.
Ciauz
Ma dove sono finito? al Summit :D
Avevo annunciato una serie di posts che affrontavano un po’ la vita OutOfBrowser di un’applicazione sviluppata con Silverlight 4 ma poi, proprio quando la cosa si faceva più interessante, sono sparito.
Per vari motivi, e devo dire alcuni anche piacevoli, ho dovuto trascurare il blog. Un po’ perchè da questa settimana non sono più in MTV (dove ho passato più di tre anni bellissimi) e mi trovo a lavorare con quel “matto” di Mauro in Gaia, ed un po’ perchè sono stato all’MVP Summit in Microsoft, a Seattle.
Devo dire che quest’ultima è stata un’esperienza stupenda: girare per il Campus, conoscere Scott Guthrie, parlare via Twitter con Scott Hanselman mentre tiene una sessione…è stato emozionante. In più ho avuto l’occasione di conoscere di persona alcuni italiani che lavorano al campus, nello specifico Vittorio, Enzo (aka paperino) e Giorgio, che mi hanno offerto anche il pranzo e sono stati gentilissimi.
Putroppo non posso raccontare molto del Summit (è tutto sotto NDA), ma posso solo dire che l’esperienza è stata sopra le aspettative e che tutti gli Italians Boys (Raf, Alessandro, Alead, Janky, Davide Mauri, Andrea, Davide Zordan, Giorgio, Leone, Valter ed il mio super roommate Gian Maria) sono stati mitici.
Sotto Alcune foto.
Stay tuned!
Recent Comments