2011-01-28 66 views

回答

2

[编辑]

也许比我下面提出什么是从使用INSERT语句使用数据库过程登录改变比较明显的解决方案。如果您使用数据库过程,那么您可以自行处理从空字符串变为空的字符串。我不确定你可以在NLog的数据库目标中使用数据库过程。 Log4net支持它,所以我的猜测是Nlog也是如此。

下面是一个例子(在链接问题的答案中)我发现了某人使用NLog使用存储过程登录数据库的配置。

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

我在这里看到:

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

,它不工作(至少在NLOG 2.0测试版)投诉。这两个例子之间

一个区别是,工作示例使用“EXEC LoggingProcedureName ......”,而非工作一个使用“LoggingProcedureName ......”

希望这有助于。

[编辑完]

我不能为什么写NLOG字符串emptry而不是null或如何使NLOG写空的,而不是空字符串发表评论,但我不知道,如果你能他们的方式,使这项工作你想通过额外的配置?

什么时候记录参数是可选的?你的代码中是否有某些地方总是记录一些值和其他你永远不记录某些值的地方?您(作为开发人员)能够知道哪些可选参数适用于您的应用程序的哪些部分?

您可以配置多个数据库目标,每个目标都指定了“正确”参数吗?然后,您可以将记录器指向适合代码位置的特定数据库目标。

假设您的应用程序按(通过名称空间)划分为(通常)在“之前”,“之间”和“之后”执行的代码。

在“before”代码中,您可能正在记录参数A.在“during”代码中,您可能正在记录参数B.在“after”代码中,您可能正在记录参数C.所以,你的记录表可能有这样的列:

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

现在你有插入所有这些值的每一个记录语句的数据库对象。

如果你有一个插入值,像这样三个数据库对象:

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

你可以配置你的部分是这样的:

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

显然,可能有几个问题与此想法:

  1. 它可能(或容易)分开你的logge rs以匹配参数的“可选性”。

  2. 可能有太多的可选参数的组合,以使其可行(可能与1相同的缺点)。

  3. 将数据库目标的日志一次激活可能不是一个好主意。也许这会导致性能问题。

好吧,就是我所拥有的。我不知道我的想法甚至可以工作,如果它是实用的,我的想法就更少。

也许更好的解决方案是让NLog允许每个数据库参数有一个额外的属性,允许您说“发送null而不是空字符串”。

我想我应该建议你也可以在NLog forum提问这个问题。该论坛上的“克雷格”在今天早些时候提出了相同的(或类似的)问题。也许你是克雷格。

+0

我是论坛上的Craig用户。感谢这些想法,我已经为我的nlog数据库目标使用了一个存储过程,因此测试传入参数上的空字符串并写入null可能是我将要去的方式,我只是在寻找您提供的替代方法,谢谢为选项。 – cweston 2011-01-30 21:31:49

6

NLog使用StringBuilder制作参数值。即使未指定参数,它也会将值初始化为builder.ToString(),它是空字符串。

你可以改变你的CommandText是这样的:

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

它虽然看起来像一个黑客攻击我。我希望有一个更好的解决方案。

+0

感谢你们,我将在我的存储过程中使用类似的方法将空字符串过滤为空,尽管我同意这是一个解决方法。希望他们将来能够实现这一功能。 (对于建议+1。) – cweston 2011-01-30 21:34:08

+0

是的,这有点不方便,但它的工作原理。谢谢:) – 2013-07-19 13:24:04

11

这是一个古老的问题,但由于给出的解决方案有点'hacky'我想给我自己的,我认为这是更简单的实现比db程序更优雅,使用案例。

您可以尝试使用NULLIF函数编写NULL来比较2个表达式,如果它们相等则返回NULL,否则返回第一个表达式(msdn NULLIF page)。

这样在CommandText您NLOG配置文件看起来像:

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

我用稍微不同的方法。


因为我不喜欢编写查询我创建的这是否为我的扩展,我NLOG配置是这样的:

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

注意两件事情在这里:

  • commandText="[dbo].[Log]"必须遵循的格式[Schema].[Table]
  • @Exception:null其中null意味着它可以为空

没有<commandText>元素,而是我用这个扩展名从参数自动创建INSERT

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

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

Additionaly它可以解析参数名称和插入NULLIF为空的参数。

另一部分帮助我分析它:

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

这意味着你需要的commandText="[dbo].[Log]"属性添加到数据库中的对象,移除查询并添加:null可空列的参数名称。

在代码中,您只需调用它,扩展就可以实现这种魔力。

NLogDatabaseTarget.GenerateInsertQueries();