Saturday, February 16, 2013

Lucene AppEngine 4.1 is out

I'm glad to announce the release of new versions of Lucene AppEngine project.
What's new in LAE project:
  • Released LAE 3.6.2 as stable powered by Lucene 3.6.2;
  • Released LAE 4.0.0 as stable powered by Lucene 4.0;
  • Released LAE 4.1.0-SNAPSHOT powered by Lucene 4.1;
  • For each release slf4j framework updated to 1.7.2;
  • All released libraries available into maven repository, see project home page for details;
  • Minor performance improvements/refactoring.

What's new in LAE examples and in Live Demo:
  • Aligned LAE Examples to latest LAE 4.1.0-SNAPSHOT version;
  • Live Demo up and running with latest LAE 4.1.0-SNAPSHOT version.

Enjoy!


Wednesday, November 7, 2012

Java: Bad protected final log

Another post about logging? Yes!
Writing good log statements is something hard to learn, in my honest opinion every log statement should follow these principles:
  • contains complete information: in other words, there's no need to scroll to understand the meaning of the log or to read other related informations;
  • log output is in a single line (see also logging in single line for grep);
  • contains informations about where the log is placed (package and class).
About the latter point I'll share with you some bad techniques I've seen. Some people get tired about logging, so, in order to write less code, they do something like this:

    package foo.bar;
    
    import org.slf4j.*;

    public class SharedClass {
        protected final Logger log = LoggerFactory.getLogger(this.getClass());
        
        public void doLog(int count) {
            log.info("Called doLog with count = '{}'.", 1);
            for (int i = 0; i < count; i++) {
                callAnotherMethod(i);
            }
        }
        public void callAnotherMethod(int i) {
            log.info("Called method with i = '{}'.", i);
        }
    }
    public class SubClass extends SharedClass {
        @Override
        public void callAnotherMethod(int i) {
            log.info("Called method with i = '{}'.", i);
        }
    }
Where's the problem? Run this main and try to understand what happens:
    public class Main {
        public static void main(String[] args) {
            SharedClass parent = new SharedClass();
            SharedClass child = new SubClass();
            child.doLog(10);
            parent.doLog(10);
        }
    }
Pretend that you are reading the log output below without any knowledge of the application, and pretend that you are searching for a bug and the anomaly is on row one (e.g. because count is one but method is called five times):
13:21:40.261 [main] INFO  foo.bar.SubClass - Called doLog with count = '1'.
13:21:40.264 [main] INFO  foo.bar.SubClass - Called method with i = '0'.
13:21:40.264 [main] INFO  foo.bar.SubClass - Called method with i = '1'.
13:21:40.264 [main] INFO  foo.bar.SubClass - Called method with i = '2'.
13:21:40.264 [main] INFO  foo.bar.SubClass - Called method with i = '3'.
13:21:40.264 [main] INFO  foo.bar.SubClass - Called method with i = '4'.
13:21:40.264 [main] INFO  foo.bar.SharedClass - Called doLog with count = '1'.
13:21:40.264 [main] INFO  foo.bar.SharedClass - Called method with i = '0'.
13:21:40.264 [main] INFO  foo.bar.SharedClass - Called method with i = '1'.
13:21:40.264 [main] INFO  foo.bar.SharedClass - Called method with i = '2'.
13:21:40.264 [main] INFO  foo.bar.SharedClass - Called method with i = '3'.
13:21:40.264 [main] INFO  foo.bar.SharedClass - Called method with i = '4'.
Hence you start searching the class... but?! There is no log statement like that in SubClass! And now?! Do you try to search a string like "Called doLog with count"? And what to do if the string doesn't exist because it's composed using concatenation?
Anybody could answer me: "It's simple! There should be a super class", but where? And if a method, somewhere around the code, is using the Logger of another class?

Solution: declare Logger as private static final Logger log, initialized by the same class as where it's declared. Yes the log fields is lower case because it is not a constant, it is a final reference (see also stackoverflow).
    package foo.bar;
    
    import org.slf4j.*;

    public class SharedClass {
        private static final Logger log = LoggerFactory.getLogger(SharedClass.class);
        
        public void doLog(int count) {
            log.info("Called doLog with count = '{}'.", 1);
            for (int i = 0; i < count; i++) {
                callAnotherMethod(i);
            }
        }
        public void callAnotherMethod(int i) {
            log.info("Called method with i = '{}'.", i);
        }
    }
    public class SubClass extends SharedClass {
        private static final Logger log = LoggerFactory.getLogger(SubClass.class);
        @Override
        public void callAnotherMethod(int i) {
            log.info("Called method with i = '{}'.", i);
        }
    }
Eventually we can see a more debuggable log output, like the one below:
13:23:57.351 [main] INFO  foo.bar.SharedClass - Called doLog with count = '1'.
13:23:57.354 [main] INFO  foo.bar.SubClass - Called method with i = '0'.
13:23:57.354 [main] INFO  foo.bar.SubClass - Called method with i = '1'.
13:23:57.354 [main] INFO  foo.bar.SubClass - Called method with i = '2'.
13:23:57.354 [main] INFO  foo.bar.SubClass - Called method with i = '3'.
13:23:57.354 [main] INFO  foo.bar.SubClass - Called method with i = '4'.
13:23:57.354 [main] INFO  foo.bar.SharedClass - Called doLog with count = '1'.
13:23:57.354 [main] INFO  foo.bar.SharedClass - Called method with i = '0'.
13:23:57.354 [main] INFO  foo.bar.SharedClass - Called method with i = '1'.
13:23:57.354 [main] INFO  foo.bar.SharedClass - Called method with i = '2'.
13:23:57.354 [main] INFO  foo.bar.SharedClass - Called method with i = '3'.
13:23:57.354 [main] INFO  foo.bar.SharedClass - Called method with i = '4'.
Thank you everybody for your patience.
A question: what to do if you can't edit all the wrong Logger declarations? Wait for Go to my next post to have the answer! :-)

Related posts:

Sunday, November 4, 2012

SLF4J 1.7 and var-args

Logging logging... logging logging... this log entry doesn't contain complete informations.... ufff! ...new Object[]{....}

Are you bored? I think so. Yes log is something tedious and, in some situations, error prone especially using the log method with an array as input, for details see the post slf4j doesn't log exception stacktrace.

I'm happy to give you all some good news: from now we can avoid using the log methods with an array as input. With the new version of slf4j (version 1.7 and above) now everyone can use the var-args methods!
This can be seen as a minor improvement but it isn't; if you have red my post slf4j doesn't log exception stacktrace, that problem is vanished if you are using var-args methods, or slf4j 1.6 and above.

Conclusion: with slf4j 1.7 now we have var-args methods to log statement as you wish.

Bad news? SLF4j now supports Java 5 and above (imho: At last!).


Related posts:

Friday, November 2, 2012

LAE now ready for Lucene 4.0

I'm glad to announce the new versions of project LAE.
Now LAE is stable for Lucene 3.6.x and ready for Lucene 4.0.x.


For Lucene 3.6.1 the stable version is the 3.6.1
For Lucene 4.0.0 there is a working SNAPSHOT version: 4.0.0-SNAPSHOT

Visit the main LAE project site to view all features.

Related posts:

Monday, September 24, 2012

SLF4J doesn't log Exception stackTrace! Why?

A lot of people knowing slf4j do logging with string formatting for several reasons: mainly readability and performance. Using slf4j (and many other logging frameworks, too) we can transform this log statement:
public class LogTest {
    private static final Logger log = LoggerFactory.getLogger()
    public static void main(String[] args) {
        log.info("Main is started with " + args.length + " arguments");
    }
}
into this:
public class LogTest {
    private static final Logger log = LoggerFactory.getLogger()
    public static void main(String[] args) {
        log.info("Main is started with {} arguments", args.length);
    }
}

The log of an exception should always contain the stack trace, so we can write the following code:
public class LogTest {
    private static final Logger log = LoggerFactory.getLogger()
    public static void main(String[] args) {
        log.info("Main is started with {} arguments", args.length);
        try {
            throw new RuntimeException("Do you now to log my stackTrace?");
        } catch (Exception e) {
            log.error("{} No, I don't!", e.getMessage(), e);
        }
    }
}
Are there any errors? No!? Yes, it compiles and runs as you wish... but?! Where is the stackTrace for the RuntimeException raised? The answer is simple: unfortunately slf4j does not provide methods with signatures like these:
  • [logLevel](String format, Object object, Throwable t)
  • [logLevel](String format, Object[] object, Throwable t)
So one solution is to write code this way:
public class LogTest {
    private static final Logger log = LoggerFactory.getLogger()
    public static void main(String[] args) {
        log.info("Main is started with {} arguments", args.length);
        try {
            throw new RuntimeException("Do you now to log my stackTrace?");
        } catch (Exception e) {
            log.error(e.getMessage() + " Yes, I do!", e);
        }
    }
}

Don't you like it? Below the elegant solution:
public class LogTest {
    private static final Logger log = LoggerFactory.getLogger()
    public static void main(String[] args) {
        log.info("Main is started with {} arguments", args.length);
        try {
            throw new RuntimeException("Do you now to log my stackTrace?");
        } catch (Exception e) {
            log.error("{} Yes, I do!", new Object[] {e.getMessage(), e});
        }
    }
}

The moral of the story is: always be careful about methods' signature.

Related posts:

Tuesday, September 18, 2012

Google Guava and Preconditions

In my previous article I talked about "single line else", now I'll share with you how to avoid the tedious code writing of input control blocks.

Google Guava library allows you to avoid the writing of classic if throw.
For example, code like this:
    public static int division(int a, int b) {
        if(b == 0)
            throw new IllegalArgumentException("Cannot divide by zero.")
        return a / b;
    }

With Guava becomes:
    public static int division(int a, int b) {
        Preconditions.checkArgument(b != 0, "Cannot divide by zero.")
        return a / b;
    }
Pay attention to the condition b != 0, the library allows you to formerly define a precondition so the variable b will be checked for the expected values instead of the exceptional ones.
Guava Preconditions utility methods are a lot, one for each type of exception, so you can write a big variety of if then throw, here's the list:


Related posts:

Saturday, September 15, 2012

Spring Data JPA for Repositories

Do you know repository patterns? Regardless of the answer to this question, you'll like Spring Data JPA.

What is it? A simple framework that lets you avoid the implementation of Repositories over a jpa project.

I don't want to write anything else, the extensive Spring Data JPA documentation should answer to any of your questions, read and enjoy!