具有最小圈复杂度的条件日志记录

本文介绍了具有最小圈复杂度的条件日志记录的处理方法,对大家解决问题具有一定的参考价值

问题描述

阅读后你对圈复杂度的/一个好的限制是什么?",我意识到我的许多同事对这个新的 QA 项目的政策:不再有 10 每个函数的圈复杂度.

After reading "What’s your/a good limit for cyclomatic complexity?", I realize many of my colleagues were quite annoyed with this new QA policy on our project: no more 10 cyclomatic complexity per function.

含义:不超过10个'if'、'else'、'try'、'catch'等代码工作流分支语句.对.正如我在您是否测试私有方法?"中所述,这样的政策有很多好的副作用.

Meaning: no more than 10 'if', 'else', 'try', 'catch' and other code workflow branching statement. Right. As I explained in 'Do you test private method?', such a policy has many good side-effects.

但是:在我们(200 人 - 7 年)项目开始时,我们很高兴地进行日志记录(不,我们不能轻易将其委托给某种 '面向方面的编程'日志方法).

But: At the beginning of our (200 people - 7 years long) project, we were happily logging (and no, we can not easily delegate that to some kind of 'Aspect-oriented programming' approach for logs).

myLogger.info("A String");
myLogger.fine("A more complicated String");
...

当我们系统的第一个版本上线时,我们遇到了巨大的内存问题,不是因为日志记录(曾经被关闭),而是因为日志参数(字符串),它们总是被计算,然后传递给 'info()' 或 'fine()' 函数,结果发现日志级别为 'OFF',并且没有进行日志记录!

And when the first versions of our System went live, we experienced huge memory problem not because of the logging (which was at one point turned off), but because of the log parameters (the strings), which are always calculated, then passed to the 'info()' or 'fine()' functions, only to discover that the level of logging was 'OFF', and that no logging were taking place!

因此 QA 回来并敦促我们的程序员进行条件记录.总是.

So QA came back and urged our programmers to do conditional logging. Always.

if(myLogger.isLoggable(Level.INFO) { myLogger.info("A String");
if(myLogger.isLoggable(Level.FINE) { myLogger.fine("A more complicated String");
...

但是现在,由于每个函数限制的无法移动"10 圈复杂度级别,他们争辩说,他们放入函数中的各种日志被视为一种负担,因为每个if(isLoggable())" 算作 +1 圈复杂度!

But now, with that 'can-not-be-moved' 10 cyclomatic complexity level per function limit, they argue that the various logs they put in their function is felt as a burden, because each "if(isLoggable())" is counted as +1 cyclomatic complexity!

因此,如果一个函数有 8 个if"、else"等,在一个紧密耦合的不易共享的算法中,以及 3 个关键日志操作......即使条件日志可能会违反限制不要真正成为该功能复杂性的一部分...

So if a function has 8 'if', 'else' and so on, in one tightly-coupled not-easily-shareable algorithm, and 3 critical log actions... they breach the limit even though the conditional logs may not be really part of said complexity of that function...

您将如何解决这种情况?
我在我的项目中看到了一些有趣的编码演变(由于冲突"),但我只想先了解您的想法.

How would you address this situation ?
I have seen a couple of interesting coding evolution (due to that 'conflict') in my project, but I just want to get your thoughts first.

谢谢大家的回答.
我必须坚持认为问题与格式"无关,而与参数评估"相关(评估可能非常昂贵,就在调用什么都不做的方法之前)
所以当 a 写在A String"上面时,我实际上是指 aFunction(),aFunction() 返回一个 String,并调用一个复杂的方法来收集和计算要由记录器显示的所有类型的日志数据......与否(因此是问题,以及义务使用条件日志记录,因此是人为增加圈复杂度"的实际问题......)

Thank you for all the answers.
I must insist that the problem is not 'formatting' related, but 'argument evaluation' related (evaluation that can be very costly to do, just before calling a method which will do nothing)
So when a wrote above "A String", I actually meant aFunction(), with aFunction() returning a String, and being a call to a complicated method collecting and computing all kind of log data to be displayed by the logger... or not (hence the issue, and the obligation to use conditional logging, hence the actual issue of artificial increase of 'cyclomatic complexity'...)

我现在得到了你们中的一些人提出的可变参数 函数"点(谢谢约翰).
注意:java6 中的快速测试表明我的 varargs 函数 确实在被调用之前评估其参数,因此它不能应用于函数调用,但适用于日志检索器对象"(或函数包装器"),只有在需要时才会调用 toString().明白了.

I now get the 'variadic function' point advanced by some of you (thank you John).
Note: a quick test in java6 shows that my varargs function does evaluate its arguments before being called, so it can not be applied for function call, but for 'Log retriever object' (or 'function wrapper'), on which the toString() will only be called if needed. Got it.

我现在已经发布了我在这个主题上的经验.
我会把它留到下周二投票,然后我会选择你的一个答案.
再次感谢您的所有建议:)

I have now posted my experience on this topic.
I will leave it there until next Tuesday for voting, then I will select one of your answers.
Again, thank you for all the suggestions :)

推荐答案

在 Python 中,您将格式化的值作为参数传递给日志记录函数.仅当启用日志记录时才应用字符串格式.仍然存在函数调用的开销,但与格式化相比,这是微不足道的.

In Python you pass the formatted values as parameters to the logging function. String formatting is only applied if logging is enabled. There's still the overhead of a function call, but that's minuscule compared to formatting.

log.info ("a = %s, b = %s", a, b)

您可以对任何带有可变参数的语言(C/C++、C#/Java 等)执行类似的操作.

You can do something like this for any language with variadic arguments (C/C++, C#/Java, etc).

这并不是真正用于参数难以检索的情况,而是用于将它们格式化为字符串成本高昂的情况.例如,如果您的代码中已经有一个数字列表,您可能希望记录该列表以进行调试.执行 mylist.toString() 将需要一段时间没有任何好处,因为结果将被丢弃.因此,您将 mylist 作为参数传递给日志记录函数,并让它处理字符串格式.这样,只有在需要时才会执行格式化.

This isn't really intended for when the arguments are difficult to retrieve, but for when formatting them to strings is expensive. For example, if your code already has a list of numbers in it, you might want to log that list for debugging. Executing mylist.toString() will take a while to no benefit, as the result will be thrown away. So you pass mylist as a parameter to the logging function, and let it handle string formatting. That way, formatting will only be performed if needed.

由于 OP 的问题特别提到了 Java,以下是如何使用上述内容:

Since the OP's question specifically mentions Java, here's how the above can be used:

我必须坚持认为问题与格式"无关,而与参数评估"相关(在调用什么也不做的方法之前进行评估可能会非常昂贵)

I must insist that the problem is not 'formatting' related, but 'argument evaluation' related (evaluation that can be very costly to do, just before calling a method which will do nothing)

诀窍是让对象在绝对需要之前不会执行昂贵的计算.这在支持 lambda 和闭包的 Smalltalk 或 Python 等语言中很容易,但在 Java 中仍然可行,但需要一点想象力.

The trick is to have objects that will not perform expensive computations until absolutely needed. This is easy in languages like Smalltalk or Python that support lambdas and closures, but is still doable in Java with a bit of imagination.

假设您有一个函数 get_everything().它将从您的数据库中检索每个对象到一个列表中.显然,如果结果将被丢弃,您不想调用它.因此,不是直接调用该函数,而是定义一个名为 LazyGetEverything 的内部类:

Say you have a function get_everything(). It will retrieve every object from your database into a list. You don't want to call this if the result will be discarded, obviously. So instead of using a call to that function directly, you define an inner class called LazyGetEverything:

public class MainClass {
    private class LazyGetEverything { 
        @Override
        public String toString() { 
            return getEverything().toString(); 
        }
    }

    private Object getEverything() {
        /* returns what you want to .toString() in the inner class */
    }

    public void logEverything() {
        log.info(new LazyGetEverything());
    }
}

在这段代码中,对 getEverything() 的调用被包装起来,因此它在需要之前不会被实际执行.只有在启用调试时,日志记录函数才会对其参数执行 toString().这样,您的代码将只承受函数调用的开销,而不是完整的 getEverything() 调用.

In this code, the call to getEverything() is wrapped so that it won't actually be executed until it's needed. The logging function will execute toString() on its parameters only if debugging is enabled. That way, your code will suffer only the overhead of a function call instead of the full getEverything() call.

这篇关于具有最小圈复杂度的条件日志记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,WP2

admin_action_{$_REQUEST[‘action’]}

do_action( "admin_action_{$_REQUEST[‘action’]}" )动作钩子::在发送“Action”请求变量时激发。Action Hook: Fires when an ‘action’ request variable is sent.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$_REQUEST['action']引用从GET或POST请求派生的操作。源码(Source)更新版本源码位置使用被使用2.6.0 wp-admin/admin.php:...

日期:2020-09-02 17:44:16 浏览:1159

admin_footer-{$GLOBALS[‘hook_suffix’]}

do_action( "admin_footer-{$GLOBALS[‘hook_suffix’]}", string $hook_suffix )操作挂钩:在默认页脚脚本之后打印脚本或数据。Action Hook: Print scripts or data after the default footer scripts.目录锚点:#说明#参数#源码说明(Description)钩子名的动态部分,$GLOBALS['hook_suffix']引用当前页的全局钩子后缀。参数(Parameters)参数类...

日期:2020-09-02 17:44:20 浏览:1060

customize_save_{$this->id_data[‘base’]}

do_action( "customize_save_{$this->id_data[‘base’]}", WP_Customize_Setting $this )动作钩子::在调用WP_Customize_Setting::save()方法时激发。Action Hook: Fires when the WP_Customize_Setting::save() method is called.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_data...

日期:2020-08-15 15:47:24 浏览:799

customize_value_{$this->id_data[‘base’]}

apply_filters( "customize_value_{$this->id_data[‘base’]}", mixed $default )过滤器::过滤未作为主题模式或选项处理的自定义设置值。Filter Hook: Filter a Customize setting value not handled as a theme_mod or option.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_date['base'],指的是设置...

日期:2020-08-15 15:47:24 浏览:885

get_comment_author_url

过滤钩子:过滤评论作者的URL。Filter Hook: Filters the comment author’s URL.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/comment-template.php:32610...

日期:2020-08-10 23:06:14 浏览:925

network_admin_edit_{$_GET[‘action’]}

do_action( "network_admin_edit_{$_GET[‘action’]}" )操作挂钩:启动请求的处理程序操作。Action Hook: Fires the requested handler action.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$u GET['action']引用请求的操作的名称。源码(Source)更新版本源码位置使用被使用3.1.0 wp-admin/network/edit.php:3600...

日期:2020-08-02 09:56:09 浏览:872

network_sites_updated_message_{$_GET[‘updated’]}

apply_filters( "network_sites_updated_message_{$_GET[‘updated’]}", string $msg )筛选器挂钩:在网络管理中筛选特定的非默认站点更新消息。Filter Hook: Filters a specific, non-default site-updated message in the Network admin.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分$_GET['updated']引用了非默认的...

日期:2020-08-02 09:56:03 浏览:854

pre_wp_is_site_initialized

过滤器::过滤在访问数据库之前是否初始化站点的检查。Filter Hook: Filters the check for whether a site is initialized before the database is accessed.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/ms-site.php:93910...

日期:2020-07-29 10:15:38 浏览:825

WordPress 的SEO 教学:如何在网站中加入关键字(Meta Keywords)与Meta 描述(Meta Description)?

你想在WordPress 中添加关键字和meta 描述吗?关键字和meta 描述使你能够提高网站的SEO。在本文中,我们将向你展示如何在WordPress 中正确添加关键字和meta 描述。为什么要在WordPress 中添加关键字和Meta 描述?关键字和说明让搜寻引擎更了解您的帖子和页面的内容。关键词是人们寻找您发布的内容时,可能会搜索的重要词语或片语。而Meta Description则是对你的页面和文章的简要描述。如果你想要了解更多关于中继标签的资讯,可以参考Google的说明。Meta 关键字和描...

日期:2020-10-03 21:18:25 浏览:1691

谷歌的SEO是什么

SEO (Search Engine Optimization)中文是搜寻引擎最佳化,意思近于「关键字自然排序」、「网站排名优化」。简言之,SEO是以搜索引擎(如Google、Bing)为曝光媒体的行销手法。例如搜寻「wordpress教学」,会看到本站的「WordPress教学:12个课程…」排行Google第一:关键字:wordpress教学、wordpress课程…若搜寻「网站架设」,则会看到另一个网页排名第1:关键字:网站架设、架站…以上两个网页,每月从搜寻引擎导入自然流量,达2万4千:每月「有机搜...

日期:2020-10-30 17:23:57 浏览:1297