2011-01-28 5 views
9

Estoy utilizando NLog para iniciar sesión en una aplicación ASP.Net y hacer uso del objetivo de la base de datos con el servidor Sql de Microsoft.NLog: escriba NULL en la columna de base de datos opcional

Tengo algunos parámetros de registro que son opcionales y no siempre especificados. Me gustaría que se escriban como nulos cuando no se proporcionan, sin embargo, NLog parece que siempre los escribe como cadenas vacías.

¿Hay alguna manera de configurarlo para escribir nulo como valor predeterminado?

Ref: https://github.com/nlog/NLog/wiki/Database-target

Respuesta

2

[EDIT]

Tal vez una solución más evidente que lo que propongo a continuación sería cambiar el uso de una instrucción INSERT a la utilización de un procedimiento de base de datos para iniciar la sesión. Si utiliza un procedimiento de base de datos, entonces puede manejar el swizzling de cadena vacía para anular usted mismo. No estoy seguro de que pueda usar un procedimiento de base de datos con el objetivo de base de datos de NLog. Log4net lo admite, así que supongo que NLog también lo hace.

Aquí hay un ejemplo (en la respuesta a la pregunta relacionada) que encontré de la configuración de alguien que usaba NLog para iniciar sesión en una base de datos usando el procedimiento almacenado.

http://nlog-forum.1685105.n2.nabble.com/Using-a-stored-procedure-for-the-DB-Target-td2621725.html

que ver aquí:

http://nlog.codeplex.com/workitem/5418

Una queja que no funciona (al menos en la beta Nlog 2.0).

Una diferencia entre los dos ejemplos es que el ejemplo de trabajo utiliza "LoggingProcedureName ejecutivo ...", mientras que el que no trabaja utiliza "LoggingProcedureName ..."

Espero que esto ayude.

[Edit End]

no puedo comentar por qué Nlog escribe cadenas emptry en lugar de nulo o cómo hacer Nlog nula escritura en lugar de cadenas vacías, pero me pregunto si usted podría hacer este trabajo que la forma que querer a través de configuración adicional?

¿Cuándo son opcionales los parámetros de registro? ¿Hay ciertos lugares en su código donde SIEMPRE registra algunos valores y otros lugares donde NUNCA registra algunos valores? ¿Puede usted (como desarrollador) saber qué parámetros opcionales son aplicables en qué secciones de su aplicación?

¿Podría configurar múltiples destinos de base de datos, cada uno con los parámetros "correctos" especificados? A continuación, puede apuntar a los registradores al destino específico de la base de datos que sea apropiado para la ubicación del código.

Supongamos que su aplicación está dividida (por espacio de nombres) en código que (generalmente) ejecuta "antes", "durante" y "después".

En el código "anterior", es posible que esté registrando el parámetro A. En el código "during", es posible que esté registrando el parámetro B. En el código "after", es posible que esté registrando el parámetro C.Por lo tanto, su mesa de registro podría tener columnas como:

DateTime, Logger, LogLevel, A, B, C, Message, Exception 

Ahora usted tienen un destino de base de datos que inserta todos esos valores para cada declaración de registro.

¿Qué pasa si usted tiene tres objetivos de bases de datos que se insertan los valores de este modo:

DataTime, Logger, LogLevel, A, Message, Exception 
DataTime, Logger, LogLevel, B, Message, Exception 
DataTime, Logger, LogLevel, C, Message, Exception 

Se puede configurar la sección de algo como esto:

<rules> 
    <logger name="Before.*" minlevel="Trace" writeTo="databaseA" /> 
    <logger name="During.*" minlevel="Trace" writeTo="databaseB" /> 
    <logger name="After.*" minlevel="Trace" writeTo="databaseC" /> 
    </rules> 

Obviamente, podría haber varios problemas con este idea:

  1. Es posible (o fácil) dividir su logge rs hasta coincidir con la "opcionalidad" de los parámetros.

  2. Puede haber demasiadas combinaciones de parámetros opcionales para que esto sea factible (probablemente el mismo inconveniente que 1).

  3. Puede que no sea una buena idea tener un registro de objetivos de base de datos activos al mismo tiempo. Tal vez esto llevaría a problemas de rendimiento.

Bueno, eso es todo lo que tengo. No sé si mi idea funcionaría, y mucho menos si es práctica.

Probablemente, la mejor solución sería que NLog permitiera una propiedad adicional en cada parámetro de base de datos que le permitiera decir "enviar nulo en lugar de cadena vacía".

Supongo que debería sugerir que también desee formular esta pregunta en el NLog forum. "Craig" en ese foro hizo la misma pregunta (o similar) el día de hoy. Tal vez eres Craig.

+0

Soy el usuario de Craig en los foros. Gracias por las ideas, ya estoy usando un procedimiento almacenado para mis objetivos de base de datos de nlog, por lo que es probable que busque las cadenas vacías en los parámetros entrantes y escriba nulo, solo buscaba las alternativas que me ha proporcionado, gracias para las opciones. – cweston

6

NLog usa StringBuilder para hacer el valor del parámetro. Incluso si no se especifica un parámetro, inicializa un valor como builder.ToString() que es una cadena vacía.

Puede cambiar sus CommandText así:

INSERT INTO [dbo].[log] ([message], [optional]) 
VALUES 
(
    @message, 
    case 
     when len(@optional) = 0 then null 
     else @optional 
    end 
) 

Parece como un truco para mí, sin embargo. Espero que haya una mejor solución.

+0

Gracias por esto, utilizaré un enfoque similar dentro de mi procedimiento almacenado para filtrar la cadena vacía a nula, aunque estoy de acuerdo en que es una solución alternativa. Esperemos que implementen esta funcionalidad en el futuro. (+1 para la sugerencia). – cweston

+0

Sí, es un poco raro, pero funciona. Gracias :) –

11

Esta es una vieja pregunta pero debido a las soluciones dadas son un poco 'hacky' Quería dar las mías que considero que es mucho más simple de implementar que un procedimiento de db y más elegante que usar una funda .

Puede intentar escribir el NULL con la función NULLIF que compara 2 expresiones y devuelve NULL si son iguales, de lo contrario, devuelve la primera expresión (msdn NULLIF page).

De esta manera el CommandText en su archivo de configuración Nlog se vería así:

INSERT INTO [dbo].[log] ([message], [optional]) 
VALUES (@message, NULLIF(@optional, '')) 
+0

+1 He estado usando el enfoque 'CASE WHEN BLAH BLAH', pero' NULLIF' es MUCHO mejor. ¡Gracias! – Nick

0

utilizo un enfoque ligeramente diferente.


Debido a que no me gusta escribir consultas que crea una extensión que hace esto para mí y mi configuración Nlog parece:

<target xsi:type="Database" name="Log" commandText="[dbo].[Log]" dbProvider="System.Data.SqlClient" connectionString=".."> 
    <parameter name="@Timestamp" layout="${longdate:universalTime=true}" /> 
    <parameter name="@LogLevel" layout="${level:uppercase=true}" /> 
    <parameter name="@Logger" layout="${logger}" /> 
    <parameter name="@Message" layout="${message}" /> 
    <parameter name="@Exception:null" layout="${onexception:${exceptionLayout}}" /> 
</target> 

Aviso dos cosas aquí:

  • la commandText="[dbo].[Log]" que debe seguir el formato [Schema].[Table]
  • @Exception:null donde null significa que es anulable

No hay ningún elemento <commandText> pero en su lugar utilizar esta extensión para crear la INSERT de forma automática a partir de los parámetros.

public static class NLogExtensions 
{ 
    // The commandText attribute must conatain at least the table name. 
    // Each identifier must be enclosed in square brackets: [schemaName].[tableName]. 

    public static void GenerateDatabaseTargetInsertQueries(this NLog.Config.LoggingConfiguration config) 
    { 
     var tableNameMatcher = new Regex(@"^(\[(?<schemaName>.+?)\].)?\[(?<tableName>.+?)\]$"); 

     var autoCommandTextDatabaseTargets = 
      config.AllTargets 
       .OfType<DatabaseTarget>() 
       .Where(x => tableNameMatcher.IsMatch(x.CommandText())) 
       .Select(x => x); 

     foreach (var databaseTarget in autoCommandTextDatabaseTargets) 
     { 
      databaseTarget.CommandText = databaseTarget.CreateCommandText(); 
     } 
    } 

    internal static string CommandText(this DatabaseTarget databaseTarget) 
    { 
     return ((NLog.Layouts.SimpleLayout)databaseTarget.CommandText).OriginalText; 
    } 

    internal static string CreateCommandText(this DatabaseTarget databaseTarget) 
    { 
     const string insertQueryTemplate = "INSERT INTO {0}({1}) VALUES({2})"; 

     return string.Format(
       insertQueryTemplate, 
       databaseTarget.CommandText(), 
       string.Join(", ", databaseTarget.Parameters.Select(x => x.Name())), 
       string.Join(", ", databaseTarget.Parameters.Select(x => 
       { 
        var sql = 
         x.Nullable() 
         ? string.Format("NULLIF({0}, '')", x.FullName()) 
         : x.FullName(); 

        // Rename the SqlParameter because otherwise SqlCommand will complain about it. 
        x.Name = x.FullName(); 

        return sql; 
       }))); 
    } 
} 

y

public static class NLogDatabaseTarget 
{ 
    public static void GenerateInsertQueries() 
    { 
     NLog.LogManager.Configuration.GenerateDatabaseTargetInsertQueries(); 
    } 
} 

Adicionalmente se puede analizar el nombre del parámetro y insertar el NULLIF para los parámetros anulables.

Otra de las posibilidades me ayuda a analizarlo:

public static class DatabaseParameterInfoExtensions 
{ 
    // https://regex101.com/r/wgoA3q/2 

    private static readonly Regex ParamRegex = new Regex("^(?<prefix>.)(?<name>[a-z0-9_\-]+)(?:[:](?<null>null))?", RegexOptions.IgnoreCase); 

    public static string Prefix(this DatabaseParameterInfo parameter) 
    { 
     return ParamRegex.Match(parameter.Name).Groups["prefix"].Value; 
    } 

    public static string Name(this DatabaseParameterInfo parameter) 
    { 
     return ParamRegex.Match(parameter.Name).Groups["name"].Value; 
    } 

    public static string FullName(this DatabaseParameterInfo parameter) 
    { 
     return string.Format("{0}{1}", parameter.Prefix(), parameter.Name()); 
    } 

    public static bool Nullable(this DatabaseParameterInfo parameter) 
    { 
     return ParamRegex.Match(parameter.Name).Groups["null"].Success; 
    } 
} 

Esto significa que necesita agregar el atributo commandText="[dbo].[Log]" a la meta base de datos, eliminar la consulta y añadir el :null al nombre del parámetro de columnas anulables.

En el código que acaba de llamar a esto y las extensiones harán la magia.

NLogDatabaseTarget.GenerateInsertQueries(); 
Cuestiones relacionadas