.net - started - nlog visual studio
Configuraciones de NLog más útiles (10)
Informar a un sitio web externo / base de datos
Quería una forma sencilla y automática de reportar errores (ya que los usuarios a menudo no lo hacen) de nuestras aplicaciones. La solución más fácil que pude encontrar fue una URL pública, una página web que pudiera recibir información y almacenarla en una base de datos, que se envía a los datos cuando se produce un error en la aplicación. (La base de datos podría ser verificada 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, usuario y tabla de mysql 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: provendría de una variable perteneciente a la máquina en la que se ejecutaba 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 no se configura ninguna de ellas, por lo que si solo visita la URL del sitio web, no se envía nada a la db).
Para enviar los datos a la URL, utilicé el objetivo WebService
de NLog. (Tenga en cuenta que al principio tuve algunos problemas con este objetivo. No fue hasta que miré la fuente que descubrí que mi url
no podía terminar con una /
).
Con todo, no es un mal sistema para controlar las aplicaciones externas. (Por supuesto, lo correcto es informar a sus usuarios de que informará sobre datos posiblemente confidenciales y darles una forma de habilitarlos).
Cosas de MySQL
(El usuario db solo tiene privilegios INSERT
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 enabled , el archivo es index.php
en la carpeta /report
)
<?php
$app = $_REQUEST[''app''];
$msg = $_REQUEST[''msg''];
$dev = $_REQUEST[''dev''];
$src = $_REQUEST[''src''];
$log = $_REQUEST[''log''];
$dbData =
array( '':app'' => $app,
'':msg'' => $msg,
'':dev'' => $dev,
'':src'' => $src,
'':log'' => $log
);
//print_r($dbData); // For debugging only! This could allow XSS attacks.
if(isEmpty($dbData)) die("No data provided");
try {
$db = new PDO("mysql:host=$host;dbname=reporting", "reporter", $pass, array(
PDO::ATTR_PERSISTENT => true
));
$s = $db->prepare("INSERT INTO reporting.reports
(
applicationName,
message,
developer,
source,
logData
)
VALUES
(
:app,
:msg,
:dev,
:src,
:log
);"
);
$s->execute($dbData);
print "Added report to database";
} catch (PDOException $e) {
// Sensitive information can be displayed if this exception isn''t handled
//print "Error!: " . $e->getMessage() . "<br/>";
die("PDO error");
}
function isEmpty($array = array()) {
foreach ($array as $element) {
if (!empty($element)) {
return false;
}
}
return true;
}
?>
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 sencilla de truncarlo (por ejemplo, un tail
comando de la * nix ).
¿Cuáles son las mejores o más útiles configuraciones para el registro 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 tamaño determinado, cambiar el diseño (mensaje de registro) si existe o no una excepción, escalar el nivel de registro una vez que se produce un error, etc.
Aquí hay algunos enlaces:
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 y solo los envía a un determinado 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> = Trace). Debido a que los mensajes están almacenados en búfer, esto le permite recopilar información de rastreo sobre lo que sucedió antes de que se registrara un Error o ErrorException, ¡muy útil!
Adapté este de un ejemplo en el código fuente . Al principio me lanzaron porque dejé de lado el AspNetBufferingWrapper
(ya que el mío no es una aplicación ASP). Resulta que el PostFilteringWrapper requiere algún objetivo con búfer. Tenga en cuenta que el elemento target-ref
utilizado en el ejemplo vinculado anteriormente no se puede usar en NLog 1.0 (estoy usando 1.0 Refresh para una aplicación .NET 4.0); Es necesario poner tu objetivo dentro del bloque contenedor. También tenga en cuenta que la sintaxis lógica (es decir, los símbolos mayor que o menor que, <y>) tiene que usar los símbolos, no los escapes XML para esos símbolos (es decir, >
y <
) 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>
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 objetivos, un archivo y la consola, que se filtran para determinar si existe 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 destino envoltorio 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>
Algunos de estos pertenecen a la categoría de sugerencias generales de NLog (o registro) en lugar de sugerencias de configuración estrictamente.
Aquí hay algunos enlaces generales de registro desde aquí en SO (es posible que ya hayas visto algunos o todos estos):
¿Cuál es el punto de una fachada de registro?
¿Por qué los registradores 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 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 específico del registrador, etc.).
Utilice registradores no basados en nombres de clase cuando sea apropiado. Quizás tenga una función para la que realmente desea controlar el registro por separado. Tal vez usted tenga algunas preocupaciones sobre el registro transversal (registro de rendimiento).
Si no usa el registro basado en el nombre de clase, considere nombrar a sus registradores en algún tipo de estructura jerárquica (quizás 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 de "ui". Cada uno de estos podría tener subáreas. Por lo tanto, puede solicitar madereros 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 el registrador 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:
<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 qué significa exactamente cada una de las opciones. Probablemente, los elementos más notables aquí son la capacidad de las reglas del registrador de caracteres comodín, el concepto de que varias reglas del registrador pueden "ejecutarse" para una sola declaración de registro, y que una regla del registrador puede marcarse como "final", por lo que las reglas subsiguientes no se ejecutarán para una dada la declaración de registro.
Use GlobalDiagnosticContext, MappedDiagnosticContext y NestedDiagnosticContext para agregar un contexto adicional a su salida.
Utilice "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 "personalizado" de propiedades 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 renderizadores 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 utilizar presentaciones 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 lanza 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 las extensiones de NLog (qué ensamblaje):
<extensions>
<add assembly="MyNLogExtensions"/>
</extensions>
Utilice el renderizador de diseño personalizado como este:
<variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>
Usa objetivos asíncronos:
<nlog>
<targets async="true">
<!-- all targets in this section will automatically be asynchronous -->
</targets>
</nlog>
Y envoltorios de destino por defecto:
<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 sobre ellos.
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 de LayoutRenderer que permiten realizar 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 fuerte en NLog, pero ajuste correctamente. Hay ejemplos de cómo envolver en el repositorio github de NLog. Otra razón para envolver puede ser que desee agregar automáticamente información de contexto específica a cada mensaje registrado (poniéndolo en LogEventInfo.Context).
Hay 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 la presentación de 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 desea hacerlo. Además, si está pensando en envolver, piense en cómo manejará los objetos de contexto (GDC, MDC, NDC). Common.Logging no admite actualmente una abstracción para ellos, pero se supone que está en la cola de capacidades para agregar.
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>
Proporcioné un par de respuestas razonablemente interesantes a esta pregunta:
Nlog - Sección de encabezado de generación para un archivo de registro
Añadiendo 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 solo registrador, quizás llamado "headerlogger" para registrar un solo mensaje al inicio de la aplicación y obtendrá su encabezado:
Definir los diseños de encabezado y 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}" />
Define 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 madereros:
<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");
Esto es en gran medida solo una versión más de la idea "Tratar las excepciones de manera diferente".
Registrar cada nivel de registro con un diseño diferente
Del mismo modo, el cartel quería saber cómo cambiar el formato por nivel de registro. No tenía claro cuál era el objetivo final (y si se podría lograr de una manera "mejor"), pero pude proporcionar una configuración que hizo lo que él 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>
Una vez más, muy similar a tratar las excepciones de manera diferente .
Configurar NLog a través de XML, pero programáticamente
¿Qué? ¿Sabía que puede especificar el NLog XML directamente a NLog desde su aplicación, en lugar de que NLog lo lea desde el archivo de configuración? Bien tu puedes. Digamos que tienes una aplicación distribuida y quieres usar 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 enviarlo a las ubicaciones satelitales, 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 revisar 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 configurar de esta manera.
Iniciar sesión desde Silverlight
Al usar NLog con Silverlight, puede enviar el rastreo al servidor a través del servicio web provided . También puede escribir en un archivo local en el almacenamiento aislado, que es útil si el servidor web no está disponible. Consulte here para obtener 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);
}
}
}
}
}
}
Iniciar sesión en Twitter
Basándome en este post sobre un log4net Twitter Appender , pensé que intentaría escribir un objetivo de NLog Twitter (utilizando la actualización de NLog 1.0, no 2.0). Lamentablemente, hasta ahora no he podido obtener un Tweet para publicar con éxito. No sé si hay algún error 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 "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 llega a 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 por eso 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) + "...";
}
}
}
Configúralo así:
Dígale a NLog el ensamblaje que contiene el objetivo:
<extensions>
<add assembly="NLogExtensions"/>
</extensions>
Configurar 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, vuelva a publicar con sus hallazgos.
Manera más fácil de registrar cada nivel de registro con un diseño diferente utilizando 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