Conditional logging with minimal cyclomatic complexity

前端 未结 12 1840
情歌与酒
情歌与酒 2020-11-27 10:58

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

相关标签:
12条回答
  • 2020-11-27 11:15


    (source: scala-lang.org)

    Scala has a annontation @elidable() that allows you to remove methods with a compiler flag.

    With the scala REPL:

    C:>scala

    Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1. 6.0_16). Type in expressions to have them evaluated. Type :help for more information.

    scala> import scala.annotation.elidable import scala.annotation.elidable

    scala> import scala.annotation.elidable._ import scala.annotation.elidable._

    scala> @elidable(FINE) def logDebug(arg :String) = println(arg)

    logDebug: (arg: String)Unit

    scala> logDebug("testing")

    scala>

    With elide-beloset

    C:>scala -Xelide-below 0

    Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1. 6.0_16). Type in expressions to have them evaluated. Type :help for more information.

    scala> import scala.annotation.elidable import scala.annotation.elidable

    scala> import scala.annotation.elidable._ import scala.annotation.elidable._

    scala> @elidable(FINE) def logDebug(arg :String) = println(arg)

    logDebug: (arg: String)Unit

    scala> logDebug("testing")

    testing

    scala>

    See also Scala assert definition

    0 讨论(0)
  • 2020-11-27 11:19

    As much as I hate macros in C/C++, at work we have #defines for the if part, which if false ignores (does not evaluate) the following expressions, but if true returns a stream into which stuff can be piped using the '<<' operator. Like this:

    LOGGER(LEVEL_INFO) << "A String";
    

    I assume this would eliminate the extra 'complexity' that your tool sees, and also eliminates any calculating of the string, or any expressions to be logged if the level was not reached.

    0 讨论(0)
  • 2020-11-27 11:20

    In languages supporting lambda expressions or code blocks as parameters, one solution for this would be to give just that to the logging method. That one could evaluate the configuration and only if needed actually call/execute the provided lambda/code block. Did not try it yet, though.

    Theoretically this is possible. I would not like to use it in production due to performance issues i expect with that heavy use of lamdas/code blocks for logging.

    But as always: if in doubt, test it and measure the impact on cpu load and memory.

    0 讨论(0)
  • 2020-11-27 11:20

    Conditional logging is evil. It adds unnecessary clutter to your code.

    You should always send in the objects you have to the logger:

    Logger logger = ...
    logger.log(Level.DEBUG,"The foo is {0} and the bar is {1}",new Object[]{foo, bar});
    

    and then have a java.util.logging.Formatter that uses MessageFormat to flatten foo and bar into the string to be output. It will only be called if the logger and handler will log at that level.

    For added pleasure you could have some kind of expression language to be able to get fine control over how to format the logged objects (toString may not always be useful).

    0 讨论(0)
  • 2020-11-27 11:21

    In C or C++ I'd use the preprocessor instead of the if statements for the conditional logging.

    0 讨论(0)
  • 2020-11-27 11:27

    Thank you for all your answers! You guys rock :)

    Now my feedback is not as straight-forward as yours:

    Yes, for one project (as in 'one program deployed and running on its own on a single production platform'), I suppose you can go all technical on me:

    • dedicated 'Log Retriever' objects, which can be pass to a Logger wrapper only calling toString() is necessary
    • used in conjunction with a logging variadic function (or a plain Object[] array!)

    and there you have it, as explained by @John Millikin and @erickson.

    However, this issue forced us to think a little about 'Why exactly we were logging in the first place ?'
    Our project is actually 30 different projects (5 to 10 people each) deployed on various production platforms, with asynchronous communication needs and central bus architecture.
    The simple logging described in the question was fine for each project at the beginning (5 years ago), but since then, we has to step up. Enter the KPI.

    Instead of asking to a logger to log anything, we ask to an automatically created object (called KPI) to register an event. It is a simple call (myKPI.I_am_signaling_myself_to_you()), and does not need to be conditional (which solves the 'artificial increase of cyclomatic complexity' issue).

    That KPI object knows who calls it and since he runs from the beginning of the application, he is able to retrieve lots of data we were previously computing on the spot when we were logging.
    Plus that KPI object can be monitored independently and compute/publish on demand its information on a single and separate publication bus.
    That way, each client can ask for the information he actually wants (like, 'has my process begun, and if yes, since when ?'), instead of looking for the correct log file and grepping for a cryptic String...

    Indeed, the question 'Why exactly we were logging in the first place ?' made us realize we were not logging just for the programmer and his unit or integration tests, but for a much broader community including some of the final clients themselves. Our 'reporting' mechanism had to be centralized, asynchronous, 24/7.

    The specific of that KPI mechanism is way out of the scope of this question. Suffice it to say its proper calibration is by far, hands down, the single most complicated non-functional issue we are facing. It still does bring the system on its knee from time to time! Properly calibrated however, it is a life-saver.

    Again, thank you for all the suggestions. We will consider them for some parts of our system when simple logging is still in place.
    But the other point of this question was to illustrate to you a specific problem in a much larger and more complicated context.
    Hope you liked it. I might ask a question on KPI (which, believe or not, is not in any question on SOF so far!) later next week.

    I will leave this answer up for voting until next Tuesday, then I will select an answer (not this one obviously ;) )

    0 讨论(0)
提交回复
热议问题