我正在使用NLog和.NET Web API 2框架。
服务器是一个多租户环境,其中错误记录到各个客户端数据库。 我有一个NLog.config文件,其中包含DatabaseTarget但缺少(故意)连接字符串属性。 在请求开始时,获取客户端的连接字符串并以编程方式添加到数据库目标中,以便将错误记录到客户端的数据库中。
执行Web Api操作后,我清除连接字符串,以便后续请求不会登录到错误的数据库。这适用于连续请求。
对服务器的并发请求都试图立即更改数据库目标的连接字符串。记录的均值错误是最后一次在数据库目标上设置的数据库。
是否可以将NLog实例或至少是记录目标隔离到单个请求? 如果没有,我将如何实现这一目标?
注意:要求在NLog.config文件中配置数据库目标(除了它的连接字符串),以便可以在不更改代码的情况下修改它的查询。我仍然对那些不可能的解决方案感兴趣,例如。以编程方式创建数据库目标。
NLog.config文件
<?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">
<targets>
<!--
The logdatabase target's connectionString is
added programmatically.
-->
<target name="logdatabase"
xsi:type="Database"
dbProvider="odbc">
<commandText>
<!-- super secret query is here -->
</commandText>
<!-- super secret parameters are here -->
</target>
</targets>
<rules>
<!-- rule is added programmatically so that there are no logging attempts before a connection string is added -->
</rules>
</nlog>
注入连接字符串代码段(在每个请求开始时调用)
/// <summary>
/// Set up NLog to log to the database.
/// <param name="connectionString">Database to log to</param>
/// </summary>
private void SetUpDatabaseLogging(string connectionString)
{
DatabaseTarget databaseTarget = LogManager.Configuration.FindTargetByName<DatabaseTarget>("logdatabase");
databaseTarget.ConnectionString = connectionString;
// Add rule if it does not exist already
if (!DatabaseRuleExists("logdatabase"))
{
LoggingRule logDatabase = new LoggingRule("*", LogLevel.Debug, databaseTarget);
LogManager.Configuration.LoggingRules.Add(logDatabase);
}
LogManager.ReconfigExistingLoggers();
}
/// <summary>
/// Check if a rule exists that uses the specified target
/// </summary>
/// <returns></returns>
private bool DatabaseRuleExists(string targetName)
{
bool ruleExists = false;
foreach (LoggingRule rule in LogManager.Configuration.LoggingRules)
{
if (rule.Targets.Where(target => target.Name == targetName).Count() > 0)
{
ruleExists = true;
break;
}
}
return ruleExists;
}
过滤器删除连接字符串和日志记录规则(在每个控制器上使用)
/// <summary>
/// Clean up log database connection after request
/// </summary>
public class LogCleanUpFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
// Remove connection string from logging target.
DatabaseTarget databaseTarget = LogManager.Configuration.FindTargetByName<DatabaseTarget>("logdatabase");
databaseTarget.ConnectionString = null;
// Remove database rule. Rule must not persist between requests
// because we are logging to client database.
foreach (LoggingRule rule in LogManager.Configuration.LoggingRules)
{
if (rule.Targets.Where(target => target.Name == "logdatabase").Count() > 0)
{
LogManager.Configuration.LoggingRules.Remove(rule);
break;
}
}
LogManager.ReconfigExistingLoggers();
}
}
我正在使用当前的类记录器
进行记录private Logger logger = LogManager.GetCurrentClassLogger();
logger.Error("I'm a naughty function");
谢谢你们。对不起文字墙。
答案 0 :(得分:1)
正如您所注意到的那样,在多线程环境中全局更改内容非常棘手。
在这种情况下,最好将连接字符串存储在绑定到线程的上下文中。最好的选择是&#34;映射诊断逻辑上下文&#34; (${mdlc}
),绑定到一个线程及其异步子线程。
由于您不必动态更改规则,因此它还可以使很多内容变得更容易。
用法:
创建数据库目标并使用MDLC作为connectionstring
在你的nlog.config中:
<target xsi:type="Database"
name="target1"
connectionString="${mdlc:myConnectionString}" .. />
在请求开头设置connectionstring。
(这是所有需要的C#)
MappedDiagnosticsLogicalContext.Set("myConnectionString", "server=...user=..");
这就是全部。无需LogManager.ReconfigExistingLoggers
,循环/更改NLog规则。