是否可以将代码完全记录在业务逻辑之外?


12

借助AOP,我可以从业务逻辑中删除日志记录代码。但是我认为它只能用于记录简单的事物(即记录方法的进入/退出和参数值)。

但是,如果我需要在业务逻辑中记录一些内容,该怎么办?例如

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      Log.Warn("user is not existed");        //<----------------- Log A
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   Log.Info("Step 1 is completed");            //<----------------- Log B

   //Step 2
   while(true)
   {
       //do something
   }
   Log.Info("Step 2 is completed");            //<----------------- Log C

}

上面的示例方法可能还不够清楚,我想在这里展示的是,从域的角度来看,该方法应被视为最小的单位。它不应该分成较小的部分。

是否可以将3种以上的记录代码移出该方法?这种情况下的最佳做法是什么?


我很确定您的示例的“ Step1”和“ Step2”日志应该是业务逻辑审计跟踪的一部分,而第一个是技术日志。我将首先
对它进行

Answers:


1

当然!

但是根据我的经验,有用的日志记录有两种常规类型:

一切日志:通过分析API构建的日志。非常适合识别性能问题和报告异常。十分吵闹。

业务事件日志:在业务逻辑中调用的日志。企业可能关心的任何事情。噪音最小。只是值得注意的,合乎逻辑的“业务”事件。适合审计和KPI的...

因此,我强烈建议两件事。首先,执行其他监视工具(例如New Relic)的工作,并使用.NET分析API 1。其次,在您的业务逻辑中记录逻辑业务事件。记录某些事件业务逻辑。

而且,通常我不会建议AOP进行两种日志记录2。以我的经验,您要么想要一切,这意味着您正在使用探查器,要么想要逻辑/业务事件。在后一种情况下,我认为仅在业务逻辑中调用记录器会更简单


1.但是,认真地说,您可以节省数千小时的工作,而只需使用现有的探查器工具...

2.当然,这是假定你分享我的意见,一个方面是不是一个伟大的地方躲业务规则!


我非常同意“业务事件日志”,就像其他人的回答一样,我会将日志代码保留在业务逻辑中。对于“所有日志”部分,我更喜欢使用AOP解决方案,因为它将遵循SRP,并且不会污染我的业务逻辑。无论如何,我将首先看一下分析API。
查理

10

当然,您可以轻松地为此使用AOP。只需重构零件

  • 通过ID获取用户
  • 步骤1
  • 第2步

分成单独的方法(您应该做得更干净一些)。现在,您可以轻松地配置AOP框架来记录您选择的方法调用(如此处所示)。调用者可以直接记录该异常,而无需使用AOP将其从业务逻辑中删除。

进行编辑:

我要在这里显示的是,从域的角度来看,该方法应被视为最小的单位。它不应该分成小块

为什么不呢?如果在“业务逻辑上下文”中要记录值得记录的“内容”,并且可以给该“内容”取一个合理的名称,则在大多数情况下,将代码重构为方法它自己的。如果要使用AOP,则无论您的记录要求如何,都将需要以可能应该对代码进行结构化的方式来对代码进行结构化。您可以将其解释为AOP的缺点,也可以将其解释为AOP的优点,因为它为您提供了可以改进代码结构的反馈。


我的例子不够清楚是我的坏处。我实际上想在示例中显示的是,从域的角度来看,该方法是最小的单位,不应将其分成较小的部分。
查理

@Charlie:这个例子很清楚。您在这里的误解可能是您认为拥有比步骤更大的方法可能是一个好主意。恕我直言,这是错误的,这不是一个好主意。具有值得记录的不同步骤是一个明显的信号,这些步骤应具有一个抽象名称(一个名称),因此一个名称(方法)。
布朗

@Charlie并没有阻止您执行单位或工作所调用的3个私有方法。从外部看,这种方式保持不变,但是现在您有了日志记录所需的抽象。
雷米

如果您想通过记录问题来驱动代码结构,则此方法很好。但是,有时您可能想通过其他方式来驱动它。
约翰·吴

@JohnWu:无论记录要求如何,代码结构都应反映出不同的关注点/步骤。就是驱动代码结构的原因。解决此问题后,可以通过AOP完成日志记录,这更多是为代码提供更好的结构的“副作用”。因此,我认为驱动代码结构的不是日志记录问题,更多的是使用AOP进行日志记录的要求使代码缺少它应该具有的某种结构更加透明。
布朗

3

除非记录事务是业务需求的一部分,否则最好将其完全排除在代码外。

这意味着您真的不想记录“步骤1完成”之类的内容。尽管它最初可能对调试有用,但在生产中它只会生成数以千计的垃圾,您将永远不会看。

如果Step1Complete是某种业务事件,需要采取进一步的措施,那么可以通过一个老式事件来暴露它,而不必强迫您向类中注入ILogger或类似事件


那就是我的想法。我无法提出在域/业务模型POCO中进行日志记录的合理案例。日志记录很自然地适合于核心业务模型IMO。
jleach

2

借助一些常见的模式,您可以从业务逻辑中提取日志记录代码。但是,您可能不认为这样做值得

例如,通过使用侦听器(一种手工方法或使用事件总线等),您的代码将看起来像

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      listener.OnUserNotFound(userId);
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   listener.OnStep1Finished(......);

   ...

}

通过在侦听器中实现日志记录,您的业务逻辑中将不再包含日志记录逻辑。

但是,您可能会发现这并不总是现实的,因为您可能无法始终定义有意义的逻辑事件。

另一种方法是通过Solaris中的Dtrace之类的机制,该机制允许您注入正在运行的进程(我相信在C#中可以做类似的事情吗?),以便可以在运行时定义日志记录和统计信息收集。仍然有其他缺点。


AOP试图解决的一个问题是,与“业务代码”交织在一起的非业务代码(如日志记录调用)变得无法读取的问题。用“侦听器”代替“记录器”不能解决此问题,代码的可读性不会改变,
Doc Brown

2

另一种方法是将业务日志记录和技术日志记录分开。然后,我们可以将业务日志记录称为“审核”,并应用一组特定的业务规则(例如存储期限)和处理规则(例如“业务活动监视”)。

另一方面,技术日志记录,或简称为“日志记录”,是留下技术问题的最后手段。它应该异步,快速,容忍无法持久保存日志消息。此外,日志消息应通过最少数量的代理,以尽可能地避免故障根源。

日志的逻辑是非常可变的,并且与实现紧密相关,所以您真的需要将其与代码分开吗?

审核逻辑应视为领域逻辑,并应进行相应处理。

例如,在六角结构中,可能有Audit端口以及Clients,Storage和MQ(可能还有Metrics和Control)端口。这将是辅助端口,即该端口上的活动是由业务核心而不是外部系统触发的。


我非常同意您的看法,有两种日志记录。但是我不认为Logging的逻辑是非常可变的,并且与实现紧密相关,您的意思是此处的技术日志记录吗?对于技术日志记录,我认为它用于记录方法的进入/退出和参数值,这最好放在方法之外。
查理

@Charlie是的,“记录”是指技术记录。对于纯函数,输入/退出/参数值记录就足够了。然后,或者当然,您可以使用方面或Logger monad。但是纯函数很棒,因为它们是可测试的。因此,记录程序应该跟踪的问题很可能会在开发/调试期间解决。使用不纯函数(最常使用技术日志记录)时,您要记录每个副作用的调用参数/结果,每个异常。
iTollu

1

避免直接在类或方法中登录的方法:

  1. 引发异常,然后在调用树的更远的地方进行catch记录。如果需要捕获日志级别,则可以引发自定义异常。

  2. 调用已记录的方法。


1
是否已证明日志记录存在问题,甚至值得“修复”?
whatsisname

1

确实需要将日志记录与业务逻辑分开吗?完成的记录与所编写的业务逻辑相对应,因此具有相同的类/功能是有意义的。更重要的是,它有助于简化代码的可读性。

但是,如果您确实希望将日志记录与业务逻辑分开,则应考虑引发自定义异常并将这些异常传递给日志记录。


0

不,不在C#中

OP,您的特定问题的答案是“否”,而不是c#。可能还有其他更本地的AOP语言,但是我所见过的所有C#中的AOP方法只能在连接点的上下文中应用方面的行为,这意味着必须在一个代码块和一个代码块之间进行控制。另一个。方面的行为将不会在方法中间执行,除非通过调用另一个方法来执行。

您可以“确认”某些日志记录

话虽如此,您可以提取日志记录中涉及的某些问题,而不必提取日志记录。例如,在方法进入时执行的切入点可以设置日志记录上下文并输出所有输入参数,而在退出时可以捕获异常或将日志提交到永久存储中。

无论如何,日志记录不是一个方面

我要补充一点,无论如何,日志编写并不是真正的跨领域问题。至少不调试日志记录。我的证据是,您无法编写一个横切的要求来完全解释此方面的工作,它是针对每种情况的,因为编写日志的目的是反映日志的运行情况。逻辑,并且每种方法中的逻辑应该合理地唯一(请参阅DRY)。

换句话说,在日志写入和要写入的内容之间存在不可分割的逻辑依赖性。您不能将其概括。

但是审计是

如果您有某种功能上的日志记录要求(例如,支持不可否认性要求的审核日志记录),那么有人会争辩(并且我同意),如果您发现自己需要在方法中间执行这些日志写入,您没有以与面向方面的思维一致的方式来构造代码。如果发生这种情况,则应将代码提取到单独的方法中,直到获得所需的粒度级别为止。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.