+1  A: 

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

Tom Ritter
That doesn't make it easy to turn debugging (logging) on in the production code.
Jonathan Leffler
You're absolutely right - which is one of the reasons I don't write production C/C++ code.
Tom Ritter
+2  A: 

Pass the log level to the logger and let it decide whether or not to write the log statement:

//if(myLogger.isLoggable(Level.INFO) {myLogger.info("A String");
myLogger.info(Level.INFO,"A String");

UPDATE: Ah, I see that you want to conditionally create the log string without a conditional statement. Presumably at runtime rather than compile time.

I'll just say that the way we've solved this is to put the formatting code in the logger class so that the formatting only takes place if the level passes. Very similar to a built-in sprintf. For example:

myLogger.info(Level.INFO,"A String %d",some_number);

That should meet your criteria.

That's what he's doing... before he goes to the work of formatting the message, which can be an expensive waste if the message won't be logged.
erickson
That doesn't solve the problem of the strings being evaluated and using memory despite logging being turned off.
Tom Ritter
yeap, wrong answer. Our log parameter can be quite heavy to compute...
VonC
Ok. Same answer than @John Millikin.Can you confirm to me than 'some_number' (which actually can be a complicated function returning a string) is **not** evaluated at runtime ?
VonC
+12  A: 

Are guard statements really adding complexity?

Consider excluding logging guards statements from the cyclomatic complexity calculation.

It could be argued that, due to their predictable form, conditional logging checks really don't contribute to the complexity of the code.

Inflexible metrics can make an otherwise good programmer turn bad. Be careful!

Assuming that your tools for calculating complexity can't be tailored to that degree, the following approach may offer a work-around.

The need for conditional logging

I assume that your guard statements were introduced because you had code like this:

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;
}

In Java, each of the log statements creates a new StringBuilder, and invokes the toString() method on each object concatenated to the string. These toString() methods, in turn, are likely to create StringBuilder instances of their own, and invoke the toString() methods of their members, and so on, across a potentially large object graph. (Before Java 5, it was even more expensive, since StringBuffer was used, and all of its operations are synchronized.)

This can be relatively costly, especially if the log statement is in some heavily-executed code path. And, written as above, that expensive message formatting occurs even if the logger is bound to discard the result because the log level is too high.

This leads to the introduction of guard statements of the form:

  if (log.isDebugEnabled())
    log.debug("Attempting connection of dongle " + d + " to widget " + w);

With this guard, the evaluation of arguments d and w and the string concatenation is performed only when necessary.

A solution for simple, efficient logging

However, if the logger (or a wrapper that you write around your chosen logging package) takes a formatter and arguments for the formatter, the message construction can be delayed until it is certain that it will be used, while eliminating the guard statements and their cyclomatic complexity.

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;
  }

}

Now, none of the cascading toString() calls with their buffer allocations will occur unless they are necessary! This effectively eliminates the performance hit that led to the guard statements. One small penalty, in Java, would be auto-boxing of any primitive type arguments you pass to the logger.

The code doing the logging is arguably even cleaner than ever, since untidy string concatenation is gone. It can be even cleaner if the format strings are externalized (using a ResourceBundle), which could also assist in maintenance or localization of the software.

Further enhancements

Also note that, in Java, a MessageFormat object could be used in place of a "format" String, which gives you additional capabilities such as a choice format to handle cardinal numbers more neatly. Another alternative would be to implement your own formatting capability that invokes some interface that you define for "evaluation", rather than the basic toString() method.

erickson
+1 for FormatLogger. Having done something similar I can vouch for usefulness of this approach. In some testing that we've done, there is virtually no performance hit when appropriate logging level is turned off.
javashlook
A: 

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.

quamrana
Hey, if this avoid the evaluation of "A String" at runtime, it is a valid answer (I am just a little rusty on my C/C++ level)
VonC
What would happen if logging is disabled? The resulting object would still need `operator<<`, and so the output operator chain would still be executed. I do not think this answer would help any.
John Millikin
The question includes something like: if(myLogger.isLoggable(Level.INFO) myLogger.info("A String");Now if we just add #define LOGGER(LEVEL) if (myLogger.isLoggable(LEVEL)) myLogger()then assuming 'myLogger()' returns a stream you win all ways. (except you're using macros)
quamrana
I've tried to improve the explanation. Could someone up vote this - I'm sure it doesn't deserve '-1'.
quamrana
Many thanks for the up vote :-)
quamrana
+5  A: 

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.

pointernil
Hey, that's one of the few reasons I've heard that is good enough to make me consider closures in Java.
erickson
Yeap, good theoretical answer... even though I am still coding in plain old java 1.4.2!!! and do not have access to such neat treat. Still, +1 for the suggestion, since the question is indeed language agnostic.
VonC
+10  A: 

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)

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


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.


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)

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.

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 a class LazyGetEverything (My Java's a bit rusty, but this should be close enough):

class MainClass {
    class LazyGetEverything { 
        String toString () { return get_everything ().toString(); }
    }
    [... rest of the code ...]
    {
        log = new MyLogger ();
        log.info ("get_everything() = {0}", new LazyGetEverything ());
    }
}

In this code, the call to get_everything() 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 get_everything() call.

John Millikin
That works. It's cool Python supports this directly, but you could do it in a wrapper if your logging toolkit doesn't do it.
erickson
Woa... thank you for the cleaning job, John. 3000 rep points do help to do that many downvotes ;)I am not sure about your point thought. Do you mean a and b which can be complicated functions returning String are not evaluated during the log function call ?
VonC
No, he means that rather than formatting the message in your own code, pass the data you'd use to do it to the logger, which will do the formatting for you, but only if the statement is enabled. This requires a formatter of some sort, rather than string concatenation.
erickson
I still not get it, sorry."pass the data" ??? That mean the data has to be computed/evaluated, in order to be passed to any kind of formatter mechanism, right ? If yes,... I fail to see how it address my issue (which is not related to format).
VonC
@VonC: i think you should elaborate alittle what's meant with "but because of the log parameters (the strings), which are always calculated," ... do you mean things like MySuperObject.ToString()?
pointernil
@John Millikin thank you for your patience and feedback. Please see my edited question I just completed. I will go on tomorrow, good night ;)(this StackOverflow business is really a 'follow-the-sun' service! You guys never sleep ;) )
VonC
Eh? Not sure what you mean by "cleaning". High-rep users have limited amounts of downvotes also, and I haven't edited any posts in this question. ----- VonC: I'll edit my post to show how lazy formatting helps.
John Millikin
Thank you John, I get it now.For the sake of completeness and just for the anecdote, you can also use pointer of functions, or 'lazy variadic functions!'. But I am only aware of those in the 'D language' as in http://www.digitalmars.com/d/1.0/function.html
VonC
+1  A: 

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 ;) )

VonC
+2  A: 

Maybe this is too simple, but what about using the "extract method" refactoring around the guard clause? Your example code of this:

public void Example()
{
  if(myLogger.isLoggable(Level.INFO))
      myLogger.info("A String");
  if(myLogger.isLoggable(Level.FINE))
      myLogger.fine("A more complicated String");
  // +1 for each test and log message
}

Becomes this:

public void Example()
{
   _LogInfo();
   _LogFine();
   // +0 for each test and log message
}

private void _LogInfo()
{
   if(!myLogger.isLoggable(Level.INFO))
      return;

   // Do your complex argument calculations/evaluations only when needed.
}

private void _LogFine(){ /* Ditto ... */ }
flipdoubt
Your _LogInfo() and _LogFine() methods don't take any parameters for the log message - and if they did, then the cost of concatenating the parameters would still be incurred.
matt b
Those methods don't take parameters because they calculate the parameters only after testing that the parameters should be calculated. The original poster's point was that the app was being adversely effected by the calculation of unnecessary parameters.I want my rep back!
flipdoubt
See the comment:// Do your complex argument calculations/evaluations only when needed.
flipdoubt
You just picked a crappy example.
wowest
Apparently, but I used VonC's example in the original question.
flipdoubt
A: 

Here is an elegant solution using ternary expression

logger.info(logger.isInfoEnabled() ? "Log Statement goes here..." : null);

Using the ternary operator still increases cyclomatic complexity.
mobrule
+1  A: 

alt text

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

oluies