我使用NLog來登錄ASP.Net應用程序並使用Microsoft Sql Server的數據庫目標。NLog - 將NULL寫入可選數據庫列
我有一些日誌參數是可選的,並不總是指定。我希望這些在沒有提供時被寫爲空,但NLog似乎總是將它們寫爲空字符串。
有沒有辦法將它配置爲默認寫入爲空?
編號:https://github.com/nlog/NLog/wiki/Database-target
我使用NLog來登錄ASP.Net應用程序並使用Microsoft Sql Server的數據庫目標。NLog - 將NULL寫入可選數據庫列
我有一些日誌參數是可選的,並不總是指定。我希望這些在沒有提供時被寫爲空,但NLog似乎總是將它們寫爲空字符串。
有沒有辦法將它配置爲默認寫入爲空?
編號:https://github.com/nlog/NLog/wiki/Database-target
[編輯]
也許比我下面提出什麼是從使用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>
顯然,可能有幾個問題與此想法:
它可能(或容易)分開你的logge rs以匹配參數的「可選性」。
可能有太多的可選參數的組合,以使其可行(可能與1相同的缺點)。
將數據庫目標的日誌一次激活可能不是一個好主意。也許這會導致性能問題。
好吧,就是我所擁有的。我不知道我的想法甚至可以工作,如果它是實用的,我的想法就更少。
也許更好的解決方案是讓NLog允許每個數據庫參數有一個額外的屬性,允許您說「發送null而不是空字符串」。
我想我應該建議你也可以在NLog forum提問這個問題。該論壇上的「克雷格」在今天早些時候提出了相同的(或類似的)問題。也許你是克雷格。
NLog使用StringBuilder製作參數值。即使未指定參數,它也會將值初始化爲builder.ToString(),它是空字符串。
你可以改變你的CommandText是這樣的:
INSERT INTO [dbo].[log] ([message], [optional])
VALUES
(
@message,
case
when len(@optional) = 0 then null
else @optional
end
)
它雖然看起來像一個黑客攻擊我。我希望有一個更好的解決方案。
感謝你們,我將在我的存儲過程中使用類似的方法將空字符串過濾爲空,儘管我同意這是一個解決方法。希望他們將來能夠實現這一功能。 (對於建議+1。) – cweston 2011-01-30 21:34:08
是的,這有點不方便,但它的工作原理。謝謝:) – 2013-07-19 13:24:04
這是一個古老的問題,但由於給出的解決方案有點'hacky'我想給我自己的,我認爲這是更簡單的實現比db程序更優雅,使用案例。
您可以嘗試使用NULLIF
函數編寫NULL來比較2個表達式,如果它們相等則返回NULL,否則返回第一個表達式(msdn NULLIF page)。
這樣在CommandText您NLOG配置文件看起來像:
INSERT INTO [dbo].[log] ([message], [optional])
VALUES (@message, NULLIF(@optional, ''))
我用稍微不同的方法。
因爲我不喜歡編寫查詢我創建的這是否爲我的擴展,我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();
我是論壇上的Craig用戶。感謝這些想法,我已經爲我的nlog數據庫目標使用了一個存儲過程,因此測試傳入參數上的空字符串並寫入null可能是我將要去的方式,我只是在尋找您提供的替代方法,謝謝爲選項。 – cweston 2011-01-30 21:31:49