Logging API
Brian Goetz
brian.goetz at oracle.com
Tue May 22 13:17:08 PDT 2012
Yes, some time ago we ran a "point lambdafication" survey on this list,
and this was the #1 suggestion. So rest assured, it is on the list of
ideas.
I'd also note that the primary goal of such a change is performance, and
the story there isn't as perfect as you might prefer. Capturing a
lambda which captures variables from the enclosing scope has a cost,
comparable to an object allocation. (On the other hand, capturing
non-capturing lambdas, which will be the common case for many
filter/map/reduce type of operations, should be basically free.) So
while you defer creating the string until you need it, you do not
necessarily defer creating the lambda (though the VM may be able to
optimize that away in some cases.) So the "Factory<String> approach"
will be faster than just precomputing the string unconditionally, but
probably slower than the commonly used trick of preceding the logging
call with an "if (logger.level() >= DEBUG)" statement. So if you were
thinking this would be a totally free way to replace those ugly if
statements, you might be disappointed.
Of course, there's lots the VM might do to help in the future. But
that's the future.
On 5/22/2012 4:01 PM, Adam Hawthorne wrote:
> I don't know if it's premature to discuss non-Collections APIs, but in
> trying out Lambda, I encountered problems using java.util.logging.Logger.
> Here's some sample code:
>
>
> import static java.util.logging.Level.INFO;
> import static LambdaTest.Stringifier._;
>
> import java.util.concurrent.Callable;
> import java.util.logging.Logger;
>
> public class LambdaTest {
> public static void main(String... argv) {
> // Fails to compile, no suitable method
> // Logger.getAnonymousLogger().log(INFO, "Hello {0}", () -> "World");
>
> // Fails to compile, java.lang.Object method restrictions
> // ToString arg1 = () -> "World";
> // Logger.getAnonymousLogger().log(INFO, "Hello {0}", arg1);
>
> // Does not produce "hoped for" output, Callable<String>.get()
> isn't the same as toString()
> Callable<String> arg2 = () -> "World";
> Logger.getAnonymousLogger().log(INFO, "Hello {0}", arg2);
>
> // Does not produce "hoped for" output, Object.toString() is
> defined.
> Stringer arg3 = () -> "World";
> Logger.getAnonymousLogger().log(INFO, "Hello {0}", arg3);
>
> // Works, but hokey/extra allocation
> Logger.getAnonymousLogger().log(INFO, "Hello {0}", _(() ->
> "World"));
> }
>
> public interface ToString {
> String toString();
> }
>
> public interface Stringer {
> String string();
>
> String toString() default {
> return string();
> }
> }
>
> public static class Stringifier {
> private final Callable<String> val;
>
> public Stringifier(Callable<String> val) {
> this.val = val;
> }
>
> @Override
> public String toString() {
> try {
> return val.call();
> } catch (Exception e) {
> throw new RuntimeException(e);
> }
> }
>
> public static Stringifier _(Callable<String> val) {
> return new Stringifier(val);
> }
> }
> }
>
> One of the nice things about the logging API is that it doesn't actually
> invoke toString() on the arguments unless the log level is actually
> enabled. Lambdas are obvious choices for this kind of delayed execution,
> but I tried to do battle against Logging and I lost.
>
> It seems to me that option 4 should work, although I understand from
> Defenders section 3, Object.toString() takes precedence over the defender
> toString() in Stringer. It would be helpful if using the Logging API was
> more friendly towards Lambdas.
>
> Adam
>
More information about the lambda-dev
mailing list