- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
读完“What’s your/a good limit for cyclomatic complexity?”后,我意识到我的许多同事对这个新的QA感到非常恼火。我们项目的政策:不再有 10 cyclomatic complexity每个功能。
含义:不超过10个'if'、'else'、'try'、'catch'等代码工作流分支语句。正确的。正如我在'Do you test private method?中解释的那样',这样的政策有很多好的副作用。
但是:在我们(200 人 - 7 年)项目开始时,我们很高兴进行日志记录(不,我们不能轻易地将其委托(delegate)给某种“Aspect-oriented programming”日志方法)。
myLogger.info("A String");
myLogger.fine("A more complicated String");
...
当我们的系统的第一个版本上线时,我们遇到了巨大的内存问题,不是因为日志记录(它一度被关闭),而是因为日志参数(字符串),这些值总是被计算出来,然后传递给“info()”或“fine()”函数,结果却发现日志记录级别为“OFF”,并且没有发生任何日志记录!
所以 QA 回来敦促我们的程序员进行条件日志记录。永远。
if(myLogger.isLoggable(Level.INFO) { myLogger.info("A String");
if(myLogger.isLoggable(Level.FINE) { myLogger.fine("A more complicated String");
...
但是现在,由于每个函数的“无法移动”10 圈复杂度级别限制,他们认为他们放入函数中的各种日志被认为是一种负担,因为每个“if(isLoggable() )"被计为 +1 圈复杂度!
因此,如果一个函数在一个紧密耦合的不易共享的算法中具有 8 个“if”、“else”等,以及 3 个关键日志操作……它们就违反了限制,即使条件日志可能并不是该函数的复杂性的一部分......
您将如何解决这种情况?
我在我的项目中看到了一些有趣的编码演变(由于“冲突”),但我只想先了解您的想法。
感谢您的所有回答。
我必须坚持认为问题不是与“格式”相关,而是与“参数评估”相关(在调用一个什么都不做的方法之前进行评估可能会非常昂贵)
因此,当 a 在上面写“A String”时,我实际上指的是 aFunction(),aFunction() 返回一个 String,并调用一个复杂的方法来收集和计算要由记录器显示的所有类型的日志数据......或不(因此存在问题,以及使用条件日志记录的义务,因此人为增加“圈复杂度”的实际问题......)
我现在得到 ' variadic你们中的一些人提出了“功能”观点(谢谢约翰)。
注意:java6 中的快速测试表明我的 varargs function在调用之前会评估其参数,因此它不能应用于函数调用,但适用于“日志检索器对象”(或“函数包装器”),只有在需要时才会调用 toString() 。明白了。
我现在已经发布了我在这个主题上的经验。
我会把它留在那里直到下周二投票,然后我会选择你的答案之一。
再次感谢您的所有建议:)
最佳答案
当前的日志框架(如 slf4j 或 log4j 2)在大多数情况下不需要保护语句。它们使用参数化日志语句,以便可以无条件记录事件,但仅在启用事件时才会进行消息格式化。消息构造是由记录器根据需要执行的,而不是由应用程序抢先执行的。
如果您必须使用旧的日志记录库,您可以继续阅读以获取更多背景知识以及使用参数化消息改造旧库的方法。
考虑从圈复杂度计算中排除日志保护语句。
可以说,由于其可预测的形式,条件日志检查实际上不会增加代码的复杂性。
不灵活的指标可能会让原本优秀的程序员变坏。小心!
假设您的计算复杂性的工具无法定制到这种程度,以下方法可能会提供解决方法。
我认为引入你的保护语句是因为你有这样的代码:
private static final Logger log = Logger.getLogger(MyClass.class);
Connection connect(Widget w, Dongle d, Dongle alt)
throws ConnectionException
{
log.debug("Attempting connection of dongle " + d + " to widget " + w);
Connection c;
try {
c = w.connect(d);
} catch(ConnectionException ex) {
log.warn("Connection failed; attempting alternate dongle " + d, ex);
c = w.connect(alt);
}
log.debug("Connection succeeded: " + c);
return c;
}
在 Java 中,每个日志语句都会创建一个新的 StringBuilder
,并在连接到字符串的每个对象上调用 toString()
方法。这些 toString()
方法可能会创建自己的 StringBuilder
实例,并调用其成员的 toString()
方法,等等,跨越一个可能很大的对象图。 (在 Java 5 之前,它的成本更高,因为使用了 StringBuffer
,并且它的所有操作都是同步的。)
这可能成本相对较高,尤其是当日志语句位于某些执行频繁的代码路径中时。而且,如上所述,即使记录器由于日志级别太高而必然丢弃结果,也会发生昂贵的消息格式化。
这导致引入以下形式的保护语句:
if (log.isDebugEnabled())
log.debug("Attempting connection of dongle " + d + " to widget " + w);
使用此保护,仅在必要时才执行参数 d
和 w
的计算以及字符串连接。
但是,如果记录器(或围绕所选日志记录包编写的包装器)采用格式化程序和格式化程序的参数,则可以延迟消息构造,直到确定将使用它为止,同时消除防护语句及其圈复杂度。
public final class FormatLogger
{
private final Logger log;
public FormatLogger(Logger log)
{
this.log = log;
}
public void debug(String formatter, Object... args)
{
log(Level.DEBUG, formatter, args);
}
… &c. for info, warn; also add overloads to log an exception …
public void log(Level level, String formatter, Object... args)
{
if (log.isEnabled(level)) {
/*
* Only now is the message constructed, and each "arg"
* evaluated by having its toString() method invoked.
*/
log.log(level, String.format(formatter, args));
}
}
}
class MyClass
{
private static final FormatLogger log =
new FormatLogger(Logger.getLogger(MyClass.class));
Connection connect(Widget w, Dongle d, Dongle alt)
throws ConnectionException
{
log.debug("Attempting connection of dongle %s to widget %s.", d, w);
Connection c;
try {
c = w.connect(d);
} catch(ConnectionException ex) {
log.warn("Connection failed; attempting alternate dongle %s.", d);
c = w.connect(alt);
}
log.debug("Connection succeeded: %s", c);
return c;
}
}
现在,不会发生任何带有缓冲区分配的级联 toString()
调用,除非有必要!这有效地消除了导致 Guard 语句的性能影响。在 Java 中,一个小的缺点是对传递给记录器的任何基本类型参数进行自动装箱。
可以说,执行日志记录的代码比以往任何时候都更加干净,因为不整齐的字符串连接已经消失了。如果格式字符串被外部化(使用ResourceBundle),它会更干净,这也有助于软件的维护或本地化。
另请注意,在 Java 中,可以使用 MessageFormat
对象来代替“格式”String
,它为您提供了额外的功能,例如选择格式更巧妙地处理基数。另一种选择是实现您自己的格式化功能,该功能调用您为“评估”定义的某些接口(interface),而不是基本的 toString()
方法。
关于language-agnostic - 具有最小圈复杂度的条件日志记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/105852/
我一直在阅读Code Complete 2 .由于我不是以英语为母语的人,因此我需要一些时间才能理解某些陈述。我希望你描述作者在他的书中所做的这两个陈述之间的区别: You should progra
我将 TYPO3 安装从 8.6 升级到 9.5。在内容语言设置为“所有语言”的页面上,这些元素不会显示在前端。 似乎在 ContentObjectRenderer->getQueryConstrai
我和一个学生正在建立一个新的口译员家庭,我们希望以现有口译员为基准。我们很乐意下载包含 Computer Language Benchmarks Game 中使用的所有源代码的 tarball 或 z
因此,我正在尝试创建一个双语网站,只需单击一下即可将其从英语切换为葡萄牙语,反之亦然。 我收到此错误:Use of undefined constant language - assumed 'lan
我正在阅读一个比较不同编程语言的演示文稿。衡量的特征之一是“语言表现”。什么意思? 最佳答案 真正的答案:它没有任何意义,或者该演示文稿的作者当时在想什么。 也许它的意思是“执行相对于其他语言具有相同
Go talk 2009 pdf 有评论解释了他们为什么想出 go 语言: No new major systems language in a decade. 系统语言是什么意思? 它是一种应该通过
我现在正在使用 Squeak4.1 学习 SmallTalk。我使用 Squeak by Example 作为教程,在这里我反驳了一个 delema,“Morphic 是由...开发的,用于自编程语言
谁能告诉我如何编译和运行在 ZOMBIE 上编写的程序?例如,如果我编写了以下源代码线以将“hello world”打印到屏幕上,我该如何制作一个 .exe 文件。谢谢! HelloWorld is
我在看帖子Why Language is Important (Why I prefer C#)来自“Dot Net Thoughts”,文章的第一段以以下声明结尾: ...every languag
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 7年前关闭。 Improve t
我正在尝试使用 qTranslate X 制作一个基本的语言切换器,例如: FR | EN 有一个函数可以实现这一点:qtranxf_generateLanguageSelectCode('text'
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 9 年前。 Improv
我正在研究浏览器指纹。在应用程序的 webview 中运行的 js 将通过获取 navigator.language.substr(0,2) == navigator.languages[0].sub
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
"Page description language"、"markup language"、"Page description markup language"有什么区别和关系? 根据他们的 wiki
我正在为 IntelliJ 创建一个自定义语言插件。 我希望新语言的文件可以包含其他语言的文本片段。 我想支持的特定语言是 HTML、JS、CSS 和 SQL。 我还想支持其他自定义语言(即我要为其定
我正在用 mysql 创建一个图书数据库。一本书可以有不同的语言。并且将会有数千本书。 (而且我远不是这个领域的专家,之前只创建过小型和简单的数据库) 1) 只创建一个包含语言列的表并为其创建索引是否
我的网站使用用户可切换的三种不同语言。语言切换由 JavaScript (AngularJS) 在客户端完成。 我在我的网站上使用 reCAPTCHA 2,当用户切换网站语言时需要更改 reCAPTC
我在 Core Image Kernel Language 中有以下函数,我需要在 Metal Shading Language 中等效的东西,但是我对 destCoord 、 unpremultip
“不存在‘编译语言’或‘解释语言’之类的东西。语言实现者选择编写编译器、解释器还是两者之间的任何东西都是实现细节,与语言无关。” 上述说法是否属实? 最佳答案 是的,在最严格的解释中它是正确的。您可以
我是一名优秀的程序员,十分优秀!