记录具有保存敏感数据(待散列)属性的对象

Logging objects with properties which hold sensitive data (to be hashed)

上下文

我正在设计应用程序的日志记录功能。
寻找一个优雅且性能良好的解决方案。
(当然我不会重新实现轮子 :)。我将使用流行的日志记录库——例如 Nlog、Serilog……)。

要求

记录 可能包含具有 敏感信息.
属性的对象
让我们假设一个 class 人:

class Person {
    string Name { get; set; }
    string SensitiveInfo { get; set; }
}

一个糟糕的(在我看来)方法是做这样的事情:

logger.Debug($"{person.Name}, {Hash(person.SenstiveInfo)}");

为什么我觉得这很糟糕?

  1. 这是一个调试日志,可能永远不会记录(基于日志记录级别)。因此,它正在浪费 CPU 资源进行散列,而无需任何必要。
  2. 我宁愿允许对象按原样传递(例如:logger.Debug(person)),而不是强迫开发人员手动分解所有对象属性。

到目前为止的想法...

以下两种方法都需要一个 loggerWrapper,如果可能的话,我想避免:

class LoggerWrapper {
    LoggerWrapper (ILogger logger) {
        //store the ILogger
    }
    void Log(object objectToBeLogged, LoggingLevel level) {
        //If logging level is not accepted return
        _logger.Log(objectToBeLogged, level);
    }
}

1.创建一个 ILoggable 接口

interface ILoggable {
    string GetLogValue(HashManager hashManager);
}

class Person: ILoggable {
    string Name { get; set; }
    string SensitiveInfo { get; set; }

    string GetLogValue(HashManager hashManager) {
        string hashedValue = hashManager.Hash(person.SenstiveInfo);
        return $"{person.Name}, {hashedValue}"
    }
}

// Now the LogWrapper.Log will expect an ILoggable, and will use the 'GetLogValue' method
// overriding 'ToString()' is an alternative but it is exposed by the 'object' class, and i fear that is not strict enough/can be forgotten.

对这种方法的担忧:对开发人员要求太高?可维护性?

2。注释和反射

class Person {
    [Loggable]
    string Name { get; set; }
    [Loggable, SensitiveInfo]
    string SensitiveInfo { get; set; }
}

// Now the logWrapper will rely on reflection (i think) and choose what to log/hash

担心这种方法:性能?


上述方法是否被视为不良做法? 有没有更好的方法?
是否有任何图书馆已经在处理这个问题?

你的第二种方法没问题。

我可以想到几种替代方法,但它们最终会非常接近您所拥有的属性注释。
- 比如在外部文件中保留某种注释信息。
- 或者创建像'SensitiveInfo<T>'这样的特殊类型,并在必须进行散列的地方使用它,但几乎与注释属性相同。

表演部分:
您可以在应用程序开始时创建一次结构,该结构扫描程序集中的所有类型。该结构可以是将类型名称作为键 ("MyCompany.App.Core.Person") 的字典,对于值,您可以使用 Func object created from dynamically compiled lambda expression,这将 return 在需要时使用散列值记录消息。 (这将是困难的部分)。
重要说明: jit 编译器在需要之前不会加载 dll,因此尝试在启动开始时创建此结构可以跳过一些程序集。作为替代方法,您可以尝试按需(惰性)填充结构作为记录器包装器的一部分。

然后我将使用日志包装器,它将使用该结构为特定对象创建日志消息。

一些建议:
1. 记录太多信息不一定是坏事 - 所以我不会使用 [Loggable] 属性 - 只记录对象中的所有内容。这样开发人员的开销就会最小。有时你的对象中可能有很长的字符串,这些字符串并不重要,那么你可以引入 [NonLoggable] 属性。
2. 您可能会处理层次结构对象 - 这会增加创建 func/labbda 表达式的额外复杂性。

总的来说,这种方法比较复杂,但对开发人员的要求较低。
最后,这取决于您和您的团队愿意做什么以及 spend/saved 的工作量(这将取决于项目的大小......等)。


我认为 第一种方法 将难以维护并增加很多噪音。日志记录应该是基础架构关注点——尽可能自动化。