gpt4 book ai didi

c# - 用于控制请求对象解构的选项

转载 作者:行者123 更新时间:2023-12-02 01:12:06 26 4
gpt4 key购买 nike

我遇到了一个问题,正在努力寻找一个干净的解决方案,而谷歌搜索并没有让我变得更明智。

情况

(1) 我们有自己的程序集,用于设置 Serilog 记录器并将其添加到我们的任何项目(一致的日志输出、主题等),并且该程序集没有引用任何使用项目(位于不同的项目中)。 repo )。我们将其称为 CompanySerilog 程序集。

(2) 使用项目之一是外部可访问的 API,其“合约”对象在ExternalContracts 程序集中定义。即请求和响应对象,以及用作这些对象一部分的任何枚举。这个ExternalContracts程序集可以提供给针对API进行集成的开发人员。

(3) 我们想要记录所有请求,并使用 IActionFilter 使用 Serilog 结构化日志记录方法注销每个请求对象。例如循环上下文中的每个参数并最终执行 _logger.LogDebug("With {name} of {@requestObject}", name, value);

问题

某些请求对象具有我们想要屏蔽的敏感数据,但是:

  • 我们可以使用标准的 .Destruction 扩展在 CompanySerilog 中创建记录器时定义解构方法,但不知道或想知 Prop 体细节请求对象,因为这些对象可能来自 Api1Api2 等,这意味着添加对每个使用项目的引用。
  • 我们可以向请求对象添加属性 (Destructurama.Attributed),但这意味着我们的 ExternalContracts 程序集现在需要对该 NuGet 包的引用,而 NuGet 包又需要引用所有必需的 Serilog 包。严格来说,ExternalContracts 程序集中不需要日志记录问题:这是我们的问题,而不是 API 的使用者的问题

正如我所说,我一直在努力想出解决这个问题的方法,但找不到太多有关使用的信息,例如 IDestructuringPolicy 以及它是否合适,或者是否应该进行转换进入游戏。到目前为止,我只能想到以下选项,但我希望其他人也遇到了这个问题,并且有一个非常聪明和干净的方法来支持这个用例。

解决方案?

  • 停止进行结构化日志记录,只需为每个请求对象定义一个 ToString() 即可屏蔽我们不想记录的值。这很简单,不需要讨厌的项目交叉引用或将日志记录问题添加到外部契约(Contract)中。但这确实意味着不可能进行结构化日志记录。

  • 将所有需要的日志记录引用添加到外部合约中。这将使我们能够继续使用内置销毁,但意味着我们的 API 的使用者将拥有一个包含日志记录程序集的ExternalContracts程序集

  • 在配置 CompanySerilog 中的日志记录时,通过引用将使用此程序集的每个项目来设置 .Destruct 值。这不会发生!

  • 还有别的事吗?请!

最佳答案

我们提出了两种可能的解决方案,如果有人遇到类似问题,我将与大家分享 - 两者都涉及使用 IDestructuringPolicy

解决方案1

CompanySerilog 程序集中有一个通用的 IDestructuringPolicy

public class SensitiveDataDestructuringPolicy : IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
var props = value.GetType().GetTypeInfo().DeclaredProperties;
var logEventProperties = new List<LogEventProperty>();

foreach (var propertyInfo in props)
{
switch (propertyInfo.Name.ToLower())
{
case "cardnumber":
case "password":
logEventProperties.Add(new LogEventProperty(propertyInfo.Name,propertyValueFactory.CreatePropertyValue("***")));
break;
default:
logEventProperties.Add(new LogEventProperty(propertyInfo.Name, propertyValueFactory.CreatePropertyValue(propertyInfo.GetValue(value))));
break;
}

}
result = new StructureValue(logEventProperties);
return true;
}
}

并且在设置记录器时,使用以下配置:

var logger = new LoggerConfiguration()
// snipped out all the other things that need configuring
// ...
.Destructure.With<SensitiveDataDestructuringPolicy>
.CreateLogger();

这种方法的优点:

  • 一个地方(在日志记录程序集中)负责决定如何记录对象,而不知道这些对象的类型

这种方法的缺点:

  • 这将反射(reflect)每个对象的每个属性,如果只有一两个对象需要屏蔽,这就太过分了

由于第一个解决方案的缺点,最终我们采用了不同的方法。

解决方案2

CompanySerilog 中创建记录器的方法在使用它的程序集中查找 IDestructuringPolicies。

public static ILogger Create()
{
var destructuringPolicies = GetAllDestructuringPolicies();

var logger = new LoggerConfiguration()
// snipped out all the other things that need configuring
// ...
.Destructure.With(destructuringPolicies)
.CreateLogger();

//Set the static instance of Serilog.Log with the same config
Log.Logger = logger;

logger.Debug($"Found {destructuringPolicies.Length} destructuring policies");
return logger;
}

/// <summary>
/// Finds all classes that implement IDestructuringPolicy, in the assembly that is calling this
/// </summary>
/// <returns></returns>
private static IDestructuringPolicy[] GetAllDestructuringPolicies()
{
var policies = Assembly.GetEntryAssembly().GetTypes().Where(x => typeof(IDestructuringPolicy).IsAssignableFrom(x));
var instances = policies.Select(x => (IDestructuringPolicy)Activator.CreateInstance(x));
return instances.ToArray();
}

现在,此 CompanySerilog 程序集的任何使用者都负责通过为其关心的每个类定义一个 IDestructuringPolicy 来定义它想要如何记录敏感数据。例如:

public class RegisterNewUserDestructuringPolicy : IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
var request = value as RegisterNewUserRequest;
if (request == null)
{
result = null;
return false;
}

var logEventProperties = new List<LogEventProperty>
{
new LogEventProperty(nameof(request.Claims), propertyValueFactory.CreatePropertyValue(request.Claims)),
new LogEventProperty(nameof(request.Email), propertyValueFactory.CreatePropertyValue(request.Email)),
new LogEventProperty(nameof(request.Password), propertyValueFactory.CreatePropertyValue("****")),
new LogEventProperty(nameof(request.Roles), propertyValueFactory.CreatePropertyValue(request.Roles)),
new LogEventProperty(nameof(request.UserName),
propertyValueFactory.CreatePropertyValue(request.UserName))
};

result = new StructureValue(logEventProperties);
return true;
}
}

这种方法相对于解决方案 1 的优点在于,我们现在处理的是具体类型,如果该类型没有策略,那么它将不会被反射(reflect)。

关于c# - 用于控制请求对象解构的选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51541340/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com