Json.NET : Configurer ITraceWriter sur JsonMediaTypeFormatter

La dernière version de Json.NET (version 4.5.11 publiée sur Nuget en novembre 2012) permet de fournir une implémentation de ITraceWriter pour obtenir les traces générées lors des opérations de sérialisation.


Son créateur propose sur son blog un article avec un exemple d’utilisation que je reproduis ici:


Staff staff = new Staff();
staff.Name = "Arnie Admin";
staff.Roles = new List { "Administrator" };
staff.StartDate = DateTime.Now;
 
ITraceWriter traceWriter = new MemoryTraceWriter();
 
JsonConvert.SerializeObject(
  staff,
  new JsonSerializerSettings { TraceWriter = traceWriter, Converters = { new JavaScriptDateTimeConverter() } });
 
Console.WriteLine(traceWriter);
/*
2012-11-11T12:08:42.761 Info Started serializing Newtonsoft.Json.Tests.Serialization.Staff. Path ''.
2012-11-11T12:08:42.785 Info Started serializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path 'StartDate'.
2012-11-11T12:08:42.791 Info Finished serializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path 'StartDate'.
2012-11-11T12:08:42.797 Info Started serializing System.Collections.Generic.List`1[System.String]. Path 'Roles'.
2012-11-11T12:08:42.798 Info Finished serializing System.Collections.Generic.List`1[System.String]. Path 'Roles'.
2012-11-11T12:08:42.799 Info Finished serializing Newtonsoft.Json.Tests.Serialization.Staff. Path ''.
*/

Contexte WebApi

Par défaut, les contrôleurs utiliseront la classe JsonMediaTypeFormatter pour retourner une réponse au format JSON. Et JsonMediaTypeFormatter utilise par défaut Json.NET pour la sérialisation de la réponse.

Pour définir le ITraceWriter à utiliser, il suffit donc de modifier la propriété SerializerSettings du serializer au démarrage de l’application (global.asax.cs):


protected void Application_Start()
{
    // [...] 
    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    var jsonTracer = (Newtonsoft.Json.Serialization.ITraceWriter)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(Newtonsoft.Json.Serialization.ITraceWriter));
    json.SerializerSettings.TraceWriter = jsonTracer;
}

Le code ci-dessus suppose que le ITraceWriter utilisé est résolu par le conteneur IoC.

Si la configuration par défaut n’est pas modifiée (typiquement si JsonMediaTypeFormatter est bien utilisé et que sa propriété UseDataContractJsonSerializer est désactivée), les opérations de sérialisation devraient générer des traces. Noter que lors de mes tests, la sérialisation d’un type primitif (comme string) ne générait aucune trace. Cela fonctionne bien avec un objet complexe comme dans le premier exemple.

Exemple d’implémentation de ITraceWriter avec NLog

Jusqu’à présent, j’utilisais déjà un wrapper autour de NLog pour implémenter différents traceurs dont System.Web.Http.Tracing.ITraceWriter. L’implémentation de cette nouvelle interface était donc très simple dans mon cas. Voici le code partiel:


public class NLogLogger 
    : 
    System.Web.Http.Tracing.ITraceWriter, 
    Newtonsoft.Json.Serialization.ITraceWriter
{
 private readonly Logger _logger;

 // [...]
 
 #region Newtonsoft.Json.Serialization.ITraceWriter
 public System.Diagnostics.TraceLevel LevelFilter
 {
     get { return System.Diagnostics.TraceLevel.Verbose; }
 }
 
 public void Trace(System.Diagnostics.TraceLevel level, string message, Exception ex)
 {
     const string exceptionMessageFormat = "{0}\r\n{1}";
     switch (level)
     {
         case System.Diagnostics.TraceLevel.Off:
             break;
         case System.Diagnostics.TraceLevel.Info:
             if (ex != null)
                 _logger.Info(exceptionMessageFormat, message, ex);
             else
                 _logger.Info(message);
             break;
         case System.Diagnostics.TraceLevel.Warning:
             if (ex != null)
                 _logger.Warn(exceptionMessageFormat, message, ex);
             else
                 _logger.Warn(message);
             break;
         case System.Diagnostics.TraceLevel.Error:
             if (ex != null)
                 _logger.Error(exceptionMessageFormat, message, ex);
             else
                 _logger.Error(message);
             break;
         case System.Diagnostics.TraceLevel.Verbose:
         default:
             if (ex != null)
                 _logger.Debug(exceptionMessageFormat, message, ex);
             else
                 _logger.Debug(message);
             break;
     }
 }
 #endregion
}