哪种设计(使用 OOP)更好?外部化还是内部化日志记录?

Which design (using OOP) is better? Externalized or internalized logging?

我需要验证日志信息(错误消息)以及结果集。这里logging在我这里也可以理解为report generation

外部化日志记录

我是否应该将日志消息(对于任何错误)与结果一起存储并在业务逻辑步骤之后进行日志记录

优点:

  1. 这为我提供了日志信息,我可以使用这些信息在单元测试期间验证负面案例,而不是解析日志文件。
  2. 将日志记录与业务逻辑分开。
  3. 我可以将日志记录作为一个单独的功能来实现,我可以根据实现以不同的格式登录(HTML、JSON 等)

缺点

  1. 这将导致代码重复,因为我最终会得到与计算结果集相同的日志循环。
  2. 在记录阶段,parent 必须获取 child 信息。存储所有这些信息使其变得复杂且不可读。

内部化日志记录

我是否应该在执行业务逻辑的同时进行日志记录

优势

  1. 不存储任何信息并简化解决方案并有效地将 parent object 的上下文传递给 child object、
  2. 发生异常时记录。

缺点

  1. 但无法将 logging/reporting 与业务逻辑分开。
  2. 我不会得到日志信息来验证 unit-tests 的负面案例。所以我需要解析日志文件来验证。

更多上下文如下:

我正在构建此工具以比较两种资源的属性,这些资源的类型可以是 - JSON、属性、VM、REST API 等 Python。

该工具读取具有如下结构的元数据json:

{
  "run-name": "Run Tests"
  "tests": [
    {
      "name": "Test 1",
      "checks":[
         {
          "name": "Dynamic Multiple",
          "type": "COMPARE",
          "dynamic": [
            {
              "file": "source.json",
              "type": "JSON",
              "property": "topology.wlsClusters.[].clusterName"
            }
          ],
          "source": {
            "file": "source.json",
            "type": "JSON",
            "property": "topology.wlsClusters.[clusterName == ].Xms"
          },
          "target": {
            "file": "target.properties",
            "type": "PROPERTY",
            "property": "fusion.FADomain..default.minmaxmemory.main",
            "format": "-Xms{}?"
          }
        },
      ]
    }
  ]
}

以上 JSON 告诉我的工具:

  1. 从 topology.wlsClusters 中的每个 wlsCluster object 中获取 'clusterName'。这给出了 'clusterNames'.
  2. 的列表
  3. 从 'source.json' 从每个 wlsCluster object 获取 Xms 值,其中 'clusterName' 属于上述列表。
  4. 同样使用上面的列表从 target.properties 文件中获取所有 Xms 值。
  5. 比较源 Xms 列表和目标 Xmx 列表中的每个值。
  6. 如果全部匹配则成功,否则失败。

直观上上面的JSON可以映射到对应的object为:

现在理想情况下,我知道我应该执行以下步骤:

大体上我有这些步骤:

  1. 获取并存储值。
  2. 比较值

我也想按以下格式打印日志。

[<TIMESTAMP> <RUN-NAME> <TEST-NAME> <CHECK-NAME> <ERROR-LEVEL> <MESSAGE-TYPE> <RESOURCE-NAME>] custom-msg

其中

ERROR-TYPE: INFO DEBUG etc
MESSAGE-TYPE: COMPARE, SYNATAX-ERROR, MISSING-PROPERTY etc

现在如果我遵循上面的 object 模型,并且每个 object 负责处理它自己的日志记录,它就不会拥有所有这些信息。所以我需要:

我更喜欢第二种方法,因为这样我就可以存储获取的结果并将日志记录(如果有的话)延迟到比较之后。通过这种方式,我还可以 运行 验证(unit-tests),因为我也验证错误消息(负面情况)。

但这就是我的解决方案变得复杂的地方。

注意:这是在 Python 中开发的。 (如果这对你很重要。)

每个 class 都应该对自己的状态负责。当您让 classes 根据其他 classes 中的属性做出决定时,您最终会得到意大利面条代码。

类似 if (test.check.resource.AProperty == aValue) 的代码清楚地表明您的意大利面已经开始烹饪了。

在这种情况下,您希望在 classes 中完全登录。您想要确定一系列操作是否成功完成。 因此,您想记录结果

考虑到这一点,根本不要让 classes 记录,而只报告他们 tested/checked 的内容及其结果。

一种常见的方法是提供用于接收结果的上下文对象。

这里有一些 C# 代码来说明(我不太了解 python):

interface VerifierContext
{
  void AddSuccess(string checkName, string resourceName, string message);
  void AddFailure(string checkName, string resourceName, SomeEnum failureType, string message);
}
public class SomeChecker
{
    public void Validate(VerifierContext context)
    {
        context.AddFailure("CompanyNameLength", "cluster.Company", SomeEnum.LengthRestriction, "Company name was 30chars, can only be 10");
    }
}

这将为您提供一个简单的验证列表。如果你想嵌套,你可以添加 Enter/Exit 方法:

public class SomeChecker
{
    public void Validate(VerifierContext context)
    {
        context.Enter("CompanyValidations");

        foreach (var validator in _childValidators)
            validator.Validate(context);

        context.Exit("CompanyValidations");
    }
}

你当然可以用很多不同的方式来设计它。我的主要观点是,您 check/parser 中的每个 class 都应该决定一切是否正常。它不应该决定应该如何记录事情。

触发工作的 class 然后可以遍历所有结果并根据错误类型等选择日志级别

所有 class 也很容易测试,因为它们仅取决于上下文。