Configurazioni NLog più utili [chiuso]


348

Quali sono le configurazioni migliori o più utili per la registrazione con NLog? (Questi possono essere semplici o complessi, purché utili.)

Sto pensando ad esempi come il rollover automatico dei file di registro a una determinata dimensione, la modifica del layout (messaggio di registro) in presenza di un'eccezione, l'escalation del livello di registro dopo che si è verificato un errore, ecc.

Ecco alcuni link:


3
Ecco alcuni suggerimenti per il tuning delle prestazioni basati sui test: deep-depth.blogspot.com/2014/01/…
Neil

Risposte:


391

Alcuni di questi rientrano nella categoria di suggerimenti NLog generali (o di registrazione) piuttosto che in suggerimenti di configurazione.

Ecco alcuni link di registrazione generali da qui a SO (potresti aver già visto alcuni o tutti questi):

log4net vs. Nlog

Registrazione delle migliori pratiche

Qual è il punto di una facciata in legno?

Perché i logger raccomandano di usare un logger per classe?

Utilizzare il modello comune di denominazione del logger in base alla classe Logger logger = LogManager.GetCurrentClassLogger(). Questo ti dà un alto grado di granularità nei tuoi logger e ti dà una grande flessibilità nella configurazione dei logger (controllo globale, per spazio dei nomi, per nome specifico del logger, ecc.).

Utilizzare i logger non basati sul nome di classe, ove appropriato. Forse hai una funzione per la quale vuoi davvero controllare la registrazione separatamente. Forse hai dei problemi di registrazione trasversale (registrazione delle prestazioni).

Se non si utilizza la registrazione basata su classname, prendere in considerazione la denominazione dei propri logger in una sorta di struttura gerarchica (forse per area funzionale) in modo da poter mantenere una maggiore flessibilità nella configurazione. Ad esempio, potresti avere un'area funzionale "database", un FA "analisi" e un FA "ui". Ognuno di questi potrebbe avere aree secondarie. Quindi, potresti richiedere logger come questo:

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

E così via. Con i logger gerarchici, è possibile configurare la registrazione a livello globale ("*" o root logger), per FA (Database, Analysis, UI) o per sottoarea (Database.Connect, ecc.).

I logger hanno molte opzioni di configurazione:

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

Consulta la guida di NLog per ulteriori informazioni sul significato esatto di ciascuna opzione. Probabilmente gli elementi più importanti qui sono la possibilità di regole jolly del logger, il concetto che più regole del logger possono "eseguire" per una singola istruzione di registrazione e che una regola del logger può essere contrassegnata come "finale", quindi le regole successive non verranno eseguite per un data dichiarazione di registrazione.

Utilizzare GlobalDiagnosticContext, MappedDiagnosticContext e NestedDiagnosticContext per aggiungere ulteriore contesto all'output.

Usa "variabile" nel tuo file di configurazione per semplificare. Ad esempio, è possibile definire variabili per i layout e quindi fare riferimento alla variabile nella configurazione di destinazione anziché specificare direttamente il 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>

In alternativa, è possibile creare un set "personalizzato" di proprietà da aggiungere a 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}"/>

Oppure, puoi fare cose come creare renderizzatori di layout "giorno" o "mese" rigorosamente tramite la configurazione:

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

Puoi anche utilizzare i rendering di layout per definire il tuo nome file:

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

Se arrotoli il tuo file ogni giorno, ogni file potrebbe essere chiamato "Monday.log", "Tuesday.log", ecc.

Non aver paura di scrivere il tuo renderizzatore di layout. È facile e consente di aggiungere le informazioni di contesto al file di registro tramite la configurazione. Ad esempio, ecco un renderer di layout (basato su NLog 1.x, non 2.0) che può aggiungere Trace.CorrelationManager.ActivityId al registro:

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

Indica a NLog dove si trovano le estensioni NLog (quale assembly) in questo modo:

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

Usa il renderer di layout personalizzato in questo modo:

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

Usa target asincroni:

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

E wrapper target predefiniti:

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

ove opportuno. Vedi i documenti NLog per maggiori informazioni su quelli.

Di 'a NLog di guardare e ricaricare automaticamente la configurazione se cambia:

<nlog autoReload="true" /> 

Esistono diverse opzioni di configurazione per la risoluzione dei problemi di NLog

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

Vedere la Guida NLog per ulteriori informazioni.

NLog 2.0 aggiunge i wrapper LayoutRenderer che consentono di eseguire ulteriori elaborazioni sull'output di un renderer di layout (come il taglio di spazi bianchi, lettere maiuscole, lettere minuscole, ecc.).

Non aver paura di racchiudere il logger se vuoi isolare il tuo codice da una forte dipendenza da NLog, ma avvolgi correttamente. Ci sono esempi di come eseguire il wrapping nel repository github di NLog. Un altro motivo per concludere potrebbe essere che si desidera aggiungere automaticamente informazioni di contesto specifiche a ciascun messaggio registrato (inserendolo in LogEventInfo.Context).

Ci sono pro e contro nel wrapping (o nell'astrazione) di NLog (o in qualsiasi altro framework di registrazione per quella materia). Con un piccolo sforzo, puoi trovare molte informazioni qui su SO che presentano entrambe le parti.

Se stai pensando di eseguire il wrapping, considera l'utilizzo di Common.Logging . Funziona abbastanza bene e consente di passare facilmente a un altro framework di registrazione se lo si desidera. Inoltre, se stai pensando di eseguire il wrapping, pensa a come gestirai gli oggetti di contesto (GDC, MDC, NDC). Common.Logging non supporta attualmente un'astrazione per loro, ma è presumibilmente nella coda di funzionalità da aggiungere.


3
Bella risposta. Solo una cosa da aggiungere, $ {machine} dovrebbe essere $ {machinename}. Vedi github.com/nlog/NLog/wiki/Layout-Renderers .
liang,

2
Ho modificato Common.Logging e aggiunto l'astrazione mancante, vedere il progetto GitHub o NuGet .
Danny Varod,

Non sono riuscito a trovare nulla di informativo sul nlog nella loro documentazione, forse sto guardando gli esempi di github nel modo sbagliato? Chissà.
JARRRRG

Come utilizzare quel renderer personalizzato con l'API (nessun file di configurazione)? Ecco cosa sto cercando di realizzare.
InteXX

Ok capito. Il NewLinelayout completa il compito. Ecco cosa mi è venuto in mente. È sicuramente molto più semplice di quanto mi aspettassi.
InteXX

65

Trattare le eccezioni in modo diverso

Vogliamo spesso ottenere maggiori informazioni in caso di eccezione. La seguente configurazione ha due target, un file e la console, che filtrano sulla presenza o meno di informazioni sull'eccezione. (EDIT: Jarek ha pubblicato un nuovo metodo per farlo in vNext .)

La chiave è avere un target wrapper con 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
È piuttosto interessante con il target separato e FilteringWrapper per formattare l'eccezione. Di recente ho appena risposto a una domanda di un ragazzo che voleva includere il renderer di layout {exception} nel suo output ma non voleva ottenere il () apparentemente registrato se NON c'è un'eccezione. Questa tecnica probabilmente funzionerebbe bene per lui.
wageoghe

+1 Molto bello. L'ho aggiunto ai segnalibri per molto tempo e ho fatto riferimento al "commento di Pat" da un'altra domanda SO in merito a un layout condizionale.
eduncan911,

1
Se viene registrata un'eccezione, verrà registrata due volte (parte VerboseLayout).
Tien Do

2
L'ho appena provato domani nel mio progetto, poiché hai impostato una regola minlevel = "Avvisa" su "file, fileAsException", tutti i registri verranno prima registrati con destinazione del file (nessun filtro) e se si tratta di un'eccezione (come filtrato da condizione) verrà inoltre registrato con fileAsException.
Tien Do,

3
@Tiendq Oh, capisco. Ciò ha senso, sebbene l'eccezione stessa (in dettaglio) verrà registrata una sola volta (ma il suo messaggio verrà registrato due volte). Probabilmente puoi risolverlo aggiungendo condition="length('${exception}')=0(o forse lo è ==) a target name="file".
Pat

60

Apparentemente, ora puoi usare NLog con Growl per 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 con Growl per Windows Messaggio di traccia NLog con Growl per Windows Messaggio di debug NLog con Growl per Windows Messaggio informativo NLog con Growl per Windows NLog avvisa il messaggio con Growl per Windows Messaggio di errore NLog con Growl per Windows Messaggio fatale NLog con Growl per Windows


puoi dirmi cosa devo fare per la connessione remort? la cosa funziona per me per localhost ma quando ho dato un indirizzo IP nell'host non funziona !!
Neel,

@Nel, dovresti controllare le impostazioni "Sicurezza" in Growl sul computer di destinazione. Devi abilitare esplicitamente le notifiche "LAN" e potresti voler impostare una password (che dovresti quindi aggiungere al tuo target NLog). Ma non mi piaceva che le notifiche remote apparissero in Growl con una "Origine" di "Macchina locale"; Dovrei aggiungere l'host alle voci del registro per sapere dove hanno avuto origine le notifiche.
Kenny Evitt,

Posso far funzionare le notifiche sul mio computer locale, ma non da remoto. Le mie impostazioni di sicurezza non hanno password su growl, quindi ho aggiunto solo IP e porta. Ma nulla viene inviato.
Jack Reilly,

1
Questo progetto è morto al 100%
Developer

28

Configurare NLog tramite XML, ma a livello di programmazione

Che cosa? Sapevi che puoi specificare il NLog XML direttamente su NLog dalla tua app, invece di far leggere a NLog dal file di configurazione? Bene, puoi. Supponiamo che tu abbia un'app distribuita e desideri utilizzare la stessa configurazione ovunque. È possibile mantenere un file di configurazione in ogni posizione e gestirlo separatamente, è possibile mantenerne uno in una posizione centrale e inviarlo alle posizioni dei satelliti, oppure è possibile fare molte altre cose. In alternativa, è possibile archiviare l'XML in un database, ottenerlo all'avvio dell'app e configurare NLog direttamente con quell'XML (magari ricontrollando periodicamente per vedere se è cambiato).

  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

Non sono sicuro di quanto sia robusto, ma questo esempio fornisce un utile punto di partenza per le persone che potrebbero voler provare a configurare in questo modo.


funziona molto bene ... tranne che usando questo non è più possibile riconfigurare dinamicamente il sistema di registrazione. Ciò è particolarmente vero se si collega a un file esterno (include)
Newtopian

2
Questo ha funzionato, anche se ho dovuto scrivere un XML "buono" includendo:<?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
Questo è un bel segway nella configurazione centralizzata. Lettori futuri, XML codificato in questo esempio è solo per demo (IMHO), leggerlo da un database o file centralizzato potrebbe essere la vera implementazione.
granadaCoder

@wageoghe; Perché ricevo un errore (il logger non esiste)? Ho appena copiato e incollato il codice
Bsflasher il

22

Registrazione di diversi livelli in base alla presenza o meno di un errore

Questo esempio consente di ottenere ulteriori informazioni in caso di errore nel codice. Fondamentalmente, bufferizza i messaggi e li emette solo a un certo livello di registro (ad esempio Avvisa) a meno che non venga soddisfatta una certa condizione (ad esempio si è verificato un errore, quindi il livello di registro è> = Errore), quindi genererà più informazioni (ad es. tutti i messaggi dai livelli di registro> = Traccia). Poiché i messaggi sono bufferizzati, ciò consente di raccogliere informazioni di traccia di ciò che è accaduto prima della registrazione di Error o ErrorException - molto utile!

L'ho adattato da un esempio nel codice sorgente . All'inizio sonoAspNetBufferingWrapper stato scartato perché ho lasciato fuori (poiché la mia non è un'app ASP) - si scopre che PostFilteringWrapper richiede un target bufferizzato. Si noti che l' target-refelemento utilizzato nell'esempio collegato sopra non può essere utilizzato in NLog 1.0 (sto usando 1.0 Aggiorna per un'app .NET 4.0); è necessario inserire l'obiettivo all'interno del blocco wrapper. Si noti inoltre che la sintassi logica (ovvero simboli maggiore o minore di, <e>) deve utilizzare i simboli, non l'escape XML per tali simboli (cioè &gt;e&lt; escape ) oppure NLog si guasterà.

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 alcune versioni di NLog (per mono e credo 2.0), ciò causa StackOverflowException, ma non in altre (aggiornamento NLog 1).
Pat

Per quanto riguarda l'overflow - sembra essere dovuto solo al layout di tipo CSV - se faccio un layout normale non c'è problema.
Pat

A cosa serve il fileAsCsv target-ref? Sto cercando di far funzionare questo esempio con NLog v2.0.0.2000 ma finora non ci riesco.
Peter Mounce,

@PeterMounce Il fileAsCsvtarget-ref è solo un artefatto del mio test. Credo che NLog 2 abbia / abbia avuto problemi con CsvLayouts che NLog 1 / Refresh non aveva.
Pat

22

Ho fornito un paio di risposte ragionevolmente interessanti a questa domanda:

Nlog - Generazione della sezione di intestazione per un file di registro

Aggiunta di un'intestazione:

La domanda voleva sapere come aggiungere un'intestazione al file di registro. L'uso di voci di configurazione come questa consente di definire il formato dell'intestazione separatamente dal formato delle altre voci del registro. Usa un singolo logger, forse chiamato "headerlogger" per registrare un singolo messaggio all'inizio dell'applicazione e otterrai la tua intestazione:

Definisci l'intestazione e i layout dei file:

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

Definisci gli obiettivi usando i layout:

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

Definire i logger:

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

Scrivi l'intestazione, probabilmente all'inizio del programma:

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

Questa è in gran parte solo un'altra versione dell'idea "Trattare le eccezioni in modo diverso".

Registra ogni livello di registro con un layout diverso

Allo stesso modo, il poster voleva sapere come cambiare il formato per livello di registrazione. Non mi era chiaro quale fosse l'obiettivo finale (e se potesse essere raggiunto in modo "migliore"), ma sono stato in grado di fornire una configurazione che ha fatto quello che mi ha chiesto:

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

Ancora una volta, molto simile al trattamento delle eccezioni in modo diverso .


1
Freddo! Non avevo visto GlobalDiagnosticsContextprima.
Pat

10

Accedi a Twitter

Sulla base di questo post su un appender Twitter log4net , ho pensato di provare a scrivere un target Twitter NLog (usando l'aggiornamento NLog 1.0, non 2.0). Purtroppo, finora non sono stato in grado di ottenere un Tweet per pubblicare effettivamente con successo. Non so se c'è qualcosa di sbagliato nel mio codice, Twitter, connessione Internet / firewall della nostra azienda o cosa. Sto pubblicando il codice qui nel caso qualcuno fosse interessato a provarlo. Si noti che esistono tre diversi metodi "Posta". Il primo che ho provato è PostMessageToTwitter. PostMessageToTwitter è essenzialmente lo stesso di PostLoggingEvent nel post originale. Se lo uso ottengo un'eccezione 401. PostMessageBasic ottiene la stessa eccezione. PostMessage funziona senza errori, ma il messaggio non arriva ancora su Twitter. PostMessage e PostMessageBasic sono basati su esempi che ho trovato qui su SO.

Cordiali saluti - Ho appena trovato un commento di @Jason Diller a una risposta in questo post che dice che Twitter disattiverà l'autenticazione di base "il prossimo mese". Era il maggio 2010 ed è ora il dicembre 2010, quindi immagino che potrebbe essere il motivo per cui questo non funziona.

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

Configuralo in questo modo:

Di 'a NLog l'assembly che contiene il target:

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

Configura il target:

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

Se qualcuno lo prova e ha successo, rispondi con i tuoi risultati.


Twitter utilizza OAuth - .NET ha un provider in dotnetopenauth.net
Pat

8

Modo più semplice per registrare ogni livello di registro con un layout diverso utilizzando Layout condizionali

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

Vedi https://github.com/NLog/NLog/wiki/When-Filter per la sintassi


7

Segnalazione a un sito Web / database esterno

Volevo un modo per segnalare in modo semplice e automatico errori (poiché gli utenti spesso non lo fanno) dalle nostre applicazioni. La soluzione più semplice che ho potuto trovare era un URL pubblico - una pagina Web che poteva prendere l'input e archiviarlo in un database - a cui venivano inviati dati in caso di errore dell'applicazione. (Il database potrebbe quindi essere controllato da uno sviluppatore o uno script per sapere se ci sono nuovi errori.)

Ho scritto la pagina Web in PHP e creato un database mysql, un utente e una tabella per archiviare i dati. Ho deciso su quattro variabili utente, un ID e un timestamp. Le possibili variabili (incluse nell'URL o come dati POST) sono:

  • app (Nome dell'applicazione)
  • msg (messaggio - ad es. si è verificata un'eccezione ...)
  • dev (sviluppatore - ad es. Pat)
  • src(fonte: questo verrebbe da una variabile relativa al computer su cui era in esecuzione l'app, ad esempio Environment.MachineNameo alcuni di questi)
  • log (un file di registro o un messaggio dettagliato)

(Tutte le variabili sono opzionali, ma non viene segnalato nulla se nessuna di esse è impostata, quindi se si visita l'URL del sito Web nulla viene inviato al database.)

Per inviare i dati all'URL, ho usato il WebServicetarget NLog . (Nota, all'inizio ho avuto alcuni problemi con questo obiettivo. Non è stato fino a quando ho guardato la fonte che ho capito che il mio urlnon poteva finire con un /.)

Tutto sommato, non è un cattivo sistema per tenere sotto controllo le app esterne. (Ovviamente, la cosa educata da fare è informare i tuoi utenti che segnalerai eventuali dati sensibili e dare loro un modo per attivare / disattivare.)

Roba di MySQL

(L'utente db ha solo i INSERTprivilegi su questa tabella nel proprio database.)

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'

Codice del sito web

(PHP 5.3 o 5.2 con PDO abilitato , il file è index.phpnella /reportcartella)

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

Codice app (file di configurazione 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>

Nota: potrebbero esserci dei problemi con la dimensione del file di registro, ma non ho trovato un modo semplice per troncarlo (ad esempio un tailcomando di la * nix ).


Questo ha funzionato per un progetto, ma in altri ho avuto problemi con url: InnerException: System.InvalidCastException Message = Cast non valido da 'System.String' a 'System.Uri'. Source = mscorlib StackTrace: at System.Convert.DefaultToType (valore IConvertible, Type targetType, provider IFormatProvider) su System.String.System.IConvertible.ToType (tipo di tipo, provider IFormatProvider) su System.Convert.ChangeType (valore oggetto, conversione tipo , Fornitore IFormatProvider)
Pat

Un'altra opzione se si desidera essere in grado di monitorare il registro e ricevere una notifica in caso di errore sarebbe un target Twitter. Vedi questo link per un Appender di Twitter scritto per log4net: twitterappender.codeplex.com La pubblicazione originale del blog che discute di questo è qui: caseywatson.com/2009/07/07/log4net-twitter-awesome Dovrebbe essere abbastanza facile scrivere qualcosa di simile per NLog.
wageoghe,

Mi sono divertito a scrivere un NLog TwitterTarget, ma in realtà non ho avuto successo nel pubblicare un Tweet. Ho pubblicato il codice come risposta. Sentiti libero di provarlo se lo desideri.
wageoghe,

4

Accedi da Silverlight

Quando si utilizza NLog con Silverlight è possibile inviare la traccia sul lato server tramite il servizio Web fornito . È inoltre possibile scrivere su un file locale nell'archivio isolato, che risulta utile se il server Web non è disponibile. Vedi qui per i dettagli, cioè usa qualcosa del genere per diventare un bersaglio:

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);
                    }
                }
            }
        }
    } 
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.