我使用NLog登录ASP.Net应用程序并使用Microsoft Sql Server的数据库目标。
我有一些可选的记录参数,并不总是指定。我希望这些在没有提供时写为null,但是NLog似乎总是把它们写成空字符串。
有没有办法将其配置为将null写为默认值?
答案 0 :(得分:12)
这是一个老问题,但由于给出的解决方案有点“ hacky '我想提供自己的功能,我认为它比db程序更简单,使用案例更优雅。
您可以尝试使用比较2个表达式的NULLIF
函数编写NULL,如果它们相等则返回NULL,否则返回第一个表达式(msdn NULLIF page)。
这样,NLog配置文件中的commandText将如下所示:
INSERT INTO [dbo].[log] ([message], [optional])
VALUES (@message, NULLIF(@optional, ''))
答案 1 :(得分:7)
NLog使用StringBuilder来创建参数值。即使未指定参数,它也会将值初始化为builder.ToString(),这是一个空字符串。
您可以像这样更改commandText:
INSERT INTO [dbo].[log] ([message], [optional])
VALUES
(
@message,
case
when len(@optional) = 0 then null
else @optional
end
)
虽然对我来说似乎是个黑客。我希望有更好的解决方案。
答案 2 :(得分:2)
[编辑]
也许比我在下面提出的更明显的解决方案是从使用INSERT语句转换为使用数据库过程进行记录。如果您使用数据库过程,那么您可以处理从空字符串调到自己的null。我不确定您是否可以将数据库过程与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写入null而不是空字符串,但我想知道你是否可以通过其他配置以他们想要的方式工作?
日志记录参数何时可选?您的代码中是否存在某些位置,您始终记录某些值以及其他未记录某些值的位置?您(作为开发人员)能否知道哪些可选参数适用于您的应用程序的哪些部分?
您是否可以配置多个数据库目标,每个目标都指定了“正确”参数?然后,您可以将Loggers指向适合代码位置的特定数据库目标。
假设您的应用程序(按名称空间)划分为(通常)在“之前”,“期间”和“之后”执行的代码。
在“之前”代码中,您可能正在记录参数A.在“期间”代码中,您可能正在记录参数B.在“之后”代码中,您可能正在记录参数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相同)。
一次激活数据库目标日志可能不是一个好主意。也许这会导致性能问题。
嗯,这就是我的全部。我不知道我的想法是否会奏效,更不用说如果它是实用的。
可能更好的解决方案是NLog允许在每个数据库参数上允许你说“发送空而不是空字符串”的额外属性。
我想我应该建议您也可以在NLog forum中提出这个问题。该论坛上的“Craig”今天早些时候提出了同样(或类似)的问题。也许你是克雷格。
答案 3 :(得分:1)
NLog版本。 4.7.4添加了参数选项df1 %<>%
mutate(responded_at= as.Date(column_name))
,因此您可以这样做:
AllowDbNull
在<parameter name="@exception" layout="${exception:format=tostring}" allowDbNull="true" />
<parameter name="@correlationid" layout="${activityid}" dbType="DbType.Guid" allowDbNull="true" />
时,Nlog会自动将空字符串/无输出转换为DbNull值。
答案 4 :(得分: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
表示可以为空的<{li} 没有<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();
}
}
另外,它可以解析参数名称并为可以为空的参数插入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();