Configuraciones NLog más útiles [cerrado]

348

¿Cuáles son las configuraciones mejores o más útiles para iniciar sesión con NLog? (Estos pueden ser simples o complejos, siempre que sean útiles).

Estoy pensando en ejemplos como pasar automáticamente los archivos de registro a un cierto tamaño, cambiar el diseño (mensaje de registro) si hay una excepción o no, aumentar el nivel de registro una vez que se ha producido un error, etc.

Aquí hay algunos enlaces:

Palmadita
fuente
3
Aquí hay algunos consejos de ajuste de rendimiento basados ​​en pruebas: deep-depth.blogspot.com/2014/01/…
Neil

Respuestas:

391

Algunos de estos entran en la categoría de consejos generales de NLog (o registro) en lugar de estrictamente sugerencias de configuración.

Aquí hay algunos enlaces de registro generales desde aquí en SO (puede que ya haya visto algunos o todos estos):

log4net vs. Nlog

Registro de mejores prácticas

¿Cuál es el punto de una fachada de tala?

¿Por qué los registradores recomiendan usar un registrador por clase?

Use el patrón común de nombrar su registrador en función de la clase Logger logger = LogManager.GetCurrentClassLogger(). Esto le brinda un alto grado de granularidad en sus registradores y le brinda una gran flexibilidad en la configuración de los registradores (control global, por espacio de nombres, por nombre de registrador específico, etc.).

Utilice registradores que no estén basados ​​en el nombre de clase cuando corresponda. Tal vez tenga una función para la que realmente quiera controlar el registro por separado. Tal vez tenga algunas preocupaciones transversales de registro (registro de rendimiento).

Si no utiliza el registro basado en el nombre de clase, considere nombrar sus registradores en algún tipo de estructura jerárquica (tal vez por área funcional) para que pueda mantener una mayor flexibilidad en su configuración. Por ejemplo, puede tener un área funcional de "base de datos", un FA de "análisis" y un FA "ui". Cada uno de estos puede tener subáreas. Por lo tanto, puede solicitar registradores como este:

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

Y así. Con los registradores jerárquicos, puede configurar el registro globalmente (el "*" o registrador raíz), por FA (Base de datos, Análisis, UI) o por subárea (Database.Connect, etc.).

Los registradores tienen muchas opciones de configuración:

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

Consulte la ayuda de NLog para obtener más información sobre exactamente lo que significa cada una de las opciones. Probablemente, los elementos más notables aquí son la capacidad de comodín de las reglas de registro, el concepto de que varias reglas de registro pueden "ejecutarse" para una sola declaración de registro, y que una regla de registro puede marcarse como "final" para que las reglas posteriores no se ejecuten para un declaración de registro dada.

Use GlobalDiagnosticContext, MappedDiagnosticContext y NestedDiagnosticContext para agregar contexto adicional a su salida.

Use "variable" en su archivo de configuración para simplificar. Por ejemplo, puede definir variables para sus diseños y luego hacer referencia a la variable en la configuración de destino en lugar de especificar el diseño directamente.

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

O bien, puede crear un conjunto de propiedades "personalizadas" para agregar a un diseño.

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

O bien, puede hacer cosas como crear representadores de diseño "día" o "mes" estrictamente a través de la configuración:

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

También puede usar renders de diseño para definir su nombre de archivo:

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

Si enrolla su archivo diariamente, cada archivo podría llamarse "Monday.log", "Tuesday.log", etc.

No tenga miedo de escribir su propio renderizador de diseño. Es fácil y le permite agregar su propia información de contexto al archivo de registro a través de la configuración. Por ejemplo, aquí hay un renderizador de diseño (basado en NLog 1.x, no 2.0) que puede agregar 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;
    }
  }

Dígale a NLog dónde están sus extensiones de NLog (qué ensamblaje) de esta manera:

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

Utilice el renderizador de diseño personalizado de esta manera:

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

Use objetivos asíncronos:

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

Y envoltorios de destino predeterminados:

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

donde corresponda. Consulte los documentos de NLog para obtener más información al respecto.

Dígale a NLog que mire y vuelva a cargar automáticamente la configuración si cambia:

<nlog autoReload="true" /> 

Hay varias opciones de configuración para ayudar a solucionar problemas de NLog

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

Consulte la Ayuda de NLog para obtener más información.

NLog 2.0 agrega envoltorios LayoutRenderer que permiten que se realice un procesamiento adicional en la salida de un renderizador de diseño (como recortar espacios en blanco, mayúsculas, minúsculas, etc.).

No tenga miedo de envolver el registrador si desea aislar su código de una dependencia dura de NLog, pero ajústelo correctamente. Hay ejemplos de cómo envolver en el repositorio github de NLog. Otra razón para ajustar puede ser que desee agregar automáticamente información de contexto específica a cada mensaje registrado (poniéndolo en LogEventInfo.Context).

Existen ventajas y desventajas para envolver (o abstraer) NLog (o cualquier otro marco de registro para el caso). Con un poco de esfuerzo, puede encontrar mucha información aquí sobre SO presentando ambos lados.

Si está considerando envolver, considere usar Common.Logging . Funciona bastante bien y le permite cambiar fácilmente a otro marco de registro si lo desea. Además, si está considerando ajustar, piense cómo manejará los objetos de contexto (GDC, MDC, NDC). Common.Logging actualmente no admite una abstracción para ellos, pero supuestamente está en la cola de capacidades para agregar.

Salaroghe
fuente
3
Gran respuesta. Solo una cosa para agregar, $ {machine} debería ser $ {machinename}. Ver github.com/nlog/NLog/wiki/Layout-Renderers .
liang
2
Bifurqué Common.Logging y agregué la abstracción que faltaba, vea el proyecto GitHub o NuGet .
Danny Varod
No pude encontrar nada informativo sobre nlog en su propia documentación, ¿tal vez estoy mirando los ejemplos de github de manera incorrecta? Quién sabe.
JARRRRG
¿Cómo usar ese renderizador personalizado con la API (sin archivo de configuración)? Esto es lo que estoy tratando de lograr.
InteXX
Ok lo tengo. El NewLinediseño cumple la tarea. Esto es lo que se me ocurrió. Es mucho más simple de lo que esperaba.
InteXX
65

Tratar las excepciones de manera diferente

A menudo queremos obtener más información cuando hay una excepción. La siguiente configuración tiene dos objetivos, un archivo y la consola, que filtran si hay o no información de excepción. (EDITAR: Jarek ha publicado sobre un nuevo método para hacer esto en vNext ).

La clave es tener un objetivo de envoltura 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>
Palmadita
fuente
1
Eso es bastante bueno con el objetivo separado y FilteringWrapper para formatear la excepción. Acabo de responder una pregunta de un chico que quería incluir el renderizador de diseño de {excepción} en su salida pero no quería obtener el () que aparentemente se registra si NO hay una excepción. Esta técnica probablemente funcionaría bien para él.
Salaroghe
+1 Muy bien. He marcado este marcador durante mucho tiempo, y me remitieron al "comentario de Pat" de otra pregunta de SO con respecto a un diseño condicional.
eduncan911
1
Si se registra una excepción, se registrará dos veces (parte de VerboseLayout).
Tien Do
2
Lo probé mañana en mi proyecto, ya que estableces una regla minlevel = "Warn" en "file, fileAsException", todos los registros se registrarán primero con el destino del archivo (sin filtro), y si es una excepción (según lo filtrado por condición) también se registrará con fileAsException.
Tien Do
3
@Tiendq Oh, ya veo. Eso tiene sentido, aunque la excepción en sí (en detalle) solo se registrará una vez (pero su mensaje se registrará dos veces). Probablemente pueda solucionarlo agregando condition="length('${exception}')=0(o tal vez sea ==) a target name="file".
Pat
60

Aparentemente, ahora puedes usar NLog con Growl para 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 para Windows Mensaje de seguimiento de NLog con Growl para Windows Mensaje de depuración de NLog con Growl para Windows Mensaje de información de registro con Growl para Windows Mensaje de advertencia de NLog con Growl para Windows Mensaje de error NLog con Growl para Windows Mensaje fatal de NLog con Growl para Windows

Çağdaş Tekin
fuente
¿Me puede decir qué hacer para volver a conectar la conexión? La cosa funciona para mí para localhost, pero cuando he dado alguna dirección IP en el host, ¡no funciona!
Neel
@Neel, debe verificar la configuración de "Seguridad" en Growl en la computadora de destino. Debe habilitar explícitamente las notificaciones de "LAN" y es posible que desee configurar una contraseña (que luego deberá agregar a su destino NLog). Pero no me gustó que las notificaciones remotas aparecieran en Growl con un "Origen" de "Máquina local"; Tendría que agregar el host a las entradas de registro para saber dónde se originaron las notificaciones.
Kenny Evitt
Puedo obtener las notificaciones para trabajar en mi máquina local, pero no de forma remota. Mi configuración de seguridad no tiene contraseña en el gruñido, por lo que todo lo que agregué fue la IP y el puerto. Pero no se envía nada.
Jack Reilly
1
Este proyecto está muerto al 100%
Desarrollador
28

Configure NLog a través de XML, pero mediante programación

¿Qué? ¿Sabía que puede especificar el XML NLog directamente a NLog desde su aplicación, en lugar de que NLog lo lea desde el archivo de configuración? Bien tu puedes. Supongamos que tiene una aplicación distribuida y desea utilizar la misma configuración en todas partes. Podría mantener un archivo de configuración en cada ubicación y mantenerlo por separado, podría mantener uno en una ubicación central y llevarlo a las ubicaciones de satélite, o probablemente podría hacer muchas otras cosas. O bien, puede almacenar su XML en una base de datos, obtenerlo al inicio de la aplicación y configurar NLog directamente con ese XML (tal vez revise periódicamente para ver si ha cambiado).

  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

No estoy seguro de cuán robusto es esto, pero este ejemplo proporciona un punto de partida útil para las personas que quieran intentar una configuración como esta.

Salaroghe
fuente
funciona muy bien ... excepto que al usar esto ya no es posible volver a configurar dinámicamente el sistema de registro. Esto es especialmente cierto si se vincula a un archivo externo (incluir)
Newtopian
2
Esto funcionó, aunque tuve que escribir XML "bueno" incluyendo:<?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
Este es un buen segway en configuración centralizada. Lectores futuros, xml codificado en este ejemplo es solo para demostración (en mi humilde opinión), leerlo desde una base de datos o archivo centralizado podría ser la implementación real.
granadaCoder 01 de
@wageoghe; ¿Por qué recibo un error (el registrador no existe)? Acabo de copiar y pegar el código
Bsflasher
22

Registro de diferentes niveles dependiendo de si hay o no un error

Este ejemplo le permite obtener más información cuando hay un error en su código. Básicamente, almacena los mensajes en el búfer y solo los envía a un cierto nivel de registro (por ejemplo, Advertir) a menos que se cumpla una determinada condición (por ejemplo, ha habido un error, por lo que el nivel de registro es> = Error), luego generará más información (por ejemplo, todos los mensajes de niveles de registro> = Rastreo). Como los mensajes están almacenados en el búfer, esto le permite recopilar información de rastreo sobre lo que sucedió antes se registrara un Error o ErrorException, ¡muy útil!

Adapté este de un ejemplo en el código fuente . Al principio me arrojaron porque dejé de lado AspNetBufferingWrapper(ya que la mía no es una aplicación ASP); resulta que PostFilteringWrapper requiere un objetivo almacenado en búfer. Tenga en cuenta que el target-refelemento utilizado en el ejemplo vinculado anteriormente no se puede utilizar en NLog 1.0 (estoy utilizando 1.0 Actualizar para una aplicación .NET 4.0); Es necesario poner su objetivo dentro del bloque de envoltura. También tenga en cuenta que la sintaxis lógica (es decir, mayor o menor que los símbolos, <y>) tiene que usar los símbolos, no los escapes XML para esos símbolos (es decir, &gt;y&lt; ) o de lo contrario NLog producirá un error.

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>
Palmadita
fuente
En algunas versiones de NLog (para mono y creo que 2.0), esto provoca una excepción StackOverflowException, pero no en otras (actualización NLog 1).
Pat
Con respecto al desbordamiento: parece deberse solo al diseño del tipo CSV; si hago un diseño regular, no hay problema.
Pat
¿Para qué sirve el fileAsCsv target-ref? Estoy tratando de hacer que este ejemplo funcione contra NLog v2.0.0.2000 pero hasta ahora estoy fallando.
Peter Mounce
@PeterMounce El fileAsCsvtarget-ref es solo un artefacto de mis pruebas. Creo que NLog 2 tiene / tuvo problemas con CsvLayouts que NLog 1 / Refresh no tenía.
Pat
22

Proporcioné un par de respuestas razonablemente interesantes a esta pregunta:

Nlog: sección de generación de encabezado para un archivo de registro

Agregar un encabezado:

La pregunta quería saber cómo agregar un encabezado al archivo de registro. El uso de entradas de configuración como esta le permite definir el formato del encabezado por separado del formato del resto de las entradas del registro. Use un único registrador, quizás llamado "encabezado registrador" para registrar un solo mensaje al inicio de la aplicación y obtendrá su encabezado:

Defina el encabezado y los diseños de archivo:

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

Defina los objetivos utilizando los diseños:

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

Definir los registradores:

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

Escriba el encabezado, probablemente al principio del programa:

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

Esta es en gran parte una versión más de la idea "Tratar las excepciones de manera diferente".

Registre cada nivel de registro con un diseño diferente

Del mismo modo, el póster quería saber cómo cambiar el formato por nivel de registro. No estaba claro para mí cuál era el objetivo final (y si se podía lograr de una manera "mejor"), pero pude proporcionar una configuración que hizo lo que pidió:

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

Nuevamente, muy similar a Tratar las excepciones de manera diferente .

Salaroghe
fuente
1
¡Frio! No había visto el GlobalDiagnosticsContextantes.
Pat
10

Inicia sesión en Twitter

Basado en esta publicación sobre un Log4net Twitter Appender, Pensé que intentaría escribir un destino NLog Twitter (usando la actualización NLog 1.0, no 2.0). Por desgracia, hasta ahora no he podido obtener un Tweet para publicar realmente con éxito. No sé si hay algo mal en mi código, Twitter, la conexión a Internet / firewall de nuestra empresa, o qué. Estoy publicando el código aquí en caso de que alguien esté interesado en probarlo. Tenga en cuenta que hay tres métodos diferentes de "Publicación". El primero que probé es PostMessageToTwitter. PostMessageToTwitter es esencialmente lo mismo que PostLoggingEvent en la publicación original. Si uso eso, obtengo una excepción 401. PostMessageBasic obtiene la misma excepción. PostMessage se ejecuta sin errores, pero el mensaje aún no está a la altura de Twitter. PostMessage y PostMessageBasic se basan en ejemplos que encontré aquí en SO.

FYI - Acabo de encontrar un comentario de @Jason Diller a una respuesta en esta publicación que dice que Twitter va a desactivar la autenticación básica "el próximo mes". Esto fue en mayo de 2010 y ahora es diciembre de 2010, así que supongo que podría ser la razón por la que esto no funciona.

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

Configurarlo así:

Dígale a NLog el ensamblado que contiene el objetivo:

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

Configure el objetivo:

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

Si alguien prueba esto y tiene éxito, regrese con sus hallazgos.

Salaroghe
fuente
Twitter usa OAuth - .NET tiene un proveedor en dotnetopenauth.net
Pat
8

Manera más fácil de registrar cada nivel de registro con un diseño diferente usando diseños condicionales

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

Ver https://github.com/NLog/NLog/wiki/When-Filter para la sintaxis

Lukie
fuente
7

Informar a un sitio web / base de datos externo

Quería una manera simple y automática de informar errores (ya que los usuarios a menudo no lo hacen) desde nuestras aplicaciones. La solución más fácil que se me ocurrió fue una URL pública, una página web que podría recibir información y almacenarla en una base de datos, que se envía datos ante un error de la aplicación. (La base de datos podría ser revisada por un desarrollador o un script para saber si hay nuevos errores).

Escribí la página web en PHP y creé una base de datos mysql, usuario y tabla para almacenar los datos. Me decidí por cuatro variables de usuario, una identificación y una marca de tiempo. Las posibles variables (incluidas en la URL o como datos POST) son:

  • app (Nombre de la aplicación)
  • msg (mensaje: por ejemplo, se produjo una excepción ...)
  • dev (desarrollador - por ejemplo, Pat)
  • src(fuente: esto vendría de una variable perteneciente a la máquina en la que se estaba ejecutando la aplicación, por ejemplo, Environment.MachineNameo algo así)
  • log (un archivo de registro o mensaje detallado)

(Todas las variables son opcionales, pero no se informa nada si ninguna de ellas está configurada; por lo tanto, si visita la URL del sitio web, no se envía nada a la base de datos).

Para enviar los datos a la URL, utilicé el WebServiceobjetivo de NLog . (Tenga en cuenta que tuve algunos problemas con este objetivo al principio. No fue hasta que miré la fuente que me di cuenta de que mi urlno podía terminar con a /).

En general, no es un mal sistema para controlar pestañas en aplicaciones externas. (Por supuesto, lo educado es informar a sus usuarios que informará sobre datos posiblemente confidenciales y darles una forma de optar por la entrada / salida).

Cosas de MySQL

(El usuario db solo tiene INSERTprivilegios en esta tabla en su propia base de datos).

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'

Código del sitio web

(PHP 5.3 o 5.2 con PDO habilitado , el archivo está index.phpen la /reportcarpeta)

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

Código de aplicación (archivo de configuración 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: puede haber algunos problemas con el tamaño del archivo de registro, pero no he descubierto una forma simple de truncarlo (por ejemplo, el tailcomando de la * nix ).

Palmadita
fuente
Esto funcionó para un proyecto, pero en otros tuve problemas con url: InnerException: System.InvalidCastException Message = Conversión no válida de 'System.String' a 'System.Uri'. Fuente = mscorlib StackTrace: en System.Convert.DefaultToType (valor IConvertible, tipo targetType, proveedor IFormatProvider) en System.String.System.IConvertible.ToType (tipo de tipo, proveedor IFormatProvider) en System.Convert.ChangeType (valor de objeto, tipo conversionType , Proveedor IFormatProvider)
Pat
Otra opción si desea poder monitorear el registro y recibir una notificación en caso de error sería un Objetivo de Twitter. Vea este enlace para un Appender de Twitter escrito para log4net: twitterappender.codeplex.com La publicación original del blog que discute esto está aquí: caseywatson.com/2009/07/07/log4net-twitter-awesome Debería ser bastante fácil escribir algo similar para NLog.
Salaroghe
Me he engañado con escribir un NLog TwitterTarget pero no he tenido éxito al publicar un Tweet. He publicado el código como respuesta. Siéntase libre de probarlo si lo desea.
Salaroghe
4

Iniciar sesión desde Silverlight

Al usar NLog con Silverlight, puede enviar el seguimiento al lado del servidor a través del servicio web proporcionado . También puede escribir en un archivo local en el Almacenamiento aislado, que es útil si el servidor web no está disponible. Vea aquí para más detalles, es decir, use algo como esto para convertirse en un objetivo:

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

        public IsolatedStorageTarget()
        {
        }

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

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

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

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