Configuration NLog la plus utile [fermé]

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

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

voici quelques liens:

  • NLog Démo
  • exemples dans la source
329
demandé sur Taryn 2010-11-03 23:56:58

10 réponses

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

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

log4net vs. Nlog

"1519230920 Exploitation" meilleures pratiques

Quel est le but d'une exploitation forestière? façade?

pourquoi les bûcherons recommandent-ils l'utilisation d'un bûcheron par classe?

utilisez le modèle commun de nommer votre logger basé sur la classe Logger logger = LogManager.GetCurrentClassLogger() . Cela vous donne un haut degré de granularité dans vos loggers et vous donne une grande flexibilité dans la configuration des loggers (contrôle global, par namespace, par nom de logger spécifique, etc).

utiliser des loggers sans nom de classe où approprié. Peut-être que vous avez une fonction pour laquelle vous voulez vraiment pour contrôler l'enregistrement séparément. Peut-être avez-vous des préoccupations transversales en matière de journalisation (performance logging).

si vous n'utilisez pas la journalisation basée sur classname, pensez à nommer vos loggers dans une sorte de structure hiérarchique (peut-être par zone fonctionnelle) afin que vous puissiez maintenir une plus grande flexibilité dans votre configuration. Par exemple, vous pourriez avoir un domaine fonctionnel "base de données", une fonction" analyse " et une fonction "l'interface utilisateur" FA. Chacun d'eux pourrait comporter des sous-secteurs. Ainsi, vous pouvez demander des loggers comme ceci:

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");

et ainsi de suite. Avec les loggers hiérarchiques, vous pouvez configurer la journalisation globalement (le logger "*" ou root), par FA (base de données, Analyse, UI), ou par sous-zone (base de données.Se connecter, etc).

les Loggers 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" /> 

voir le NLog help pour plus d'informations sur exactement ce que chaque des options signifie. Probablement les éléments les plus notables ici sont la possibilité de Joker les règles logger, le concept que les règles logger multiples peuvent "exécuter" pour une seule instruction de journalisation, et qu'une règle logger peut être marquée comme "finale" de sorte que les règles suivantes ne s'exécuteront pas pour une instruction de journalisation donnée.

utilisez le Contextediagnostique Global, Le Contextediagnostique mapped et le Contextediagnostique nested pour ajouter du contexte à votre sortie.

Use "variable" dans votre fichier de configuration pour simplifier. Par exemple, vous pouvez définir des variables pour vos layouts et ensuite référencer la variable dans la configuration cible plutôt que de spécifier directement layout.

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

ou, vous pouvez créer un ensemble" personnalisé " de propriétés à ajouter à un layout.

  <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 layout renders 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 déroulez votre fichier quotidiennement, chaque fichier pourrait être appelé "lundi".journal", "mardi.journal", etc.

n'ayez pas peur d'écrire votre propre Rendu. Il est facile et vous permet d'ajouter vos propres informations de contexte au fichier log via la configuration. Par exemple, voici un rendu de mise en page (basé sur NLog 1.x, Pas 2.0) qui peut ajouter le Trace.CorrelationManager.ActivityId dans le 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}"/>

utiliser des cibles async:

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

Et la cible par défaut wrappers:

<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 docs NLog pour plus d'informations sur ceux-ci.

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

<nlog autoReload="true" /> 

il y a plusieurs options de configuration pour aider à résoudre 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 NLog Aide 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 rogner l'espace blanc, majuscules, minuscules, etc.).

ne pas avoir peur d'envelopper l'enregistreur si vous voulez isoler votre code d'une dépendance dure sur NLog, mais envelopper correctement. Il y a des exemples de la façon d'envelopper dans le dépôt Github de NLog. Une autre raison d'envelopper pourrait être que vous voulez automatiquement ajouter des informations de contexte spécifiques à chaque message enregistré (en le mettant dans LogEventInfo.Cadre.)

il y a des avantages et des inconvénients à envelopper (ou à abstraire) NLog (ou tout autre cadre de journalisation pour cette question). Avec un peu d'effort, vous pouvez trouver beaucoup d'informations ici de sorte à présenter les deux côtés.

si vous envisagez d'emballer, envisagez d'utiliser commun.Logging . Il fonctionne très bien et permet de changer facilement de journalisation si vous le désir de le faire. Aussi si vous envisagez d'emballer, pensez à la façon dont vous allez gérer les objets contextuels (GDC, MDC, NDC). Commun.La journalisation ne supporte pas actuellement une abstraction pour eux, mais il est supposé être dans la file d'attente des capacités à ajouter.

373
répondu wageoghe 2017-12-13 06:36:06

traiter les exceptions différemment

nous voulons souvent obtenir plus d'information lorsqu'il y a une exception. La configuration suivante a deux cibles, un fichier et la console, qui filtrent selon qu'il y a ou non des informations d'exception. (EDIT: Jarek a posté à propos d'une nouvelle méthode de faire cela dans vNext .)

la clé est d'avoir une cible enveloppante 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>
61
répondu Pat 2014-06-09 23:28:32

apparemment, vous pouvez maintenant utiliser NLog avec grognement 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 with Growl for Windows NLog trace message with Growl for Windows NLog debug message with Growl for Windows NLog info message with Growl for Windows NLog warn message with Growl for Windows NLog error message with Growl for Windows NLog fatal message with Growl for Windows

58
répondu Çağdaş Tekin 2011-06-29 03:42:26

configurer NLog via XML, mais Programmatiquement

quoi? Saviez-vous que vous pouvez spécifier le XML NLog directement à NLog à partir de votre application, au lieu que NLog le lise à partir du fichier de configuration? Eh bien, vous le pouvez. Disons que vous avez une application distribuée et que vous voulez utiliser la même configuration de partout. Vous pouvez garder un fichier de configuration dans chaque emplacement et le maintenir séparément, vous pouvez en maintenir un dans un emplacement central et pousser à l'emplacement des satellites, ou vous pourriez 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 vérifier périodiquement pour voir si elle avait 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 suis pas sûr de la robustesse de cet exemple, mais cet exemple fournit un point de départ utile pour les personnes qui pourraient vouloir essayer de configurer ainsi.

27
répondu wageoghe 2010-11-23 19:44:38

l'enregistrement des niveaux différents selon qu'il existe ou non une erreur

cet exemple vous permet d'obtenir plus de renseignements lorsqu'il y a une erreur dans votre code. Fondamentalement, il amortit les messages et ne produit ceux à un certain niveau de log (par exemple Warn) sauf si une certaine condition est remplie (par exemple il y a eu une erreur, donc le niveau de log est >= erreur), alors il produira plus d'informations (par exemple tous les messages des niveaux de log >= Trace). Parce que le les messages sont tamponnés, cela vous permet de recueillir des informations de trace sur ce qui s'est passé avant une erreur ou ErrorException a été enregistrée - très utile!

j'ai adapté celui-ci de un exemple dans le code source . J'ai été éjecté en premier parce que j'ai laissé de côté le AspNetBufferingWrapper (puisque le mien n'est pas une application ASP) - il s'avère que le PostFilteringWrapper nécessite une cible tamponnée. Notez que le target-ref l'é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 placer votre cible dans le bloc d'enrubannage. Notez également que la syntaxe logique (c.-à-d. plus grand ou moins grand que les symboles, < et >) doit utiliser les symboles, pas le XML échappe pour ces symboles (c.-à-d. &gt; et &lt; ) sinon NLog fera 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>
22
répondu Pat 2010-11-19 15:53:40

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

Nlog Génératrices de Section d'en-Tête d'un fichier journal

Ajouter un en-tête:

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

définit l'en-tête et la disposition des fichiers:

  <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éfinir les cibles en utilisant les dispositions:

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

définir les loggers:

<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");

c'est en grande partie juste une autre version de l'idée de" traiter les exceptions différemment".

Journal de chaque niveau de journal avec une mise en page différente

de même, l'affiche voulait savoir comment modifier le format par niveau de journalisation. Je n'ai pas compris quel était le but final (et s'il pouvait être atteint d'une "meilleure" façon), 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 .

21
répondu wageoghe 2017-05-23 10:31:18

Se connecter à Twitter

Basé sur ce post sur un log4net Twitter Appender , j'ai pensé que je voudrais m'essayer à l'écriture d'un NLog Twitter Cible (à l'aide de NLog 1.0 actualisation, pas 2.0). Hélas, jusqu'à présent, je n'ai pas été en mesure d'obtenir un Tweet pour effectivement poster avec succès. Je ne sais pas si c'est quelque chose qui ne va pas dans mon code, Twitter, notre connexion internet/pare-feu, ou quoi. Je poste le code ici au cas où quelqu'un est intéressé à essayer. Notez qu'il existe trois différents "Post". Le premier que j'ai essayé est Postmessagetwitter. PostMessageToTwitter est essentiellement le même que PostLoggingEvent dans le post original. Si j'utilise ça, j'ai droit à une exception. PostMessageBasic obtient la même exception. PostMessage fonctionne sans erreur, mais le message ne se fait toujours pas à Twitter. PostMessage et PostMessageBasic sont basés sur des exemples que j'ai trouvé ici sur SO.

pour information - 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 il est maintenant en décembre 2010, donc je suppose que peut-être pourquoi 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) + "...";
    }
  }
}

configurer comme ceci:

indiquer à NLog l'assemblage contenant la cible:

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

configurer la cible:

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

Si quelqu'un essaie cela et a du succès, post de retour avec vos conclusions.

10
répondu wageoghe 2017-05-23 12:34:58

de l'information vers un site externe/base de données

je voulais un moyen simple et automatique de signaler des erreurs (puisque les utilisateurs ne le font pas souvent) à partir de nos applications. La solution la plus facile que j'ai pu trouver était une URL publique - une page web qui pourrait prendre entrée et la stocker dans une base de données - qui est envoyé des données en cas d'erreur d'application. (La base de données peut alors être vérifiée par un dev ou un script pour savoir s'il y a de nouvelles erreurs.)

j'ai écrit le web page en PHP et a créé une base de données mysql, l'utilisateur, et la table pour stocker les données. J'ai choisi quatre variables d'utilisateur, un id, et un timestamp. Les variables possibles (incluses dans L'URL ou comme données POST) sont:

  • app (nom de la demande)
  • msg (message - p. ex. Exception survenue ...)
  • dev (développeur - p.ex. Pat)
  • src (source: d'une variable relative à la machine sur laquelle l'app fonctionnait, p.ex. Environment.MachineName ou une telle)
  • log (un fichier journal ou un message verbeux)

(toutes les variables sont optionnelles, 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é WebService target de NLog . (Remarque, j'ai eu quelques problèmes avec cet objectif au premier abord. Ce n'est qu'en regardant la source que j'ai compris que mon url ne pouvait pas se terminer avec 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 polie à faire est de informer vos utilisateurs que vous allez déclarer des données éventuellement sensibles et de leur donner un moyen d'entrer/sortir.)

MySQL stuff

(l'utilisateur db n'a que les privilèges INSERT 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 AOP activé , le fichier est index.php dans /report dossier)

<?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;
}
?>

le code de l'Application (NLog fichier de config)

<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 des problèmes avec le Taille du fichier journal, mais je n'ai pas trouvé un moyen simple de le tronquer (par exemple tail commande d'un la *nix ).

7
répondu Pat 2017-05-23 10:31:18

moyen plus facile de loguer chaque niveau logarithmique avec une disposition différente en utilisant des 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 syntaxe

4
répondu Lukie 2018-07-06 12:34:42

Journal de Silverlight

lorsque vous utilisez NLog avec Silverlight vous pouvez envoyer la trace du côté du 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);
                    }
                }
            }
        }
    } 
}
3
répondu BaBu 2017-05-23 11:47:36