Nützlichste NLog-Konfigurationen [geschlossen]


348

Was sind die besten oder nützlichsten Konfigurationen für die Protokollierung mit NLog? (Diese können einfach oder komplex sein, solange sie nützlich sind.)

Ich denke an Beispiele wie das automatische Überfahren von Protokolldateien mit einer bestimmten Größe, das Ändern des Layouts (Protokollmeldung), ob es eine Ausnahme gibt oder nicht, das Eskalieren der Protokollstufe, sobald ein Fehler aufgetreten ist usw.

Hier sind einige Links:


3
Hier sind einige Tipps zur Leistungsoptimierung basierend auf Tests: deep-depth.blogspot.com/2014/01/…
Neil

Antworten:


391

Einige davon fallen eher in die Kategorie der allgemeinen NLog- (oder Protokollierungs-) Tipps als in reine Konfigurationsvorschläge.

Hier sind einige allgemeine Protokollierungslinks von hier bei SO (möglicherweise haben Sie einige oder alle bereits gesehen):

log4net vs. Nlog

Best Practices für die Protokollierung

Was bringt eine Holzfassade?

Warum empfehlen Logger die Verwendung eines Loggers pro Klasse?

Verwenden Sie das allgemeine Muster für die Benennung Ihres Loggers basierend auf der Klasse Logger logger = LogManager.GetCurrentClassLogger(). Dies gibt Ihnen ein hohes Maß an Granularität in Ihren Loggern und bietet Ihnen große Flexibilität bei der Konfiguration der Logger (Steuerung global, nach Namespace, nach spezifischem Loggernamen usw.).

Verwenden Sie gegebenenfalls nicht auf Klassennamen basierende Logger. Möglicherweise haben Sie eine Funktion, für die Sie die Protokollierung wirklich separat steuern möchten. Möglicherweise haben Sie einige Probleme mit der Protokollierung (Leistungsprotokollierung).

Wenn Sie keine auf Klassennamen basierende Protokollierung verwenden, sollten Sie Ihre Protokollierer in einer hierarchischen Struktur (möglicherweise nach Funktionsbereich) benennen, damit Sie Ihre Konfiguration flexibler gestalten können. Beispielsweise könnten Sie einen Funktionsbereich "Datenbank", einen FA "Analyse" und einen FA "UI" haben. Jeder von diesen kann Unterbereiche haben. Sie können also folgende Logger anfordern:

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

Und so weiter. Mit hierarchischen Protokollierern können Sie die Protokollierung global ("*" oder Root-Protokollierer), nach FA (Datenbank, Analyse, Benutzeroberfläche) oder nach Unterbereich (Database.Connect usw.) konfigurieren.

Logger haben viele Konfigurationsoptionen:

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

Weitere Informationen dazu, was die einzelnen Optionen bedeuten, finden Sie in der NLog-Hilfe . Die wahrscheinlich bemerkenswertesten Elemente sind die Möglichkeit, Protokollierungsregeln mit Platzhaltern zu versehen, das Konzept, dass mehrere Protokollierungsregeln für eine einzelne Protokollierungsanweisung "ausgeführt" werden können und dass eine Protokollierungsregel als "endgültig" markiert werden kann, sodass nachfolgende Regeln für a nicht ausgeführt werden Protokollierungsanweisung gegeben.

Verwenden Sie GlobalDiagnosticContext, MappedDiagnosticContext und NestedDiagnosticContext, um Ihrer Ausgabe zusätzlichen Kontext hinzuzufügen.

Verwenden Sie zur Vereinfachung "Variable" in Ihrer Konfigurationsdatei. Beispielsweise können Sie Variablen für Ihre Layouts definieren und dann auf die Variable in der Zielkonfiguration verweisen, anstatt das Layout direkt anzugeben.

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

Sie können auch einen "benutzerdefinierten" Satz von Eigenschaften erstellen, um ihn einem Layout hinzuzufügen.

  <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}"/>

Oder Sie können beispielsweise "Tag" - oder "Monat" -Layout-Renderer ausschließlich über die Konfiguration erstellen:

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

Sie können auch Layout-Renderings verwenden, um Ihren Dateinamen zu definieren:

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

Wenn Sie Ihre Datei täglich rollen, kann jede Datei den Namen "Monday.log", "Tuesday.log" usw. haben.

Haben Sie keine Angst, Ihren eigenen Layout-Renderer zu schreiben. Es ist einfach und ermöglicht es Ihnen, Ihre eigenen Kontextinformationen über die Konfiguration zur Protokolldatei hinzuzufügen. Hier ist beispielsweise ein Layout-Renderer (basierend auf NLog 1.x, nicht 2.0), der die Trace.CorrelationManager.ActivityId zum Protokoll hinzufügen kann:

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

Teilen Sie NLog mit, wo Ihre NLog-Erweiterungen (welche Assembly) folgendermaßen aussehen:

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

Verwenden Sie den benutzerdefinierten Layout-Renderer wie folgt:

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

Verwenden Sie asynchrone Ziele:

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

Und Standard-Ziel-Wrapper:

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

gegebenenfalls. Weitere Informationen hierzu finden Sie in den NLog-Dokumenten.

Weisen Sie NLog an, die Konfiguration zu überwachen und automatisch neu zu laden, wenn sie sich ändert:

<nlog autoReload="true" /> 

Es gibt verschiedene Konfigurationsoptionen, die bei der Fehlerbehebung bei NLog helfen

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

Weitere Informationen finden Sie in der NLog-Hilfe.

NLog 2.0 fügt LayoutRenderer-Wrapper hinzu, mit denen zusätzliche Verarbeitungen für die Ausgabe eines Layout-Renderers durchgeführt werden können (z. B. Trimmen von Leerzeichen, Groß- und Kleinschreibung usw.).

Haben Sie keine Angst, den Logger zu verpacken, wenn Sie Ihren Code von einer harten Abhängigkeit von NLog isolieren möchten, aber verpacken Sie ihn korrekt. Es gibt Beispiele für das Einschließen in das Github-Repository des NLog. Ein weiterer Grund für den Wrap könnte sein, dass Sie jeder protokollierten Nachricht automatisch bestimmte Kontextinformationen hinzufügen möchten (indem Sie sie in LogEventInfo.Context einfügen).

Das Umschließen (oder Abstrahieren) von NLog (oder eines anderen Protokollierungsframeworks) hat Vor- und Nachteile. Mit ein wenig Aufwand finden Sie hier viele Informationen zu SO, die beide Seiten präsentieren.

Wenn Sie ein Wrapping in Betracht ziehen, sollten Sie Common.Logging verwenden . Es funktioniert ziemlich gut und ermöglicht es Ihnen, einfach zu einem anderen Protokollierungsframework zu wechseln, wenn Sie dies wünschen. Überlegen Sie auch, wie Sie mit den Kontextobjekten (GDC, MDC, NDC) umgehen, wenn Sie über das Umbrechen nachdenken. Common.Logging unterstützt derzeit keine Abstraktion für sie, befindet sich jedoch angeblich in der Warteschlange der hinzuzufügenden Funktionen.


3
Gute Antwort. Nur eine Sache, die hinzugefügt werden muss: $ {machine} sollte $ {machinename} sein. Siehe github.com/nlog/NLog/wiki/Layout-Renderers .
Liang

2
Ich habe Common.Logging gegabelt und die fehlende Abstraktion hinzugefügt, siehe GitHub-Projekt oder NuGet .
Danny Varod

Ich habe in ihrer eigenen Dokumentation nichts Informatives über nlog gefunden. Vielleicht schaue ich die Github-Beispiele falsch durch? Wer weiß.
JARRRRG

Wie verwende ich diesen benutzerdefinierten Renderer mit der API (keine Konfigurationsdatei)? Hier ist , was ich versuche zu erreichen.
InteXX

OK habe es. Das NewLineLayout erfüllt die Aufgabe. Folgendes habe ich mir ausgedacht. Es ist sicher viel einfacher als ich erwartet hatte.
InteXX

65

Ausnahmen anders behandeln

Wir möchten oft mehr Informationen erhalten, wenn es eine Ausnahme gibt. Die folgende Konfiguration hat zwei Ziele, eine Datei und die Konsole, die filtern, ob Ausnahmeinformationen vorhanden sind oder nicht. (BEARBEITEN: Jarek hat in vNext eine neue Methode dafür veröffentlicht .)

Der Schlüssel ist, ein Wrapper-Ziel mit zu haben 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>

1
Das ist ziemlich cool mit dem separaten Ziel und FilteringWrapper, um die Ausnahme zu formatieren. Ich habe kürzlich eine Frage von einem Mann beantwortet, der den Layout-Renderer {exception} in seine Ausgabe aufnehmen wollte, aber nicht das () protokollieren wollte, das anscheinend protokolliert wird, wenn es KEINE Ausnahme gibt. Diese Technik würde wahrscheinlich gut für ihn funktionieren.
Lohn

+1 Sehr schön. Ich habe dieses Lesezeichen schon lange mit einem Lesezeichen versehen und wurde von einer anderen SO-Frage in Bezug auf ein bedingtes Layout auf "Pats Kommentar" verwiesen.
eduncan911

1
Wenn eine Ausnahme protokolliert wird, wird sie zweimal protokolliert (VerboseLayout-Teil).
Tien Do

2
Ich habe es erst morgen in meinem Projekt versucht, da Sie eine Regel minlevel = "Warn" auf "file, fileAsException" setzen, werden alle Protokolle zuerst mit dem Dateiziel (kein Filter) protokolliert, und wenn es sich um eine Ausnahme handelt (gefiltert nach) Bedingung) wird auch mit fileAsException protokolliert.
Tien Do

3
@Tiendq Oh, ich verstehe. Dies ist sinnvoll, obwohl die Ausnahme selbst (im Detail) nur einmal protokolliert wird (ihre Nachricht wird jedoch zweimal protokolliert). Sie können das wahrscheinlich beheben, indem Sie hinzufügen condition="length('${exception}')=0(oder vielleicht ist es ==) zu target name="file".
Pat

60

Anscheinend können Sie NLog jetzt mit Growl für Windows verwenden .

<?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 mit Growl für Windows NLog-Trace-Nachricht mit Growl für Windows NLog-Debug-Nachricht mit Growl für Windows NLog-Info-Nachricht mit Growl für Windows NLog-Warnmeldung mit Growl für Windows NLog-Fehlermeldung mit Growl für Windows NLog schwerwiegende Nachricht mit Growl für Windows


Kannst du mir sagen, was ich für eine Remort-Verbindung tun soll? Das Ding funktioniert für mich für localhost, aber wenn ich eine IP-Adresse im Host angegeben habe, funktioniert es nicht !!
Neel

@Neel, du solltest die "Sicherheits" -Einstellungen in Growl auf dem Zielcomputer überprüfen. Sie müssen "LAN" -Benachrichtigungen explizit aktivieren und möchten möglicherweise ein Kennwort einrichten (das Sie dann Ihrem NLog-Ziel hinzufügen müssen). Aber es hat mir nicht gefallen, dass die Remote-Benachrichtigungen in Growl mit einem "Ursprung" von "Local Machine" angezeigt wurden. Ich müsste den Host zu den Protokolleinträgen hinzufügen, um zu wissen, woher die Benachrichtigungen stammen.
Kenny Evitt

Ich kann die Benachrichtigungen auf meinem lokalen Computer abrufen, jedoch nicht remote. Meine Sicherheitseinstellungen haben beim Knurren kein Passwort, daher habe ich nur die IP und den Port hinzugefügt. Aber nichts wird gesendet.
Jack Reilly

1
Dieses Projekt ist zu 100% tot
Entwickler

28

Konfigurieren Sie NLog über XML, jedoch programmgesteuert

Was? Wussten Sie, dass Sie das NLog-XML direkt in Ihrer App für NLog angeben können, anstatt es von NLog aus der Konfigurationsdatei lesen zu lassen? Ja, du kannst. Angenommen, Sie haben eine verteilte App und möchten überall dieselbe Konfiguration verwenden. Sie können eine Konfigurationsdatei an jedem Speicherort aufbewahren und separat verwalten, Sie können eine an einem zentralen Speicherort verwalten und an die Satellitenstandorte senden, oder Sie können wahrscheinlich viele andere Dinge tun. Sie können Ihr XML auch in einer Datenbank speichern, es beim Start der App abrufen und NLog direkt mit diesem XML konfigurieren (möglicherweise regelmäßig überprüfen, ob es sich geändert hat).

  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

Ich bin mir nicht sicher, wie robust dies ist, aber dieses Beispiel bietet einen nützlichen Ausgangspunkt für Leute, die versuchen möchten, so zu konfigurieren.


es funktioniert sehr gut ... außer wenn dies verwendet wird, ist es nicht mehr möglich, das Protokollierungssystem dynamisch neu zu konfigurieren. Dies ist besonders dann der
Newtopian

2
Dies funktionierte, obwohl ich "gutes" XML schreiben musste, indem ich <?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'>
Folgendes

1
Dies ist ein schöner Übergang in die zentralisierte Konfiguration. Zukünftige Leser, fest codierte XML-Dateien in diesem Beispiel sind nur für Demos (IMHO) vorgesehen. Das Lesen aus einer Datenbank oder einer zentralisierten Datei ist möglicherweise die eigentliche Implementierung.
GranadaCoder

@wageoghe; Warum erhalte ich eine Fehlermeldung (Logger ist nicht vorhanden)? Ich kopiere einfach den Code
füge ihn ein

22

Protokollierung verschiedener Ebenen, je nachdem, ob ein Fehler vorliegt oder nicht

In diesem Beispiel können Sie weitere Informationen abrufen, wenn ein Fehler in Ihrem Code vorliegt. Grundsätzlich puffert es Nachrichten und gibt nur Nachrichten auf einer bestimmten Protokollebene aus (z. B. Warnen), es sei denn, eine bestimmte Bedingung ist erfüllt (z. B. ist ein Fehler aufgetreten, daher ist die Protokollebene> = Fehler). Anschließend werden weitere Informationen ausgegeben (z alle Nachrichten von Protokollebenen> = Trace). Da die Nachrichten gepuffert sind, können Sie auf diese Weise Trace-Informationen darüber sammeln, was passiert ist, bevor ein Fehler oder eine ErrorException protokolliert wurde - sehr nützlich!

Ich habe dieses anhand eines Beispiels im Quellcode angepasst . Ich wurde zuerst geworfen, weil ich die ausgelassen habe AspNetBufferingWrapper(da meine keine ASP-App ist) - es stellt sich heraus, dass der PostFilteringWrapper ein gepuffertes Ziel benötigt. Beachten Sie, dass das target-refim oben verlinkten Beispiel verwendete Element in NLog 1.0 nicht verwendet werden kann (ich verwende 1.0 Refresh für eine .NET 4.0-App). Es ist notwendig, dass Sie Ihr Ziel in den Wrapper-Block legen. Beachten Sie auch, dass die Logiksyntax (dh Symbole größer und kleiner als <und>) die Symbole verwenden muss, nicht das XML- Escapezeichen für diese Symbole (dh &gt;und &lt;), da sonst NLog fehlerhaft wird.

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>

In einigen Versionen von NLog (für Mono und ich denke 2.0) verursacht dies eine StackOverflowException, in anderen jedoch nicht (NLog 1-Aktualisierung).
Pat

In Bezug auf den Überlauf - Es scheint nur darauf zurückzuführen zu sein, dass das Layout vom CSV-Typ ist - wenn ich ein reguläres Layout mache, gibt es kein Problem.
Pat

Wofür ist die fileAsCsv-Zielreferenz da? Ich versuche, dieses Beispiel gegen NLog v2.0.0.2000 zum Laufen zu bringen, aber bisher ist dies fehlgeschlagen.
Peter Mounce

@PeterMounce Die Zielreferenz fileAsCsvist nur ein Artefakt aus meinen Tests. Ich glaube, NLog 2 hat / hatte Probleme mit CsvLayouts, die NLog 1 / Refresh nicht hatte.
Pat

22

Ich habe einige einigermaßen interessante Antworten auf diese Frage gegeben:

Nlog - Generieren eines Header-Abschnitts für eine Protokolldatei

Hinzufügen eines Headers:

Die Frage wollte wissen, wie man der Protokolldatei einen Header hinzufügt. Mit solchen Konfigurationseinträgen können Sie das Header-Format getrennt vom Format der übrigen Protokolleinträge definieren. Verwenden Sie einen einzelnen Logger, möglicherweise "Headerlogger" genannt, um eine einzelne Nachricht zu Beginn der Anwendung zu protokollieren, und Sie erhalten Ihren Header:

Definieren Sie die Header- und Dateilayouts:

  <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}" />

Definieren Sie die Ziele anhand der Layouts:

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

Definieren Sie die Logger:

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

Schreiben Sie den Header, wahrscheinlich zu Beginn des Programms:

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

Dies ist größtenteils nur eine andere Version der Idee "Ausnahmen anders behandeln".

Protokollieren Sie jede Protokollebene mit einem anderen Layout

Ebenso wollte das Poster wissen, wie das Format pro Protokollierungsstufe geändert werden kann. Mir war nicht klar, was das Endziel war (und ob es auf "bessere" Weise erreicht werden könnte), aber ich konnte eine Konfiguration bereitstellen, die das tat, was er verlangte:

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

Wieder sehr ähnlich wie Ausnahmen anders zu behandeln .


1
Cool! Ich hatte das noch nie gesehen GlobalDiagnosticsContext.
Pat

10

Bei Twitter anmelden

Basierend auf diesem Beitrag über einen log4net Twitter AppenderIch dachte, ich würde versuchen, ein NLog-Twitter-Ziel zu schreiben (mit NLog 1.0-Aktualisierung, nicht mit 2.0). Leider konnte ich bisher keinen Tweet bekommen, um tatsächlich erfolgreich zu posten. Ich weiß nicht, ob etwas in meinem Code, Twitter, der Internetverbindung / Firewall unseres Unternehmens oder was nicht stimmt. Ich poste den Code hier, falls jemand daran interessiert ist, ihn auszuprobieren. Beachten Sie, dass es drei verschiedene "Post" -Methoden gibt. Der erste, den ich ausprobiert habe, ist PostMessageToTwitter. PostMessageToTwitter ist im Wesentlichen dasselbe wie PostLoggingEvent im ursprünglichen Beitrag. Wenn ich das benutze, bekomme ich eine 401-Ausnahme. PostMessageBasic erhält dieselbe Ausnahme. PostMessage läuft fehlerfrei, aber die Nachricht erreicht Twitter immer noch nicht. PostMessage und PostMessageBasic basieren auf Beispielen, die ich hier auf SO gefunden habe.

Zu Ihrer Information - Ich habe gerade einen Kommentar von @Jason Diller zu einer Antwort in diesem Beitrag gefunden , die besagt, dass Twitter die grundlegende Authentifizierung "nächsten Monat" deaktivieren wird . Dies war im Mai 2010 und es ist jetzt Dezember 2010, also könnte dies der Grund sein, warum dies nicht funktioniert.

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) + "...";
    }
  }
}

Konfigurieren Sie es folgendermaßen:

Teilen Sie NLog die Assembly mit, die das Ziel enthält:

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

Konfigurieren Sie das Ziel:

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

Wenn jemand dies ausprobiert und Erfolg hat, senden Sie Ihre Ergebnisse zurück.


Twitter verwendet OAuth - .NET hat einen Anbieter in dotnetopenauth.net
Pat

7

Berichterstattung an eine externe Website / Datenbank

Ich wollte eine Möglichkeit, Fehler in unseren Anwendungen einfach und automatisch zu melden (da Benutzer dies häufig nicht tun). Die einfachste Lösung, die ich finden konnte, war eine öffentliche URL - eine Webseite, die Eingaben entgegennehmen und in einer Datenbank speichern konnte -, die bei einem Anwendungsfehler Daten sendet. (Die Datenbank könnte dann von einem Entwickler oder einem Skript überprüft werden, um festzustellen, ob neue Fehler vorliegen.)

Ich habe die Webseite in PHP geschrieben und eine MySQL-Datenbank, einen Benutzer und eine Tabelle zum Speichern der Daten erstellt. Ich entschied mich für vier Benutzervariablen, eine ID und einen Zeitstempel. Die möglichen Variablen (entweder in der URL oder als POST-Daten enthalten) sind:

  • app (Anwendungsname)
  • msg (Meldung - zB Ausnahme aufgetreten ...)
  • dev (Entwickler - zB Pat)
  • src(Quelle - Dies würde von einer Variablen stammen, die sich auf den Computer bezieht, auf dem die App ausgeführt wurde, z. B. Environment.MachineNameoder auf einem solchen Computer. )
  • log (eine Protokolldatei oder eine ausführliche Nachricht)

(Alle Variablen sind optional, aber es wird nichts gemeldet, wenn keine von ihnen festgelegt ist. Wenn Sie also nur die Website-URL besuchen, wird nichts an die Datenbank gesendet.)

Um die Daten an die URL zu senden, habe ich das WebServiceZiel von NLog verwendet . (Beachten Sie, dass ich zuerst einige Probleme mit diesem Ziel hatte. Erst als ich mir die Quelle ansah, stellte ich fest, dass mein Ziel urlnicht mit a enden konnte /.)

Alles in allem ist es kein schlechtes System, um externe Apps im Auge zu behalten. (Natürlich ist es höflich, Ihre Benutzer darüber zu informieren, dass Sie möglicherweise sensible Daten melden, und ihnen die Möglichkeit zu geben, sich ein- oder auszuschalten.)

MySQL-Zeug

(Der INSERTDatenbankbenutzer hat nur Berechtigungen für diese eine Tabelle in seiner eigenen Datenbank.)

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'

Website-Code

(PHP 5.3 oder 5.2 mit aktiviertem PDO , Datei befindet sich im Ordner)index.php/report

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

App-Code (NLog-Konfigurationsdatei)

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

Hinweis: Es kann einige Probleme mit der Größe der Protokolldatei geben, aber ich habe keine einfache Möglichkeit gefunden, sie abzuschneiden (z. B. den tailBefehl a la * nix ).


Dies funktionierte für ein Projekt, aber in anderen hatte ich Probleme mit url: InnerException: System.InvalidCastException Message = Ungültige Umwandlung von 'System.String' in 'System.Uri'. Source = mscorlib StackTrace: bei System.Convert.DefaultToType (IConvertible-Wert, Typ targetType, IFormatProvider-Anbieter) bei System.String.System.IConvertible.ToType (Typtyp, IFormatProvider-Anbieter) bei System.Convert.ChangeType (Objektwert, Typkonvertierungstyp) , IFormatProvider-Anbieter)
Pat

Eine weitere Option, wenn Sie das Protokoll überwachen und im Fehlerfall benachrichtigt werden möchten, ist ein Twitter-Ziel. Unter diesem Link finden Sie einen Twitter-Appender, der für log4net geschrieben wurde: twitterappender.codeplex.com Der ursprüngliche Blog-Beitrag, in dem dies diskutiert wird, ist hier: caseywatson.com/2009/07/07/log4net-twitter-awesome Es sollte ziemlich einfach sein, etwas Ähnliches zu schreiben NLog.
Lohn

Ich habe mit dem Schreiben eines NLog-TwitterTargets herumgespielt, aber keinen Erfolg damit gehabt, einen Tweet zu veröffentlichen. Ich habe den Code als Antwort gepostet. Probieren Sie es einfach aus, wenn Sie möchten.
Lohn

6

Einfachere Möglichkeit, jede Protokollebene mit einem anderen Layout mithilfe von bedingten Layouts zu protokollieren

<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}" />

Die Syntax finden Sie unter https://github.com/NLog/NLog/wiki/When-Filter


4

Protokoll von Silverlight

Wenn Sie NLog mit Silverlight verwenden, können Sie den Trace über den bereitgestellten Webdienst an die Serverseite senden . Sie können auch in eine lokale Datei im isolierten Speicher schreiben. Dies ist hilfreich, wenn der Webserver nicht verfügbar ist. Einzelheiten finden Sie hier , dh verwenden Sie so etwas, um sich selbst zum Ziel zu machen:

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);
                    }
                }
            }
        }
    } 
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.