Configurations NLog les plus utiles [fermé]

348

Quelles sont les configurations les meilleures ou les plus utiles pour se connecter avec NLog? (Ceux-ci peuvent être simples ou complexes, tant qu'ils sont utiles.)

Je pense à des exemples comme le survol automatique des fichiers journaux à une certaine taille, la modification de la disposition (message de journal), qu'il y ait ou non une exception, l'augmentation du niveau de journal une fois qu'une erreur s'est produite, etc.

Voici quelques liens:

Tapoter
la source
3
Voici quelques conseils d' optimisation
Neil

Réponses:

391

Certains d'entre eux entrent dans la catégorie des conseils NLog (ou de journalisation) généraux plutôt que des suggestions de configuration strictes.

Voici quelques liens de journalisation généraux d'ici chez SO (vous en avez peut-être déjà vu certains ou tous):

log4net contre Nlog

Journalisation des meilleures pratiques

Quel est l'intérêt d'une façade forestière?

Pourquoi les enregistreurs recommandent-ils d'utiliser un enregistreur par classe?

Utilisez le modèle courant pour nommer votre enregistreur en fonction de la classe Logger logger = LogManager.GetCurrentClassLogger(). Cela vous donne un degré élevé de granularité dans vos enregistreurs et vous donne une grande flexibilité dans la configuration des enregistreurs (contrôle global, par espace de noms, par nom de enregistreur spécifique, etc.).

Utilisez des enregistreurs non basés sur le nom de classe, le cas échéant. Peut-être avez-vous une fonction pour laquelle vous voulez vraiment contrôler la journalisation séparément. Vous avez peut-être des problèmes de journalisation intersectoriels (journalisation des performances).

Si vous n'utilisez pas la journalisation basée sur le nom de classe, pensez à nommer vos enregistreurs dans une sorte de structure hiérarchique (peut-être par domaine fonctionnel) afin de conserver une plus grande flexibilité dans votre configuration. Par exemple, vous pouvez avoir un domaine fonctionnel "base de données", un FA "analyse" et un FA "ui". Chacun de ces éléments peut avoir des sous-zones. Vous pouvez donc demander des enregistreurs comme celui-ci:

Logger logger = LogManager.GetLogger("Database.Connect");
Logger logger = LogManager.GetLogger("Database.Query");
Logger logger = LogManager.GetLogger("Database.SQL");
Logger logger = LogManager.GetLogger("Analysis.Financial");
Logger logger = LogManager.GetLogger("Analysis.Personnel");
Logger logger = LogManager.GetLogger("Analysis.Inventory");

Etc. Avec les enregistreurs hiérarchiques, vous pouvez configurer la journalisation de manière globale (le "*" ou enregistreur racine), par FA (Database, Analysis, UI) ou par sous-zone (Database.Connect, etc.).

Les enregistreurs ont de nombreuses options de configuration:

<logger name="Name.Space.Class1" minlevel="Debug" writeTo="f1" /> 
<logger name="Name.Space.Class1" levels="Debug,Error" writeTo="f1" /> 
<logger name="Name.Space.*" writeTo="f3,f4" />
<logger name="Name.Space.*" minlevel="Debug" maxlevel="Error" final="true" /> 

Consultez l' aide de NLog pour plus d'informations sur la signification exacte de chacune des options. Les éléments les plus notables ici sont probablement la possibilité de joker des règles de journalisation, le concept selon lequel plusieurs règles de journalisation peuvent "s'exécuter" pour une seule instruction de journalisation, et qu'une règle de journalisation peut être marquée comme "finale" afin que les règles suivantes ne s'exécutent pas pour un compte rendu de consignation donné.

Utilisez GlobalDiagnosticContext, MappedDiagnosticContext et NestedDiagnosticContext pour ajouter un contexte supplémentaire à votre sortie.

Utilisez "variable" dans votre fichier de configuration pour simplifier. Par exemple, vous pouvez définir des variables pour vos présentations, puis référencer la variable dans la configuration cible plutôt que de spécifier directement la présentation.

  <variable name="brief" value="${longdate} | ${level} | ${logger} | ${message}"/>
  <variable name="verbose" value="${longdate} | ${machinename} | ${processid} | ${processname} | ${level} | ${logger} | ${message}"/>
  <targets>
    <target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${shortdate}.log" />
    <target name="console" xsi:type="ColoredConsole" layout="${brief}" />
  </targets>

Vous pouvez également créer un ensemble de propriétés "personnalisé" à ajouter à une présentation.

  <variable name="mycontext" value="${gdc:item=appname} , ${mdc:item=threadprop}"/>
  <variable name="fmt1withcontext" value="${longdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>
  <variable name="fmt2withcontext" value="${shortdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>

Ou, vous pouvez faire des choses comme créer des rendus de mise en page "jour" ou "mois" strictement via la configuration:

  <variable name="day" value="${date:format=dddd}"/>
  <variable name="month" value="${date:format=MMMM}"/>
  <variable name="fmt" value="${longdate} | ${level} | ${logger} | ${day} | ${month} | ${message}"/>
  <targets>
    <target name="console" xsi:type="ColoredConsole" layout="${fmt}" />
  </targets>

Vous pouvez également utiliser des rendus de mise en page pour définir votre nom de fichier:

  <variable name="day" value="${date:format=dddd}"/>
  <targets>
    <target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${day}.log" />
  </targets>

Si vous faites rouler votre fichier quotidiennement, chaque fichier pourrait être nommé "Monday.log", "Tuesday.log", etc.

N'ayez pas peur d'écrire votre propre rendu de mise en page. Il est facile et vous permet d'ajouter vos propres informations de contexte au fichier journal via la configuration. Par exemple, voici un rendu de mise en page (basé sur NLog 1.x et non 2.0) qui peut ajouter Trace.CorrelationManager.ActivityId au journal:

  [LayoutRenderer("ActivityId")]
  class ActivityIdLayoutRenderer : LayoutRenderer
  {
    int estimatedSize = Guid.Empty.ToString().Length;

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
      builder.Append(Trace.CorrelationManager.ActivityId);
    }

    protected override int GetEstimatedBufferSize(LogEventInfo logEvent)
    {
      return estimatedSize;
    }
  }

Dites à NLog où vos extensions NLog (quel assemblage) comme ceci:

  <extensions>
    <add assembly="MyNLogExtensions"/>
  </extensions>

Utilisez le rendu de mise en page personnalisé comme ceci:

  <variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>

Utilisez des cibles asynchrones:

<nlog>
  <targets async="true">
    <!-- all targets in this section will automatically be asynchronous -->
  </targets>
</nlog>

Et les wrappers cibles par défaut:

<nlog>  
  <targets>  
    <default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>  
    <target name="f1" xsi:type="File" fileName="f1.txt"/>  
    <target name="f2" xsi:type="File" fileName="f2.txt"/>  
  </targets>  
  <targets>  
    <default-wrapper xsi:type="AsyncWrapper">  
      <wrapper xsi:type="RetryingWrapper"/>  
    </default-wrapper>  
    <target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>  
    <target name="n2" xsi:type="Network" address="tcp://localhost:4002"/>  
    <target name="n3" xsi:type="Network" address="tcp://localhost:4003"/>  
  </targets>  
</nlog>

le cas échéant. Voir les documents NLog pour plus d'informations à ce sujet.

Dites à NLog de regarder et de recharger automatiquement la configuration si elle change:

<nlog autoReload="true" /> 

Il existe plusieurs options de configuration pour faciliter le dépannage de NLog

<nlog throwExceptions="true" />
<nlog internalLogFile="file.txt" />
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" />
<nlog internalLogToConsole="false|true" />
<nlog internalLogToConsoleError="false|true" />

Voir l'aide de NLog pour plus d'informations.

NLog 2.0 ajoute des wrappers LayoutRenderer qui permettent d'effectuer un traitement supplémentaire sur la sortie d'un rendu de mise en page (comme le découpage des espaces blancs, les majuscules, les minuscules, etc.).

N'ayez pas peur d'envelopper l'enregistreur si vous voulez isoler votre code d'une forte dépendance à NLog, mais envelopper correctement. Il y a des exemples de comment encapsuler dans le référentiel github de NLog. Une autre raison pour encapsuler peut être que vous souhaitez ajouter automatiquement des informations de contexte spécifiques à chaque message enregistré (en les plaçant dans LogEventInfo.Context).

Il y a des avantages et des inconvénients à encapsuler (ou à résumer) NLog (ou tout autre cadre de journalisation d'ailleurs). Avec un petit effort, vous pouvez trouver ici de nombreuses informations sur SO présentant les deux côtés.

Si vous envisagez d' habiller , pensez à utiliser Common.Logging . Cela fonctionne assez bien et vous permet de basculer facilement vers un autre cadre de journalisation si vous le souhaitez. De plus, si vous envisagez d'habiller, pensez à la façon dont vous allez gérer les objets de contexte (GDC, MDC, NDC). Common.Logging ne prend actuellement pas en charge une abstraction pour eux, mais il est censé être dans la file d'attente des capacités à ajouter.

salarioghe
la source
3
Très bonne réponse. Juste une chose à ajouter, $ {machine} devrait être $ {machinename}. Voir github.com/nlog/NLog/wiki/Layout-Renderers .
liang
2
J'ai bifurqué Common.Logging et ajouté l'abstraction manquante, voir le projet GitHub ou NuGet .
Danny Varod
J'ai échoué à trouver quelque chose d'aussi informatif sur nlog dans leur propre documentation, peut-être que je regarde les exemples github dans le mauvais sens? Qui sait.
JARRRRG
Comment utiliser ce rendu personnalisé avec l'API (pas de fichier de configuration)? Voici ce que j'essaie d'accomplir.
InteXX
OK, j'ai compris. La NewLinemise en page accomplit la tâche. Voici ce que j'ai trouvé. C'est certainement beaucoup plus simple que ce à quoi je m'attendais.
InteXX
65

Traiter les exceptions différemment

Nous voulons souvent obtenir plus d'informations lorsqu'il y a une exception. La configuration suivante a deux cibles, un fichier et la console, qui filtrent s'il existe ou non des informations d'exception. (EDIT: Jarek a publié une nouvelle méthode pour le faire dans vNext .)

La clé est d'avoir une cible wrapper avec xsi:type="FilteringWrapper" condition="length('${exception}')>0"

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.mono2.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Warn"
      internalLogFile="nlog log.log"
      >
    <variable name="VerboseLayout" 
              value="${longdate} ${level:upperCase=true} ${message}  
                    (${callsite:includSourcePath=true})"            />
    <variable name="ExceptionVerboseLayout"  
              value="${VerboseLayout} (${stacktrace:topFrames=10})  
                     ${exception:format=ToString}"                  />

    <targets async="true">
        <target name="file" xsi:type="File" fileName="log.log"
                layout="${VerboseLayout}">
        </target>

        <target name="fileAsException"  
                xsi:type="FilteringWrapper" 
                condition="length('${exception}')>0">
            <target xsi:type="File"  
                    fileName="log.log"  
                    layout="${ExceptionVerboseLayout}" />
        </target>

        <target xsi:type="ColoredConsole"
                name="console"
                layout="${NormalLayout}"/>

        <target xsi:type="FilteringWrapper"  
                condition="length('${exception}')>0"  
                name="consoleException">
            <target xsi:type="ColoredConsole" 
                    layout="${ExceptionVerboseLayout}" />
        </target>
    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="console,consoleException" />
        <logger name="*" minlevel="Warn" writeTo="file,fileAsException" />
    </rules>

</nlog>
Tapoter
la source
1
C'est plutôt cool avec la cible séparée et FilteringWrapper pour formater l'exception. Je viens de répondre à une question récemment d'un gars qui voulait inclure le rendu de mise en page {exception} dans sa sortie mais il ne voulait pas obtenir le () qui est apparemment enregistré s'il n'y a PAS d'exception. Cette technique fonctionnerait probablement bien pour lui.
employeeoghe
+1 Très sympa. J'ai ce signet depuis longtemps et je me suis référé au "commentaire de Pat" d'une autre question SO concernant une mise en page conditionnelle.
eduncan911
1
Si une exception est enregistrée, elle sera enregistrée deux fois (partie VerboseLayout).
Tien Do
2
Je viens de l'essayer demain dans mon projet, puisque vous définissez une règle minlevel = "Warn" sur "file, fileAsException", tous les journaux seront enregistrés en premier avec la cible du fichier (pas de filtre), et s'il s'agit d'une exception (filtrée par condition), il sera également enregistré avec fileAsException.
Tien Do
3
@Tiendq Oh, je vois. Cela a du sens, même si l'exception elle-même (en détail) ne sera enregistrée qu'une seule fois (mais son message sera enregistré deux fois). Vous pouvez probablement résoudre ce problème en ajoutant condition="length('${exception}')=0(ou peut-être que c'est ==) à target name="file".
Pat
60

Apparemment, vous pouvez maintenant utiliser NLog avec Growl pour Windows .

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <extensions>
        <add assembly="NLog.Targets.GrowlNotify" />
    </extensions>

    <targets>
        <target name="growl" type="GrowlNotify" password="" host="" port="" />
    </targets>

    <rules>
        <logger name="*" minLevel="Trace" appendTo="growl"/>
    </rules>

</nlog>

NLog avec Growl pour Windows Message de trace NLog avec Growl pour Windows Message de débogage NLog avec Growl pour Windows Message d'information NLog avec Growl pour Windows Message d'avertissement NLog avec Growl pour Windows Message d'erreur NLog avec Growl pour Windows Message fatal NLog avec Growl pour Windows

Çağdaş Tekin
la source
pouvez-vous me dire quoi faire pour une connexion distante? la chose fonctionne pour moi pour localhost mais quand j'ai donné une adresse ip dans l'hôte cela ne fonctionne pas !!
Neel
@Neel, vous devez vérifier les paramètres "Sécurité" dans Growl sur l'ordinateur cible. Vous devez activer explicitement les notifications "LAN" et vous voudrez peut-être configurer un mot de passe (que vous devrez ensuite ajouter à votre cible NLog). Mais je n'aimais pas que les notifications à distance apparaissent dans Growl avec une "origine" de "machine locale"; Je devrais ajouter l'hôte aux entrées du journal pour savoir d'où proviennent les notifications.
Kenny Evitt
Je peux faire fonctionner les notifications sur ma machine locale, mais pas à distance. Mes paramètres de sécurité n'ont pas de mot de passe sur grognement, donc tout ce que j'ai ajouté était l'adresse IP et le port. Mais rien n'est envoyé.
Jack Reilly
1
Ce projet est mort à 100%
Développeur
28

Configurer NLog via XML, mais par programme

Quelle? Saviez-vous que vous pouvez spécifier le XML NLog directement à NLog à partir de votre application, au lieu de le faire lire par NLog à partir du fichier de configuration? Bien, vous pouvez. Disons que vous avez une application distribuée et que vous souhaitez utiliser la même configuration partout. Vous pouvez conserver un fichier de configuration dans chaque emplacement et le conserver séparément, vous pouvez en conserver un dans un emplacement central et le pousser vers les emplacements satellites, ou vous pouvez probablement faire beaucoup d'autres choses. Ou, vous pouvez stocker votre XML dans une base de données, l'obtenir au démarrage de l'application et configurer NLog directement avec ce XML (peut-être en revenant périodiquement pour voir s'il a changé).

  string xml = @"<nlog>
                   <targets>
                     <target name='console' type='Console' layout='${message}' />
                   </targets>

                   <rules>
                     <logger name='*' minlevel='Error' writeTo='console' />
                   </rules>
                 </nlog>";

  StringReader sr = new StringReader(xml);
  XmlReader xr = XmlReader.Create(sr);
  XmlLoggingConfiguration config = new XmlLoggingConfiguration(xr, null);
  LogManager.Configuration = config;
  //NLog is now configured just as if the XML above had been in NLog.config or app.config

  logger.Trace("Hello - Trace"); //Won't log
  logger.Debug("Hello - Debug"); //Won't log
  logger.Info("Hello - Info");   //Won't log
  logger.Warn("Hello - Warn");   //Won't log
  logger.Error("Hello - Error"); //Will log
  logger.Fatal("Hello - Fatal"); //Will log

  //Now let's change the config (the root logging level) ...
  string xml2 = @"<nlog>
                  <targets>
                     <target name='console' type='Console' layout='${message}' />
                   </targets>

                   <rules>
                     <logger name='*' minlevel='Trace' writeTo='console' />
                   </rules>
                 </nlog>";

  StringReader sr2 = new StringReader(xml2);
  XmlReader xr2 = XmlReader.Create(sr2);
  XmlLoggingConfiguration config2 = new XmlLoggingConfiguration(xr2, null);
  LogManager.Configuration = config2;

  logger.Trace("Hello - Trace"); //Will log
  logger.Debug("Hello - Debug"); //Will log
  logger.Info("Hello - Info");   //Will log
  logger.Warn("Hello - Warn");   //Will log
  logger.Error("Hello - Error"); //Will log
  logger.Fatal("Hello - Fatal"); //Will log

Je ne sais pas à quel point c'est robuste, mais cet exemple fournit un point de départ utile pour les personnes qui pourraient vouloir essayer de configurer comme ça.

salarioghe
la source
cela fonctionne très bien ... sauf qu'en l'utilisant, il n'est plus possible de reconfigurer dynamiquement le système de journalisation. Cela est particulièrement vrai si vous créez un lien vers un fichier externe (notamment)
Newtopian
2
Cela a fonctionné, même si j'ai dû écrire du "bon" XML en incluant:<?xml version='1.0' encoding='utf-8' ?><nlog xmlns='http://nlog-project.org/schemas/NLog.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
Gady
1
Il s'agit d'un joli segment dans la configuration centralisée. Futurs lecteurs, le code XML codé en dur dans cet exemple est destiné à la démonstration uniquement (à mon humble avis), sa lecture à partir d'une base de données ou d'un fichier centralisé pourrait être la véritable mise en œuvre.
granadaCoder
@wageoghe; Pourquoi j'obtiens une erreur (l'enregistreur n'existe pas)? Je viens de copier et coller le code
Bsflasher
22

Journalisation de différents niveaux selon qu'il y a ou non une erreur

Cet exemple vous permet d'obtenir plus d'informations en cas d'erreur dans votre code. Fondamentalement, il met en mémoire tampon les messages et ne sort que ceux à un certain niveau de journal (par exemple Warn) sauf si une certaine condition est remplie (par exemple, il y a eu une erreur, donc le niveau de journal est> = Error), puis il affichera plus d'informations (par exemple tous les messages des niveaux de journal> = Trace). Parce que les messages sont mis en mémoire tampon, cela vous permet de collecter des informations de trace sur ce qui s'est passé avant qu'une erreur ou une exception d'erreur ne soit enregistrée - très utile!

J'ai adapté celui-ci à partir d' un exemple dans le code source . J'ai été renversé au début parce que j'ai laissé de côté AspNetBufferingWrapper(car la mienne n'est pas une application ASP) - il s'avère que le PostFilteringWrapper nécessite une cible tamponnée. Notez que l' target-refélément utilisé dans l'exemple ci-dessus ne peut pas être utilisé dans NLog 1.0 (j'utilise 1.0 Refresh pour une application .NET 4.0); il est nécessaire de mettre votre cible à l'intérieur du bloc wrapper. Notez également que la syntaxe logique (c.-à-d. Symboles supérieurs ou inférieurs à, <et>) doit utiliser les symboles, et non les échappements XML pour ces symboles (c. &gt;-à- d. Et &lt;) sinon NLog provoquera une erreur.

app.config:

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
    </configSections>

    <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
        <variable name="appTitle" value="My app"/>
        <variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>

        <targets async="true">
            <!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
            <wrapper-target xsi:type="BufferingWrapper" name="smartLog">
                <wrapper-target xsi:type="PostFilteringWrapper">
                    <!--<target-ref name="fileAsCsv"/>-->
                    <target xsi:type="File" fileName="${csvPath}"
                    archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
                    >
                        <layout xsi:type="CsvLayout" delimiter="Tab" withHeader="false">
                            <column name="time" layout="${longdate}" />
                            <column name="level" layout="${level:upperCase=true}"/>
                            <column name="message" layout="${message}" />
                            <column name="callsite" layout="${callsite:includeSourcePath=true}" />
                            <column name="stacktrace" layout="${stacktrace:topFrames=10}" />
                            <column name="exception" layout="${exception:format=ToString}"/>
                            <!--<column name="logger" layout="${logger}"/>-->
                        </layout>
                    </target>

                     <!--during normal execution only log certain messages--> 
                    <defaultFilter>level >= LogLevel.Warn</defaultFilter>

                     <!--if there is at least one error, log everything from trace level--> 
                    <when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
                </wrapper-target>
            </wrapper-target>

        </targets>

        <rules>
            <logger name="*" minlevel="Trace" writeTo="smartLog"/>
        </rules>
    </nlog>
</configuration>
Tapoter
la source
Dans certaines versions de NLog (pour mono et je pense 2.0), cela provoque une StackOverflowException, mais pas dans d'autres (rafraîchissement NLog 1).
Pat
Concernant le débordement - Il semble être dû uniquement à la mise en page de type CSV - si je fais une mise en page régulière il n'y a pas de problème.
Pat
À quoi sert la référence de cible fileAsCsv, là-bas? J'essaie de faire fonctionner cet exemple contre NLog v2.0.0.2000, mais jusqu'à présent, j'échoue.
Peter Mounce
@PeterMounce Le fileAsCsvtarget-ref n'est qu'un artefact de mes tests. Je crois que NLog 2 a / a eu des problèmes avec CsvLayouts que NLog 1 / Refresh n'avait pas.
Pat
22

J'ai fourni quelques réponses raisonnablement intéressantes à cette question:

Nlog - Génération d'une section d'en-tête pour un fichier journal

Ajout d'un en-tête:

La question voulait savoir comment ajouter un en-tête au fichier journal. L'utilisation d'entrées de configuration comme celle-ci vous permet de définir le format d'en-tête séparément du format du reste des entrées de journal. Utilisez un seul enregistreur, peut-être appelé "headerlogger" pour enregistrer un seul message au début de l'application et vous obtenez votre en-tête:

Définissez les dispositions d'en-tête et de fichier:

  <variable name="HeaderLayout" value="This is the header.  Start time = ${longdate} Machine = ${machinename} Product version = ${gdc:item=version}"/>
  <variable name="FileLayout" value="${longdate} | ${logger} | ${level} | ${message}" />

Définissez les cibles à l'aide des dispositions:

<target name="fileHeader" xsi:type="File" fileName="xxx.log" layout="${HeaderLayout}" />
<target name="file" xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />

Définissez les enregistreurs:

<rules>
  <logger name="headerlogger" minlevel="Trace" writeTo="fileHeader" final="true" />
  <logger name="*" minlevel="Trace" writeTo="file" />
</rules>

Écrivez l'en-tête, probablement au début du programme:

  GlobalDiagnosticsContext.Set("version", "01.00.00.25");

  LogManager.GetLogger("headerlogger").Info("It doesn't matter what this is because the header format does not include the message, although it could");

Il s'agit en grande partie d'une autre version de l'idée "Traiter les exceptions différemment".

Enregistrez chaque niveau de journal avec une disposition différente

De même, l'affiche voulait savoir comment changer le format par niveau de journalisation. Il n'était pas clair pour moi quel était l'objectif final (et s'il pouvait être atteint d'une "meilleure" manière), mais j'ai pu fournir une configuration qui a fait ce qu'il a demandé:

  <variable name="TraceLayout" value="This is a TRACE - ${longdate} | ${logger} | ${level} | ${message}"/> 
  <variable name="DebugLayout" value="This is a DEBUG - ${longdate} | ${logger} | ${level} | ${message}"/> 
  <variable name="InfoLayout" value="This is an INFO - ${longdate} | ${logger} | ${level} | ${message}"/> 
  <variable name="WarnLayout" value="This is a WARN - ${longdate} | ${logger} | ${level} | ${message}"/> 
  <variable name="ErrorLayout" value="This is an ERROR - ${longdate} | ${logger} | ${level} | ${message}"/> 
  <variable name="FatalLayout" value="This is a FATAL - ${longdate} | ${logger} | ${level} | ${message}"/> 
  <targets> 
    <target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace"> 
      <target xsi:type="File" fileName="xxx.log" layout="${TraceLayout}" /> 
    </target> 
    <target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug"> 
      <target xsi:type="File" fileName="xxx.log" layout="${DebugLayout}" /> 
    </target> 
    <target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info"> 
      <target xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" /> 
    </target> 
    <target name="fileAsWarn" xsi:type="FilteringWrapper" condition="level==LogLevel.Warn"> 
      <target xsi:type="File" fileName="xxx.log" layout="${WarnLayout}" /> 
    </target> 
    <target name="fileAsError" xsi:type="FilteringWrapper" condition="level==LogLevel.Error"> 
      <target xsi:type="File" fileName="xxx.log" layout="${ErrorLayout}" /> 
    </target> 
    <target name="fileAsFatal" xsi:type="FilteringWrapper" condition="level==LogLevel.Fatal"> 
      <target xsi:type="File" fileName="xxx.log" layout="${FatalLayout}" /> 
    </target> 
  </targets> 


    <rules> 
      <logger name="*" minlevel="Trace" writeTo="fileAsTrace,fileAsDebug,fileAsInfo,fileAsWarn,fileAsError,fileAsFatal" /> 
      <logger name="*" minlevel="Info" writeTo="dbg" /> 
    </rules> 

Encore une fois, très similaire à Traiter les exceptions différemment .

salarioghe
la source
1
Cool! Je n'avais pas vu GlobalDiagnosticsContextavant.
Pat
10

Connectez-vous à Twitter

Basé sur ce post sur un Appender Twitter log4net, J'ai pensé que j'essaierais d'écrire une cible Twitter NLog (en utilisant le rafraîchissement NLog 1.0, pas 2.0). Hélas, jusqu'à présent, je n'ai pas pu obtenir un Tweet pour publier avec succès. Je ne sais pas si c'est quelque chose de mal dans mon code, Twitter, la connexion Internet / pare-feu de notre entreprise, ou quoi. Je poste le code ici au cas où quelqu'un serait intéressé à l'essayer. Notez qu'il existe trois méthodes "Post" différentes. Le premier que j'ai essayé est PostMessageToTwitter. PostMessageToTwitter est essentiellement le même que PostLoggingEvent dans le message d'origine. Si j'utilise cela, j'obtiens une exception 401. PostMessageBasic obtient la même exception. PostMessage s'exécute sans erreur, mais le message ne parvient toujours pas à Twitter. PostMessage et PostMessageBasic sont basés sur des exemples que j'ai trouvés ici sur SO.

FYI - Je viens de trouver un commentaire de @Jason Diller à une réponse dans ce post qui dit que Twitter va désactiver l'authentification de base "le mois prochain". C'était en mai 2010 et c'est maintenant décembre 2010, donc je suppose que cela pourrait être la raison pour laquelle cela ne fonctionne pas.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;

using NLog;
using NLog.Targets;
using NLog.Config;

namespace NLogExtensions
{
  [Target("TwitterTarget")]
  public class TwitterTarget : TargetWithLayout
  {
    private const string REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded";  

    private const string REQUEST_METHOD = "POST";  

    // The source attribute has been removed from the Twitter API,  
    // unless you're using OAuth.  
    // Even if you are using OAuth, there's still an approval process.  
    // Not worth it; "API" will work for now!  
    // private const string TWITTER_SOURCE_NAME = "Log4Net";  
    private const string TWITTER_UPDATE_URL_FORMAT = "http://twitter.com/statuses/update.xml?status={0}";  

    [RequiredParameter]
    public string TwitterUserName { get; set; }

    [RequiredParameter]
    public string TwitterPassword { get; set; }

    protected override void Write(LogEventInfo logEvent)
    {
      if (string.IsNullOrWhiteSpace(TwitterUserName) || string.IsNullOrWhiteSpace(TwitterPassword)) return;

      string msg = this.CompiledLayout.GetFormattedMessage(logEvent);

      if (string.IsNullOrWhiteSpace(msg)) return;

      try
      {
        //PostMessageToTwitter(msg);
        PostMessageBasic(msg);
      }
      catch (Exception ex)
      {
        //Should probably do something here ...
      }
    }

    private void PostMessageBasic(string msg)
    {
      // Create a webclient with the twitter account credentials, which will be used to set the HTTP header for basic authentication 
      WebClient client = new WebClient { Credentials = new NetworkCredential { UserName = TwitterUserName, Password = TwitterPassword } };

      // Don't wait to receive a 100 Continue HTTP response from the server before sending out the message body 
      ServicePointManager.Expect100Continue = false;

      // Construct the message body 
      byte[] messageBody = Encoding.ASCII.GetBytes("status=" + msg);

      // Send the HTTP headers and message body (a.k.a. Post the data) 
      client.UploadData(@"http://twitter.com/statuses/update.xml", messageBody);
    }

    private void PostMessage(string msg)
    {
      string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(TwitterUserName + ":" + TwitterPassword));
      byte [] bytes = System.Text.Encoding.UTF8.GetBytes("status=" + msg.ToTweet());
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
      request.Method = "POST";
      request.ServicePoint.Expect100Continue = false;
      request.Headers.Add("Authorization", "Basic " + user);
      request.ContentType = "application/x-www-form-urlencoded";
      request.ContentLength = bytes.Length;
      Stream reqStream = request.GetRequestStream();
      reqStream.Write(bytes, 0, bytes.Length);
      reqStream.Close();
    }

    private void PostMessageToTwitter(string msg)
    {
      var updateRequest = HttpWebRequest.Create(string.Format(TWITTER_UPDATE_URL_FORMAT,
                                                HttpUtility.UrlEncode(msg.ToTweet()))) as HttpWebRequest;
      updateRequest.ContentLength = 0;
      updateRequest.ContentType = REQUEST_CONTENT_TYPE;
      updateRequest.Credentials = new NetworkCredential(TwitterUserName, TwitterPassword);
      updateRequest.Method = REQUEST_METHOD;

      updateRequest.ServicePoint.Expect100Continue = false;

      var updateResponse = updateRequest.GetResponse() as HttpWebResponse;

      if (updateResponse.StatusCode != HttpStatusCode.OK && updateResponse.StatusCode != HttpStatusCode.Continue)
      {
        throw new Exception(string.Format("An error occurred while invoking the Twitter REST API [Response Code: {0}]", updateResponse.StatusCode));
      }
    }
  }

  public static class Extensions
  {
    public static string ToTweet(this string s)
    {
      if (string.IsNullOrEmpty(s) || s.Length < 140)
      {
        return s;
      }

      return s.Substring(0, 137) + "...";
    }
  }
}

Configurez-le comme ceci:

Dites à NLog l'assembly contenant la cible:

<extensions>
  <add assembly="NLogExtensions"/>
</extensions>

Configurez la cible:

<targets>
    <target name="twitter" type="TwitterTarget" TwitterUserName="yourtwittername" TwitterPassword="yourtwitterpassword" layout="${longdate} ${logger} ${level} ${message}" />
</targets>

Si quelqu'un essaie cela et réussit, postez-le avec vos résultats.

salarioghe
la source
Twitter utilise OAuth - .NET a un fournisseur dans dotnetopenauth.net
Pat
8

Un moyen plus simple de consigner chaque niveau de journal avec une disposition différente à l'aide de dispositions conditionnelles

<variable name="VerboseLayout" value="${level:uppercase=true}: ${longdate} | ${logger}    : 
${when:when=level == LogLevel.Trace:inner=MONITOR_TRACE ${message}} 
${when:when=level == LogLevel.Debug:inner=MONITOR_DEBUG ${message}} 
${when:when=level == LogLevel.Info:inner=MONITOR_INFO ${message}} 
${when:when=level == LogLevel.Warn:inner=MONITOR_WARN ${message}} 
${when:when=level == LogLevel.Error:inner=MONITOR_ERROR ${message}} 
${when:when=level == LogLevel.Fatal:inner=MONITOR_CRITICAL ${message}} |     
${exception:format=tostring} | ${newline} ${newline}" />

Voir https://github.com/NLog/NLog/wiki/When-Filter pour la syntaxe

Lukie
la source
7

Rapports à un site Web / une base de données externe

Je voulais un moyen de signaler simplement et automatiquement les erreurs (car les utilisateurs ne le font souvent pas) de nos applications. La solution la plus simple que j'ai pu trouver était une URL publique - une page Web qui pourrait prendre des entrées et les stocker dans une base de données - qui envoie des données en cas d'erreur d'application. (La base de données pourrait ensuite être vérifiée par un développeur ou un script pour savoir s'il y a de nouvelles erreurs.)

J'ai écrit la page Web en PHP et créé une base de données mysql, un utilisateur et une table pour stocker les données. J'ai choisi quatre variables utilisateur, un identifiant et un horodatage. Les variables possibles (incluses dans l'URL ou en tant que données POST) sont:

  • app (Nom de l'application)
  • msg (message - par exemple, une exception s'est produite ...)
  • dev (développeur - par exemple Pat)
  • src(source - cela proviendrait d'une variable se rapportant à la machine sur laquelle l'application s'exécutait, par exemple Environment.MachineNameou une telle)
  • log (un fichier journal ou un message détaillé)

(Toutes les variables sont facultatives, mais rien n'est signalé si aucune d'entre elles n'est définie - donc si vous visitez simplement l'URL du site Web, rien n'est envoyé à la base de données.)

Pour envoyer les données à l'URL, j'ai utilisé la WebServicecible de NLog . (Remarque, j'ai eu quelques problèmes avec cet objectif au début. Ce n'est que lorsque j'ai regardé la source que j'ai compris que mon urlne pouvait pas se terminer par un /.)

Dans l'ensemble, ce n'est pas un mauvais système pour garder un œil sur les applications externes. (Bien sûr, la chose la plus polie à faire est d' informer vos utilisateurs que vous signalerez des données potentiellement sensibles et de leur donner un moyen d'activer / désactiver.)

Trucs MySQL

(L'utilisateur db n'a que des INSERTprivilèges sur cette seule table dans sa propre base de données.)

CREATE TABLE `reports` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `ts` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `applicationName` text,
  `message` text,
  `developer` text,
  `source` text,
  `logData` longtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='storage place for reports from external applications'

Code du site Web

(PHP 5.3 ou 5.2 avec PDO activé , le fichier est index.phpdans le /reportdossier)

<?php
$app = $_REQUEST['app'];
$msg = $_REQUEST['msg'];
$dev = $_REQUEST['dev'];
$src = $_REQUEST['src'];
$log = $_REQUEST['log'];

$dbData =
    array(  ':app' => $app,
            ':msg' => $msg,
            ':dev' => $dev,
            ':src' => $src,
            ':log' => $log
    );
//print_r($dbData); // For debugging only! This could allow XSS attacks.
if(isEmpty($dbData)) die("No data provided");

try {
$db = new PDO("mysql:host=$host;dbname=reporting", "reporter", $pass, array(
    PDO::ATTR_PERSISTENT => true
));
$s = $db->prepare("INSERT INTO reporting.reports 
    (
    applicationName, 
    message, 
    developer, 
    source, 
    logData
    )
    VALUES
    (
    :app, 
    :msg, 
    :dev, 
    :src, 
    :log
    );"
    );
$s->execute($dbData);
print "Added report to database";
} catch (PDOException $e) {
// Sensitive information can be displayed if this exception isn't handled
//print "Error!: " . $e->getMessage() . "<br/>";
die("PDO error");
}

function isEmpty($array = array()) {
    foreach ($array as $element) {
        if (!empty($element)) {
            return false;
        }
    }
    return true;
}
?>

Code d'application (fichier de configuration NLog)

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
    <variable name="appTitle" value="My External App"/>
    <variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>
    <variable name="developer" value="Pat"/>

    <targets async="true">
        <!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
        <wrapper-target xsi:type="BufferingWrapper" name="smartLog">
            <wrapper-target xsi:type="PostFilteringWrapper">
                <target xsi:type="File" fileName="${csvPath}"
                archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
                >
                    <layout xsi:type="CsvLayout" delimiter="Comma" withHeader="false">
                        <column name="time" layout="${longdate}" />
                        <column name="level" layout="${level:upperCase=true}"/>
                        <column name="message" layout="${message}" />
                        <column name="callsite" layout="${callsite:includeSourcePath=true}" />
                        <column name="stacktrace" layout="${stacktrace:topFrames=10}" />
                        <column name="exception" layout="${exception:format=ToString}"/>
                        <!--<column name="logger" layout="${logger}"/>-->
                    </layout>
                </target>

                 <!--during normal execution only log certain messages--> 
                <defaultFilter>level >= LogLevel.Warn</defaultFilter>

                 <!--if there is at least one error, log everything from trace level--> 
                <when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
            </wrapper-target>
        </wrapper-target>

        <target xsi:type="WebService" name="web"
                url="http://example.com/report" 
                methodName=""
                namespace=""
                protocol="HttpPost"
                >
            <parameter name="app" layout="${appTitle}"/>
            <parameter name="msg" layout="${message}"/>
            <parameter name="dev" layout="${developer}"/>
            <parameter name="src" layout="${environment:variable=UserName} (${windows-identity}) on ${machinename} running os ${environment:variable=OSVersion} with CLR v${environment:variable=Version}"/>
            <parameter name="log" layout="${file-contents:fileName=${csvPath}}"/>
        </target>

    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="smartLog"/>
        <logger name="*" minlevel="Error" writeTo="web"/>
    </rules>
</nlog>

Note: il peut y avoir quelques problèmes avec la taille du fichier journal, mais je n'ai pas trouvé un moyen simple de tronquer (par exemple à la * Nix tailla commande ).

Tapoter
la source
Cela a fonctionné pour un projet, mais dans d'autres, j'ai eu des problèmes avec url: InnerException: System.InvalidCastException Message = Cast non valide de 'System.String' en 'System.Uri'. Source = mscorlib StackTrace: sur System.Convert.DefaultToType (IConvertible value, Type targetType, IFormatProvider provider) sur System.String.System.IConvertible.ToType (Type type, IFormatProvider provider) sur System.Convert.ChangeType (Valeur d'objet, Type conversionType , Fournisseur IFormatProvider)
Pat
Une autre option si vous souhaitez pouvoir surveiller le journal et être averti en cas d'erreur serait une cible Twitter. Voir ce lien pour un Twitter Appender écrit pour log4net: twitterappender.codeplex.com Le blog original en discutant est ici: caseywatson.com/2009/07/07/log4net-twitter-awesome Il devrait être assez facile d'écrire quelque chose de similaire pour NLog.
employeeoghe
J'ai duper avec l'écriture d'un NLog TwitterTarget mais je n'ai pas réussi à obtenir un Tweet publié. J'ai publié le code comme réponse. N'hésitez pas à l'essayer si vous le souhaitez.
employeeoghe
4

Journal de Silverlight

Lorsque vous utilisez NLog avec Silverlight, vous pouvez envoyer la trace côté serveur via le service Web fourni . Vous pouvez également écrire dans un fichier local dans le stockage isolé, ce qui est pratique si le serveur Web n'est pas disponible. Voir ici pour plus de détails, c'est-à-dire utiliser quelque chose comme ça pour vous faire une cible:

namespace NLogTargets
{
    [Target("IsolatedStorageTarget")]
    public sealed class IsolatedStorageTarget : TargetWithLayout
    {
        IsolatedStorageFile _storageFile = null;
        string _fileName = "Nlog.log"; // Default. Configurable through the 'filename' attribute in nlog.config

        public IsolatedStorageTarget()
        {
        }

        ~IsolatedStorageTarget()
        {
            if (_storageFile != null)
            {
                _storageFile.Dispose();
                _storageFile = null;
            }
        }

        public string filename
        {
            set
            {
                _fileName = value; 
            }
            get
            {
                return _fileName;  
            }
         }

        protected override void Write(LogEventInfo logEvent)
        {
            try
            {
                writeToIsolatedStorage(this.Layout.Render(logEvent));
            }
            catch (Exception e)
            {
                // Not much to do about his....
            }
        }

        public void writeToIsolatedStorage(string msg)
        {
            if (_storageFile == null)
                _storageFile = IsolatedStorageFile.GetUserStoreForApplication();
            using (IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                // The isolated storage is limited in size. So, when approaching the limit
                // simply purge the log file. (Yeah yeah, the file should be circular, I know...)
                if (_storageFile.AvailableFreeSpace < msg.Length * 100)
                {
                    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Truncate, FileAccess.Write, isolatedStorage))
                    { }
                }
                // Write to isolated storage
                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Append, FileAccess.Write, isolatedStorage))
                {
                    using (TextWriter writer = new StreamWriter(stream))
                    {
                        writer.WriteLine(msg);
                    }
                }
            }
        }
    } 
}
BaBu
la source