Why use lambda expressions in the logging API if the compiler can embed a registration call

Many logging frameworks (e.g. log4j) allow you to pass lambda expressions instead of String to the logging APIs. The argument is that if a string is particularly expressive for construction, the string construction can be lazily performed using the lambda expression. Thus, a line is created only if the syslog level matches the call level.

But, given that modern compilers make many methods, inserting them automatically, does it really make sense to use lambda expressions in this way? I will give a simplified example below to demonstrate this problem.

Suppose our traditional logging method is as follows:

 void log(int level, String message) { if (level >= System.logLevel) System.out.println(message); } // .... System.logLevel = Level.CRITICAL; log(Level.FINE, "Very expensive string to construct ..." + etc); 

Assume that FINE less than CRITICAL , therefore, although an expensive line is built, all this is not so, since the message is not displayed.

Lambda logging APIs help in this situation, so the string is only evaluated (constructed) if necessary:

 void log(int level, Supplier<String> message) { if (level >= System.logLevel) System.out.println(message.apply()); } // .... System.logLevel = Level.CRITICAL; log(Level.FINE, () -> "Very expensive string to construct ..." + etc); 

But is it possible that the compiler can simply embed the logging method so that the network effect is as follows:

 System.logLevel = Level.CRITICAL; if (Level.FINE >= System.logLevel) System.out.println("Very expensive string to construct..." + etc); 

In this case, we do not need to evaluate the string before calling the registration API (because it does not exist), and, presumably, we would get performance only from the attachment.

In conclusion, my question is, how do lambda expressions help us in this situation, given that the compiler can embed logging API calls? The only thing I can think of is that somehow, in the case of lambda, the string is not calculated if the logging level does not match.

+7
java lambda logging
source share
3 answers

Your optimization did not just introduce an attachment - it changed the order. This is not generally accepted.

In particular, it would be wrong to change whether methods will be called if the JIT cannot prove that these methods have no other effect. I would be very surprised if the JIT compiler included and reordered to such an extent - the cost of verifying that all operations associated with constructing a method argument does not have side effects is probably not useful in most cases. (The JIT compiler has no way to view logging methods differently for other methods.)

Therefore, when it’s possible for a really, really smart JIT compiler, I would be very surprised to see that it really is. If you find yourself working with one, and write tests to prove that this approach is not more expensive than using lambda expressions, and continue to prove that it’s great over time - but it looks like you are even more confident that which I definitely won’t.

+9
source share

Raffi lets you take a look at an example of how parsing the compiler you are talking about will change the logic of the program, and the compiler should be smart enough to understand this:

  public String process(){ //do some important bussiness logic return "Done processing"; } 

1) Without inlay, process() will be called regardless of the logging level:

 log( Level.FINE, "Very expensive string to construct ..." + process() ); 

2) With the attachment, process() will be called only at a certain level of logging, and our important business logic will not be able to work:

 if (Level.FINE >= System.logLevel) System.out.println("Very expensive string to construct..." + process() ); 

The compiler in this case should find out how the message string is created, and not embed the method if it calls any other method during its creation.

+1
source share

Such optimization-embedding will only work for really simple examples, as you indicated (when it's just a String concatenation).

In fact, this API can be used in a more complex way:

  public void log(Level level, Supplier<String> msgSupplier) 

Let's say I have a specialized provider that runs a rather expensive log message, creating:

  Supplier<String> supplier = () -> { // really complex stuff }; 

and then I use it in several places:

 LOGGER.log(Level.SEVERE, supplier); ... LOGGER.log(Level.SEVERE, supplier); 

Then what would you do? Deployment - Insert it into

 System.logLevel = Level.CRITICAL; if (Level.FINE >= System.logLevel) System.out.println(supplier.get()); 

makes no sense.

As the java.util.logging.Logger says in the JavaDoc class:

Record the message, which should only be built if the logging level is so that the message will actually be recorded.

So this is the goal: if you can avoid building, you do not need to perform these calculations and pass the result as a parameter.

0
source share

All Articles