Las configuraciones NLog más útiles

¿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 sobre los archivos de registro en un determinado tamaño, cambiar el diseño (mensaje de registro) si hay una excepción o no, escalar el nivel de registro una vez que se ha producido un error, etc.

Aquí hay algunos enlaces:

  • Demostración NLog
  • Ejemplos en la fuente

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

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

log4net vs. Nlog

Mejores prácticas de registro

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

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

Use el patrón común de nombrar su registrador basado en la clase Logger logger = LogManager.GetCurrentClassLogger() . Esto le da un alto grado de granularidad en sus registradores y le da 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 no basados ​​en el nombre de clase cuando corresponda. Tal vez tiene una función para la cual realmente desea controlar el registro por separado. Tal vez tenga algunas preocupaciones de registro cruzadas (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 podría 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 de forma global (el “*” o registrador de raíz), por FA (Base de datos, Análisis, UI) o por subárea (Base de datos.Conexión, etc.).

Los madereros tienen muchas opciones de configuración:

     

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í sean la capacidad de comodín de reglas de registrador, el concepto de que varias reglas de registrador pueden “ejecutarse” para una sola statement de registro, y que una regla de registrador puede marcarse como “final” para que las reglas subsiguientes no se ejecuten para un statement de registro dada.

Utilice 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 del objective en lugar de especificar el diseño directamente.

        

O bien, podría crear un conjunto de propiedades “personalizadas” para agregar a un diseño.

     

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

        

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

      

Si mueves tu archivo diariamente, cada archivo podría llamarse “Monday.log”, “Tuesday.log”, etc.

No tengas miedo de escribir tu 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 el 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; } } 

Dile a NLog dónde se encuentran tus extensiones de NLog (qué ensamblaje) de esta manera:

     

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

   

Usa objectives asincrónicos:

      

Y envoltorios de destino predeterminados:

                

donde corresponda. Consulte los documentos NLog para obtener más información sobre ellos.

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

  

Hay varias opciones de configuración para ayudar con la solución de problemas de NLog

      

Ver la Ayuda de NLog para 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 recorte de espacios en blanco, mayúsculas, minúsculas, etc.).

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

Existen pros y contras para envolver (o resumir) NLog (o cualquier otro marco de trabajo de registro para el caso). Con un poco de esfuerzo, puede encontrar mucha información aquí en 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 envolver, piense cómo manejará los objetos de contexto (GDC, MDC, NDC). Common.Logging no admite actualmente una abstracción para ellos, pero supuestamente está en la cola de capacidades para agregar.

Tratando las excepciones de manera diferente

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

La clave es tener un destino de contenedor con xsi:type="FilteringWrapper" condition="length('${exception}')>0"

                    

Aparentemente, ahora puedes usar NLog con Growl para Windows .

             

NLog con Growl para WindowsMensaje de rastreo NLog con Growl para WindowsMensaje de depuración NLog con Growl para WindowsMensaje de información NLog con Growl para WindowsNLog advierte mensaje con Growl para WindowsMensaje de error NLog con Growl para WindowsMensaje fatal de NLog con Growl para Windows

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

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

  string xml = @"       "; 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 = @"       "; 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 configurar de esta manera.

Registrando diferentes niveles dependiendo de si hay un error o no

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

Lo adapté de un ejemplo en el código fuente . Al principio me echaron porque AspNetBufferingWrapper (ya que la mía no es una aplicación ASP) – resulta que PostFilteringWrapper requiere algún objective de almacenamiento intermedio. Tenga en cuenta que el elemento target-ref utilizado en el ejemplo anterior vinculado no se puede usar en NLog 1.0 (estoy usando 1.0 Refresh para una aplicación .NET 4.0); es necesario poner su objective dentro del bloque contenedor. También tenga en cuenta que la syntax lógica (es decir, mayor que o menos que los símbolos, ) tiene que usar los símbolos, no el XML escapa para esos símbolos (es decir, > y < ) o de lo contrario NLog tendrá un error.

app.config:

    
level >= LogLevel.Warn

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

Nlog: generación de la secció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 rest de las entradas de registro. Utilice un solo registrador, tal vez llamado “selector de cabecera” para registrar un solo mensaje al inicio de la aplicación y obtendrá su encabezado:

Defina los diseños de encabezado y archivo:

    

Defina los objectives usando los diseños:

   

Definir los registradores:

     

Escriba el encabezado, probablemente al principio del progtwig:

  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 medida, solo otra versión 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 objective final (y si se podía lograr de una manera “mejor”), pero pude proporcionar una configuración que hiciera lo que él me pedía:

                                

De nuevo, muy similar a Tratar las excepciones de manera diferente .

Inicie sesión en Twitter

Basado en este post sobre un apéndice de Twitter log4net , pensé que podría intentar escribir un NLog Twitter Target (usando actualización de NLog 1.0, no 2.0). Por desgracia, hasta ahora no he podido obtener un Tweet para publicar correctamente. No sé si algo está mal en mi código, Twitter, la conexión a internet / firewall de nuestra compañía, 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 “Publicar”. 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á en 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 por eso que esto no está funcionando.

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

Configúrelo así:

Dile a NLog el conjunto que contiene el objective:

    

Configurar el objective:

    

Si alguien lo prueba y tiene éxito, publique de nuevo sus hallazgos.

Informes en un sitio web / base de datos externo

Quería una forma simple y automática de informar errores (ya que los usuarios a menudo no) de nuestras aplicaciones. La solución más fácil que pude encontrar fue una URL pública, una página web que podría tomar datos 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, un usuario y una tabla mysql para almacenar los datos. Decidí 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 – ej. Excepción ocurrió …)
  • 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.MachineName o algo similar)
  • 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 solo 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 objective de WebService de NLog. (Nota, tuve algunos problemas con este objective al principio. No fue hasta que miré en la fuente que descubrí que mi url no podía terminar con un / ).

En general, no es un mal sistema para controlar las aplicaciones externas. (Por supuesto, lo más educado que se debe hacer es informar a los usuarios que informarán datos posiblemente confidenciales y darles la posibilidad de participar).

Cosas de MySQL

(El usuario db solo tiene privilegios INSERT en esta única 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 es index.php en la carpeta /report )

  $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() . "
"; 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)

                     level >= LogLevel.Warn                  

Note: there may be some issues with the size of the log file, but I haven’t figured out a simple way to truncate it (eg a la *nix’s tail command ).

Easier Way To Log each log level with a different layout using Conditional Layouts

  

See https://github.com/NLog/NLog/wiki/When-Filter for syntax

Log from Silverlight

When using NLog with Silverlight you can send the trace to the server side via the provided web service. You can also write to a local file in the Isolated Storage, which come in handy if the web server is unavailable. See here for details, ie use something like this to make yourself a target:

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