Comment journaliser dans un flux UDP visible du réseau local avec NLog

Pressés? Sautez directement à la solution.

Le contexte

La librairie NLog est très bien documentée sur Internet pour une utilisation régulière. Par exemple, la configuration suivante (fichier NLog.config) permet de journaliser dans un fichier portant la date du jour dans un répertoire logs en mode Trace :

<nlog xmlns= »http://www.nlog-project.org/schemas/NLog.xsd »
      xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance »
      xsi:schemaLocation= »http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd »
autoReload= »true »
throwExceptions= »false »
internalLogLevel= »Off » internalLogFile= »c:\temp\nlog-internal.log »>

  <targets>
<target xsi:type= »File » name= »f » fileName= »${basedir}/logs/${shortdate}.log »
layout= »${longdate} ${uppercase:${level}} ${message} ${when:when=(level = LogLevel.Error):inner=${newline}${exception:format=ToString}} » deleteOldFileOnStartup= »false » />
</targets>
<rules>
<logger name= »* » minlevel= »Trace » writeTo= »f » />
</rules>
</nlog>

Par contre, la journalisation dans un fichier n’est pas toujours pratique. Par exemple, si vous développez une composante serveur, il se peut que les équipes souhaitant consulter le journal n’aient pas un accès physique au répertoire contenant le fichier. Aussi, le compte Windows configuré pour le processus (EXE) qui exécute le code qui journalise doit avoir les droits pour écrire ce fichier.

Je me suis donc mis à chercher une solution qui permettrait de visualiser le journal à distance, à partir d’un poste client, sans passer par un fichier.

La démarche

Je savais que la librairie NLog pouvait être utilisée avec un outil de visualisation de journal tel que Sentinel ou Log2Console. Il suffit d’ajouter la cible suivante et  de l’ajouter dans les règles :

<target xsi:type= »NLogViewer » name= »v » address= »udp://127.0.0.1:9999″/>

<logger name= »* » minlevel= »Trace » writeTo= »v » />

Mais cela fonctionne uniquement lorsque l’outil de visualisation est installé et exécuté localement sur le serveur. Ce n’est pas très avantageux vis-à-vis l’utilisation d’un fichier.

Ma première idée a été d’utiliser une adresse IP multicast (224.0.0.0 à 239.255.255.255) pour le flux UDP. Malheureusement, cela n’a pas fonctionné lors de mes tests. J’ai épuisé toutes mes idées de combinaisons de mots clé à rechercher sur Google pour NLog et multicast et je n’ai rien trouvé.

L’avantage d’un projet à code source libre est que la meilleure documentation reste encore le code en soi. J’ai découvert que les cibles Chainsaw et NLogViewer étaient des spécialisations de la cible Network :

image

Je suis donc allé étudier le code pour voir s’il n’y avait pas une manière de diffuser le flux UDP sur le réseau, plutôt que de toujours fonctionner localement. Lorsque la classe NetworkTarget est configurée avec une adresse UDP (udp://ip:port), sa fabrique de “NetworkSender” crée un UdpNetworkSender. C’est dans cette classe que se cachait le secret pour diffuser le journal sur le réseau local: la méthode CreateSocket contient une condition intéressante:

Uri uri;

if (Uri.TryCreate(this.Address, UriKind.Absolute, out uri) && uri.Host.Equals(IPAddress.Broadcast.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
proxy.UnderlyingSocket.EnableBroadcast = true;
}

La documentation de la propriété EnableBroadcast indique clairement qu’il peut s’agir d’une technique pour diffuser le journal:

Broadcasting is limited to a specific subnet, and must use User Datagram Protocol (UDP.) For Internet Protocol version 4, you can broadcast to your local subnet by sending a packet to 255.255.255.255; or you can use the directed broadcast address, which is the network portion of an Internet Protocol (IP) address with all bits set in the host portion. For example, if your IP address is 192.168.1.40 (a Class C address, with a netmask of 255.255.255.0 — the network portion is the first three octets, and the host portion is the last octet), your directed broadcast address is 192.168.1.255.

Et la condition de la méthode CreateSocket teste si l’adresse de la cible est égale à IPAddress.Broadcast (255.255.255.255) avant de la mettre à true. Bingo!

La solution

La recette est donc la suivante :

<nlog xmlns= »http://www.nlog-project.org/schemas/NLog.xsd »
      xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance »
      xsi:schemaLocation= »http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd »
autoReload= »true »
throwExceptions= »false »
internalLogLevel= »Off » internalLogFile= »c:\temp\nlog-internal.log »>

  <targets>
<target xsi:type= »Chainsaw » name= »c »  address= »udp://255.255.255.255:9998″ />
</targets>
<rules>
<logger name= »* » minlevel= »Trace » writeTo= »c » />
</rules>
</nlog>

Personnellement je trouve que l’outil Log2Console est moins long à mettre en place sur le poste client. Il suffit de configurer un Receiver sur le même port que celui spécifié pour le « target » du programme qui utilise NLog pour journaliser :

clip_image002

image

Par contre, Sentinel offre plus d’options et permet de sauvegarder la configuration dans un fichier pour une utilisation ultérieure. Voici les étapes :

clip_image002[4]

image

image

image

image

image

image

image

Projet de démonstration

J’ai publié un projet de démonstration sur mon GitHub que vous pouvez utiliser pour tester les cibles NLog décrites dans cette entrée de blogue.

image

En cliquant sur le bouton, le journal contiendra une entrée pour chaque niveau de log supporté par NLog. ll y a trois cibles: File, NLogViewer et Chainsaw. La première cible crée un fichier portant la date du jour dans le répertoire logs. La seconde peut être utilisée avec un outil de visualisation de journal, s’exécutant sur le même poste. La dernière cible démontre l’utilisation de la fonctionnalité de Broadcast en UDP et il est alors possible d’exécuter le programme de visualisation de journal sur un autre poste.

<target xsi:type= »File » name= »f » fileName= »${basedir}/logs/${shortdate}.log »
layout= »${longdate} ${uppercase:${level}} ${message} ${when:when=(level = LogLevel.Error):inner=${newline}${exception:format=ToString}} » deleteOldFileOnStartup= »false » />

<target xsi:type= »NLogViewer » name= »v » address= »udp://127.0.0.1:9999″/>

<target xsi:type= »Chainsaw » name= »c »  address= »udp://255.255.255.255:9998″ />

La conclusion

J’aurais préféré trouver une solution utilisant le protocole UDP en mode multicast. Les routeurs sont intelligents et peuvent envoyer les packets uniquement aux ordinateurs “inscrits” pour un groupe multicast (une adresse IP). De plus, l’outil Log2Console a un champ pour inscrire l’adresse de ce groupe dans sa configuration:

image

Malheureusement je n’y suis pas arrivé. Je préfère ne pas avoir à modifier le code source de NLog alors je devrai me contenter de l’approche utilisant le Broadcast.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s