我想知道你们中的任何人是否知道任何最佳实践或设计模式,以便我可以很好地登录我的程序而又不会显得凌乱或or肿。

我们遇到的问题是我们希望拥有良好的日志记录,但是我们似乎无法找到一条只记录操作的行,可以变成一个简单的方法,例如:在这里,我们现在有比代码行更多的日志记录行。

但是,操作人员告诉我,这种级别的日志记录是必需的,目前仍然太粗糙了。还是我坚持下去?

评论

您所需要的不是记录日志,操作实际上需要审计跟踪或事件源。日志不可靠:您记录了“用id = ...在栏上完成的事情”,但是如果dbContext的更改从未保存怎么办?

我也注意到UriAgassi的操作是斜体的,在我的情况下是因为不清楚谁是操作。来自COO或IT运营部门的要求具有不同的目标,针对它们的不同解决方案,并且它们可能具有不同的优先级。

#1 楼

可以想象用Aspect来解决这种跟踪记录问题。

面向方面的编程使您可以在程序中添加样板式代码,而无需实际编写内联样板。例如,您可以添加一个方面,而不是在每次程序进入一个方法时都记录一个方法名称和参数,然后在每次方法返回时记录该方法名称和返回值。

当然,您不一定要记录所有方法。取而代之的是,大多数方面框架都允许您指出要将样板添加到的代码。

我对C#的了解不足,无法推荐要使用的特定于方面的框架或适合您的语法。对不起。

评论


\ $ \ begingroup \ $
使用RealProxy类进行面向方面的编程非常不错。
\ $ \ endgroup \ $
– scheien
2014年3月24日10:16

\ $ \ begingroup \ $
PostSharp是解决这类问题的好工具
\ $ \ endgroup \ $
–安迪·亨特(Andy Hunt)
2014年3月24日上午11:39

\ $ \ begingroup \ $
我认为我们实际上并没有将其称为方面,但是我们正在将这种类型的东西用于日志记录的基础知识。当然有例外,我们改手工记录日志,以更好地控制要报告的内容,但是在许多情况下,我们确实会自动生成进入/退出消息。
\ $ \ endgroup \ $
–keshlam
2014年3月24日23:21在

\ $ \ begingroup \ $
我过去曾使用Castle DynamicProxy完成此任务:docs.castleproject.org/…
\ $ \ endgroup \ $
– Scott Lerch
2014年3月28日在22:29

\ $ \ begingroup \ $
@ AndyBrush,PostSharp的人将您锁定在他们的产品上,并且他们的产品由于重新编写了MSIL而被嵌入到您的代码中。几年前,PostSharp在我的公司里流行了一段时间。现在我们要摆脱它,因为它对非专家用户隐藏了功能,使开发和维护变得复杂。但是我们被锁定,因为有太多的用户使用该代码。我们发送给PostSharp的任何技术问题都会很快被取消,因为它们不再支持我们使用的版本。
\ $ \ endgroup \ $
–user2023861
15年5月28日在14:35

#2 楼

查看您的代码,我相信您的问题不仅是日志记录行很丑陋,而且有可能给您带来比帮助您更多的成本:通常会被遗忘,并且可能会因更改而破坏代码,或者-更糟的是-在尝试通过读取日志文件进行故障排除时使您感到困惑(考虑将Bar bar = dbContext().Bars.First();更改为Bar bar = dbContext().Bars.Last();而不更改日志文件的情况-日志告诉您返回的第一个小节是X,现在您会抓挠这是怎么发生的?)
代码中的日志(如果需要的话)绝对不是Info级别的,更不用说了关于级别Warn-这些是Debug日志。用调试日志填充文件将使您的日志文件变得庞大且无法使用。


Warn日志应该仅在发生某些超出预期行为的情况时才记录,以便故障排除生产将集中于此。 ,它与事务的数量成线性比例,并传达有价值的信息。

Info日志通常是临时添加的,当您尝试查找难以捉摸的错误时,需要在特定代码内提供更多信息。在生产方案中,应关闭这些日志。



从您的帖子中暗示您已从运营团队收到此要求。我很难理解操作团队为什么会要求您记录内部代码行为...他们可能会要求您在功能级别上有特定的日志,但是拥有细粒度的记录要求似乎违反直觉,而不是在他们的职责范围内。

无论如何,要解决第一个项目符号(并使您的代码更简洁),AFAIK的最佳策略是使用@BillMichell建议的面向方面的编程。

评论


\ $ \ begingroup \ $
我同意你的看法,我上面提到的级别只是伪代码的一部分。运维团队希望这样的日志的原因是,这样,如果客户记录了支持电话,则通常是关于特定部门的。这将与日志中写入的ID结婚。然后,支持团队将打开日志文件,并按该ID进行过滤,我们将看到系统对该项目所做的一切。然后,他们可以查看发生在哪里/最后发生了什么。
\ $ \ endgroup \ $
–尼克·威廉姆斯(Nick Williams)
2014年3月24日14:33

\ $ \ begingroup \ $
我理解他们的逻辑,但是您应该指出,操作需要的是用户活动,而不是用户跟踪。此范围的级别更高,并且应包括IMHO,用户最多拥有的每个入口点(哪个端点和何时),以及每个出口点(是否成功,是否成功,为什么,也许还有一些基准)花了多长时间)
\ $ \ endgroup \ $
– Uri Agassi
2014年3月24日14:39

\ $ \ begingroup \ $
对不起,也许我应该包括它是Windows服务。因此,实际上没有用户。不知道这有什么不同吗?我同意这感觉不对。
\ $ \ endgroup \ $
–尼克·威廉姆斯(Nick Williams)
2014年3月24日15:15

\ $ \ begingroup \ $
我相信这只是一个术语问题-用“客户”或其他内容替换“用户”,用“活动开始”替换“进入点”,用“活动结束”替换“退出点” ...
\ $ \ endgroup \ $
– Uri Agassi
2014年3月24日15:52

#3 楼

正如比尔·米歇尔(Bill Michell)指出的那样,面向方面的编程是添加样板日志代码的好方法,但是在这种情况下,我认为它并不是真正必要的-我会通过一些好的老式重构来解决这个问题。

在您的示例中,Foo方法会执行所有日志记录,但是如果重要的是要记录您将要执行的操作(或您已执行了某些操作),则最好将其记录在以下方法中:做那个事情而不是调用方法。通过将数据库调用重构为单独的方法(无论如何都应该位于不同的类中),并重构日志记录调用,其操作非常容易,以使Foo方法像以前一样易读。 br />
因此,为了回答更普遍的问题“如何在保持代码可读性的同时添加日志记录?”,我将方法重构为较小的方法,直到每个方法都需要可读为止。 >

评论


\ $ \ begingroup \ $
是的,这个。但理想情况下两者。从ID加载实体的每个方法都应记录相同的信息:id = x,found = y。这应该在所有实体上一致地应用。完成此操作后,您可以从客户端方法中删除四行日志记录,并大大缩减代码库。
\ $ \ endgroup \ $
– David Harkness
2014-3-27的2:41

#4 楼

当您要执行某项操作或正确执行某项操作时,记录日志对于开发代码而言是一件好事。在开发过程中,您想了解发生的事情的最好细节。

在生产代码中,您希望一切都会好起来。实际上,理想情况下,您只希望在出现问题时得到通知:您从代码中获取异常,数据库调用失败或代码的关键部分返回了意外情况。在这种情况下,您想记录出问题所在,并在可能的情况下记录正在使用的变量以帮助重现该错误。您获得Bar的时间,或者获得ID为X的Bar的时间,或者您正在Bar上执行某项操作,或者您已经成功地在Bar上完成了某些操作。您希望它们能够正常工作,因此只希望在出现问题时记录日志,然后注销在doSomething()中使用的bar变量和Bar的ID。如果someCondition为true,则您希望记录已添加的内容,但不希望记录它成功的情况,除非记录出错了。 ,他们认为这是错误的。他们不想要那些日志记录语句,他们想要那些日志记录语句生成的信​​息。他们可能需要基于这些日志记录语句或与日志记录语句相关的统计信息。与Operations讨论为什么他们需要这些语句,并为他们提供所需的内容。

#5 楼

正如这里的其他人指出的那样,日志记录承担着与注释相同的负担-过多或过少弊大于利。

一种实用的日志记录方法是在大多数关键分支中放置一条日志行-分支机构做些次要的事情,不值得记录。记录最关键的分支是catch(Exception x)-始终记录异常

对于日志的维护,我有一些独特的方法:我为自己喜欢的开发环境(VS,Eclipse)创建了插件。按下热键时,插入一个短短的,唯一的,预定长度的字符的唯一编码序列(10个字母,全部大写),然后我使用这些+运行时数据,而不是人类可读的语言。代码”,它们具有多种优势,其中一些优势:


无需复杂的措辞
您总是可以在代码中找到产生特定日志的位置
您可以轻松地计算特定消息或其他统计信息的实例:我有LinqPad脚本,它们使用日志文件名和debrix
代码来生成每个事件附近的快照。可以确保您的消息始终是最新的:如果重构了对象,则您必须
将日志消息重构为l。
在具有多个平台的复杂系统中使用唯一的debrix代码,仍然会带您到一个生成日志消息的地方,无论它是javascript,服务器端,客户端(OSX
聚光灯非常方便)
该代码可以包含在客户可见的错误消息中,以提供客户支持。除了时间戳,几乎可以保证唯一地标识一个事件

这就是我将如何使用这种方法来记录您的代码示例的方法:

/>这可以让您全面了解使用最少的消息量选择的代码路径。

评论


\ $ \ begingroup \ $
是否可以免费下载Eclipse的“残基代码”-“ addin”?
\ $ \ endgroup \ $
– MrSmith42
2014年3月24日22:07在

\ $ \ begingroup \ $
这是Eclipse的jar,因为我主要使用VS,所以边缘比较粗糙,并且安装是手动dropbox.com/s/oxx9efxhhh0n8ih/AidemaDebrix_1.0.0.6.jar |月食手册:help.eclipse.org/indigo/…
\ $ \ endgroup \ $
– Sten Petrov
2014年3月25日下午3:05

\ $ \ begingroup \ $
这是在VS2008-2012上测试的VS插件安装程序:dropbox.com/s/idgvodomej1gbrk/VisualDebrixSetup.msi
\ $ \ endgroup \ $
– Sten Petrov
2014-3-25的3:06

\ $ \ begingroup \ $
+1这是个好主意。我经常通过创建强制执行语义的类型化方法来使日志消息保持最新。但是使用唯一的消息代码使过滤变得轻而易举。
\ $ \ endgroup \ $
– David Harkness
2014年4月4日在2:53

\ $ \ begingroup \ $
@DavidHarkness VisualDebrix插件实际上将当前文件名中的大写字母作为整个代码的前缀,这样,在同一类中生成的消息具有相同的前缀,而“随机”代码实际上为您-随机线索。对于读取相邻的日志消息很有用
\ $ \ endgroup \ $
– Sten Petrov
14年4月4日在13:45

#6 楼

在日志记录级别,您所描述的调试器似乎更为明智。至少就我本人和我的子领域而言,我们将通过两种方式使用日志记录。


常规INFO和临时DEBUG级别的日志记录,让我们跟踪一个错误到应用程序的特定部分,并提供诸如id的元数据和类似信息。
其次,记录所有(用户)操作,以便可以查看用户的确切操作(既用于错误修复,也可以在客户声称两个月前做过某事,而这本来是行不通的) 。

接下来,可以使用调试器逐行浏览代码,提供您现在正在记录的所有更详细的信息,而无需所有明确的日志行来描述正在发生的事情(如有必要,请在注释中,而不是日志中。)

#7 楼

可能对于您的特定示例,派生dbContext的子类(例如,loggedDBContext)可能有用,该子类使用记录所需信息的代码包装其方法,然后执行“ true”方法。然后,添加/删除日志将包括选择在构造函数(或工厂)中使用dbContext与loggingDBContext,而不更改其他任何内容。 -更改其行为,而无需调用代码甚至不知道功能已更改,并且现在也进行日志记录;但是我不确定如果没有显式子类化,是否可以在C#中完成。