代码执行期间报告信息:最佳设计

Ste*_*ini 6 debugging logging

在设计正确的执行报告时,我总是怀疑.

假设你有以下(愚蠢,简单)的情况.我将使用python.

def doStuff():
    doStep1()
    doStep2()
    doStep3()
Run Code Online (Sandbox Code Playgroud)

现在,假设您想要报告各种步骤,如果出现问题等等.不是真正的调试:只是应用程序的信息行为.

第一个简单的解决方案是打印

def doStuff():
    print "starting doing stuff"
    print "I am starting to do step 1"
    doStep1()
    print "I did step 1"
    print "I am starting to do step 2"
    doStep2()
    print "I did step 2"
    print "I am starting to do step 3"
    doStep3()
    print "I did step 3"
Run Code Online (Sandbox Code Playgroud)

总的来说,这非常糟糕.假设这段代码最终会进入库中.我不希望我的图书馆打印出来的东西.我希望它能默默地完成这项工作.尽管如此,有时我想提供信息,不仅在调试情况下,而且还要让用户知道事情正在完成的过程中.打印也很糟糕,因为您无法控制邮件的处理.它只是去stdout,除了重定向之外,你无能为力.

另一种解决方案是拥有一个用于记录的模块.

def doStuff():
    Logging.log("starting doing stuff")
    Logging.log("I am starting to do step 1")
    doStep1()
    Logging.log("I did step 1")
    Logging.log("I am starting to do step 2")
    doStep2()
    Logging.log("I did step 2")
    Logging.log("I am starting to do step 3")
    doStep3()
    Logging.log("I did step 3")
Run Code Online (Sandbox Code Playgroud)

这样做的好处是,您可以为日志服务了解一个独特的位置,并且可以根据需要修改此服务.您可以将其静音,将其重定向到文件,stdout,甚至是网络.缺点是您与Logging模块有很强的耦合.基本上,代码的每个部分都依赖于它,并且您可以随时随地调用日志.

第三个选项是使报表对象具有清晰的界面,然后传递它

def doStuff(reporter=NullReporter()):
    reporter.log("starting doing stuff")
    reporter.log("I am starting to do step 1")
    doStep1()
    reporter.log("I did step 1")
    reporter.log("I am starting to do step 2")
    doStep2()
    reporter.log("I did step 2")
    reporter.log("I am starting to do step 3")
    doStep3()
    reporter.log("I did step 3")
Run Code Online (Sandbox Code Playgroud)

最后,如果有更多话要说,你也可以将报告对象传递给doStepX().优点:它减少了与模块的耦合,但它引入了与NullReporter对象的实例化的耦合.这可以通过使用None作为默认值并在调用log之前进行检查来解决,这是笨拙的,因为在python中你必须每次都写一个条件(在C中你可以定义一个宏)

def doStuff(reporter=None):
    if reporter is not None:
        reporter.log("starting doing stuff")
        # etc...
Run Code Online (Sandbox Code Playgroud)

编辑:另一种选择是像Qt一样工作,并有一个emit()信号策略.当您的代码执行时,它会使用正确的状态代码发出信息,任何感兴趣的人都可以订阅信号并提供信息.很好,干净,非常分离,但需要一些编码,因为我不认为这可以很快完成包括蟒蛇电池.

最后,您可以使用有意义的错误消息引发异常,但这当然只有在您退出错误条件时才能使用.它不适用于偶尔的报告.

编辑:我想澄清这样一个事实,即情况更为笼统,而不仅仅局限于一系列被调用的步骤.它还可能涉及控制结构:

 if disconnected:
     print "Trying to connect"
     connect()
 else:
     print "obtaining list of files from remote host"
     getRemoteList()
Run Code Online (Sandbox Code Playgroud)

报告也可以进入实际例程,因此您可以在connect()和getRemoteList()例程中将"print"作为第一个语句.

因此问题是:

  • 您认为某些代码(特别是在库的情况下)的最佳设计是在噪声可能对客户端造成破坏的同时保持沉默,但是在有用的时候是冗长的?
  • 如何处理逻辑代码和报告代码之间的平衡混合?
  • 代码和错误检查之间的混合已经解决,但有例外.如何从代码逻辑中划分报告的"噪音"?

编辑:更多关于头脑的想法

我认为这不仅仅是将Logging代码与逻辑代码分离的问题.我认为这也是将信息生产与信息消费脱钩的问题.类似的技术已经存在,特别是处理UI事件,但我并没有真正看到应用于日志记录问题的相同模式.


编辑:我接受了马塞洛的回答,因为他指出了事实证据表明妥协是这种情况下的最佳解决方案,并且没有灵丹妙药.然而,所有其他人都是有趣的答案,我真的很高兴赞成所有这些.谢谢您的帮助!

Mar*_* MD 1

我认为有一个点你必须划清界限并做出妥协。我认为没有办法将日志记录与系统完全解耦,因为您必须将这些消息以某人理解的方式发送到某个地方。

我会使用默认的日志记录模块,因为......它是默认的模块。它有详细的文档记录并附带默认库,因此这里不存在依赖性问题。此外,您还可以避免重新发明轮子。

也就是说,如果你真的想做一些新的事情,你可以制作一个全球记者对象。您可以在流程开始时实例化和配置它(记录、不记录、重定向流等。即使在每个流程/函数/步骤的基础上)并从任何地方调用它,不需要传递它(也许在一个多线程环境,但这将是最小的)。

您还可以将其放入另一个线程中并按照 Qt 的方式捕获日志事件。