私はクリーンな解決策を見つけるのに苦労している問題に遭遇しました、そしてグーグルは私を賢くしませんでした。
状況
(1)Serilogロガーを設定してプロジェクト(一貫性のあるロギング出力、テーマなど)に追加するための独自のアセンブリがあり、このアセンブリには、(異なるリポジトリにある)消費プロジェクトへの参照がありません。これをCompanySerilog
アセンブリと呼びましょう。
(2)消費するプロジェクトの1つは、外部からアクセス可能なAPIであり、「contract」オブジェクトはExternalContractsアセンブリで定義されます。つまり、リクエストオブジェクトとレスポンスオブジェクト、およびそれらのオブジェクトの一部として使用される列挙型。このExternalContractsアセンブリは、APIに対して統合する開発者に提供できます。
(3)すべてのリクエストをログに記録し、を使用しIActionFilter
て、Serilog構造化ログアプローチを使用して各リクエストオブジェクトをログアウトします。たとえば、コンテキスト内の各パラメータをループし、最終的に_logger.LogDebug("With {name} of {@requestObject}", name, value);
問題
一部のリクエストオブジェクトには、マスクしたい機密データがありますが、次のようになります。
CompanySerilog
標準の.Destructure
拡張機能を使用してロガーを作成するときに分解の方法を定義することはできますが、リクエストオブジェクトの詳細がわからない、または知りたい場合はApi1
、からのものである可能性があります。Api2
これは、すべてに参照を追加することを意味しますプロジェクトを消費します。Destructurama.Attributed
)に属性を追加することもできますが、そのExternalContracts
場合、アセンブリにはそのNuGetパッケージへの参照が必要になり、必要なすべてのSerilogパッケージへの参照が必要になります。厳密に言えば、ExternalContractsアセンブリではロギングの懸念は必要ありません。これは、APIのコンシューマーではなく、私たちの問題です。私が言っているように、私はこれを解決する方法を考え出すのに苦労していて、たとえばIDestructuringPolicyの使用に関する情報や、それが適切かどうか、または変換を実行する必要があるかどうかについての情報を見つけることができません。これまでのところ、私は次のオプションしか考えられませんが、他の誰かがこの問題に遭遇し、このユースケースをサポートするためのひどく賢くてクリーンな方法を持っていることを願っています。
ソリューション?
構造化ロギングの実行を停止しToString()
、ログに記録したくない値をマスクするリクエストオブジェクトごとにを定義するだけです。これは単純で、厄介なプロジェクトの相互参照や外部契約へのロギングの懸念の追加を必要としません。しかし、それは構造化ロギングが不可能であることを意味します。
必要なすべてのロギング参照を外部コントラクトに追加します。これにより、組み込みの破棄を引き続き使用できますが、APIのコンシューマーは、ロギングアセンブリを含むExternalContractsアセンブリを使用することになります。
このアセンブリを使用するすべてのプロジェクトを参照して.Destructure
、ログインを構成するときに値を設定しますCompanySerilog
。起こらない!
他に何かありますか?お願いします!
誰かが同様の問題に遭遇した場合に備えて、2つの解決策を考え出しましたIDestructuringPolicy
。どちらもを使用する必要があります。
解決策1
アセンブリに単一のジェネリックIDestructuringPolicy
がありCompanySerilog
ます。
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();
このアプローチの長所:
このアプローチの短所:
結局、最初のソリューションの短所のために、別のアプローチを採用しました。
解決策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に対するこのアプローチの利点は、現在具体的なタイプを扱っていることです。そのタイプにポリシーがない場合、それは反映されません。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加