c# - 每个 Http 请求的分散式 NLog 目标

标签 c# asp.net-web-api2 multi-tenant nlog

上下文

我正在使用 NLog 和 .NET Web API 2 框架。

服务器是一个 Multi-Tenancy 环境,错误被记录到各个客户端数据库中。 我有一个包含 DatabaseTarget 的 NLog.config 文件,但(故意)缺少连接字符串属性。 在请求开始时,获取客户端的连接字符串并以编程方式将其添加到数据库目标,以便可以将错误记录到客户端的数据库中。

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

注入(inject)连接字符串代码片段(在每个请求开始时调用)

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

删除连接字符串和日志记录规则的过滤器(在每个 Controller 上使用)

/// <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");

谢谢大家。对不起,文字墙。

最佳答案

正如您所注意到的,在多线程环境中全局更改事物是很棘手的。

在这种情况下,最好将连接字符串存储在绑定(bind)到线程的上下文中。最好的选择是“映射诊断逻辑上下文”(${mdlc}),它绑定(bind)到一个线程,它是异步子线程。

它还使很多事情变得更容易,因为您不必动态更改规则。

用法:

  1. 创建数据库目标并使用 MDLC 作为连接字符串

    在你的 nlog.config 中:

    <target xsi:type="Database"
        name="target1"
        connectionString="${mdlc:myConnectionString}" .. />
    
  2. 在请求的开头设置连接字符串。

    (这就是所有需要的 C#)

    MappedDiagnosticsLogicalContext.Set("myConnectionString", "server=...user=..");
    

就是这样。无需 LogManager.ReconfigExistingLoggers,循环/更改 NLog 规则。

See docs of the MDLC

关于c# - 每个 Http 请求的分散式 NLog 目标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46845472/

相关文章:

c# - 4 个字节到十进制 - C# 来自 Windev

asp.net-mvc - MVC 5,Identity 2.0 Android Rest/Json Api

azure - 导入api有重复的签名操作azure web api

azure - 使用 OWIN Pipeline 进行 Multi-Tenancy 身份验证

Azure 企业应用程序 : Propagating changes in grants in multi-tenant application

c# - 位掩码的 Int 到 Enum[] 或 Int[]? C#

c# - .Net C# ReadProcessMemory指针函数向后返回地址

c# - 比较两个大型通用列表

rest - 使用 REST GET API 仅检索特定属性

为租户设计具有外键约束的 SQL 模式