views:

271

answers:

6

I am a pretty new user of Log4J v. 1.2.15, as a friend of mine convinced me of the advantages over console or other forms of logging. However, as i was doing some tests, i've encountered a test case that got me thinking. Here it is what i did :

  1. I've configured the properties, added 2 appenders, a ConsoleAppender and a RollingFileAppender.
  2. I've created a new log instance, using my main class : Logger mainLogger = Logger.getLogger(Main.class);
  3. I have this collection of general-purpose, hand-made, java utils, in a library, called MyUtils.jar, added to the classpath of my main app. In the main app, i've called a static method from MyUtils.jar. This method has a try-catch{} block and the exception was handled there, printing the stack trace, using System.err. Now, using my IDE and the ConsoleAppender, I was able to spot the problem, however, the event was NOT logged in my file log. There are 2 problems that need answers here: a. I am currently using my mainLogger to log events from all classes of my app. Is that a good practice? Or should i use X logger instances for X classes? b. What can I do to be able to log errors already caught in my imports? It might sounds trivial, but I've used Object foo = MyLibrary.composeObjectFoo(), and inside the method looks similar to this example :

    public static Object composeObjectFoo() {
        try {
            .....statements.....
            .
            .
            //    something stupid here
            int a = 100/0; //an AritmethicException will be thrown 
        } catch Exception(e) {
            e.printStackTrace();
        }
    }
    

Thanks for your answers and please excuse the length of this...


A: 

I did one logical thing..i've deleted the try->catch{} block from my Utils class. The generated exception now was catched by the main app and logged. Awesome! But...is there any guarantee that the situation will not repeat itself when using libs/utils created by third partys?

I suggest you delete this answer and add it as a comment to your question.
SingleShot
I would do that if i knew where is the delete button here :-(.
Don't you have "link|edit|delete|flag" links (yeah, links, not buttons) below the answer?
Pascal Thivent
I only have the link| edit| flag buttons :-(.
+3  A: 

There are a few things I recommend about logging:

  1. Never use System.out.println(), System.err.println(), or e.printStackTrace(). Instead, use a logger. You can configure the logger to print to stdout or stderr instead, not to mention filter out unimportant information. Much more flexible.
  2. It is a best practice to create a logger in each class that can generate events you would want to log, and to use the class to create the logger name (e.g. Logger.getLogger(Main.class)). By doing this, you can then use all those unique logger names to fine-tune your log filtering. For example, you can set class A to debug and class B to info if needed.
  3. Use a logger abstraction layer like slf4j (I really like this) or commons-logging. This will allow you to change your logger implementation without having to go through all your code and change every location where you log from.

Regarding exceptions:

I also recommend you read up a bit on exception handling best practices. Everyone will argue about what is "best" but should all agree that you should do more than let everything leak up to main(). I like what this article has to say.

SingleShot
Thanks for the quick reply. I agree, logger gives u more freedom. But 1 question remains, and u raised another :-) :1. It is possible to log the output of the Utils class, using something like Logger l = Logger.getLogger(Utils.class), outside the Utils ?2. I actually need to get the Sys.err/Sys.out output to save it to my configured-per-app-instance log file, regardless of the class that generates that output. I'll check it out slf4j, sounds great, however, i doubt that i will change my logger soon..but..just to have the mentioned option in the future..
Logger.getLogger(Utils.class) simply creates a logger named "your.package.Utils". It will not intercept output in any way. You have to add log statements to it. Regarding out/err, you can have each app instance log with log4j using its own config file or own file appender. Also - see my exception handling comment added above.
SingleShot
I was guessing that Using the Logger.getLogger(Utils.class) will do just that by looking at the Logger.getLogger(String s) method. Yes, i agree, handling exceptions is a weak area to me, but i'll improve. By adding 1 logger to every class (let's assume overall 2000 classes), wouldnt that take too much resources? What should i do if the SWT library for instance throws and exception and i didnt catch that? The app could close and i would have no clue why or where that happen :-(.
A: 

You can't log an exception caught in an imported library. You can't add code to the imported library. Once it's caught, you're done. The best you can hope for is that the imported library re-throws an exception and gives you a chance to log something.

I'd wonder about the quality of an imported library that DIDN'T do logging and wrote to the console instead. That's not a good design concept.

I don't like the idea of a main class doing logging. Your instinct is good, but your implementation is not. Logging is a cross-cutting concern that should be done using aspect-oriented programming.

duffymo
The way i see it, the people who wrote the libraries, pay a lot more attention that i usually do when dealing with certain aspects like Exception handling. And that's a good thing..it means there is a big space for assimilating more good practices and drop bad habits. I have chosen to write a single class and hardcoded the logger params there. If i ever need to change a setting, i could simply edit that file, instead of the .properties file, giving me a better control of the logging mechanism (should not be configured by anyone, in my opinion). Is that a good or a bad idea, generally?
Nope, I don't think you're improving logging with your class. Log4J is a very good solution. Your instincts are good in wanting to have one class handle logging. The bad part is that you have to embed it in each and every class that wants the service. That's why aspects are a better way to go. They're declarative and cross-cutting.
duffymo
A: 

a. I am currently using my mainLogger to log events from all classes of my app. Is that a good practice? Or should i use X logger instances for X classes?

It is generally a better idea to create and use multiple Logger. This gives you (or someone coming later) finer control over logging levels, etc via the logging configuration. But I wouldn't necessarily recommend creating a (static) Logger instance in every class.

b. What can I do to be able to log errors already caught in my imports?

There is very little that you can do about this ... except overhaul the imported codebase to improve its internal error reporting and logging.

The flip side is that if you are creating library code that might be reused elsewhere you should pay attention to getting the error reporting and logging right. In particular, clean out inappropriate use System.err, printStackTrace(), etcetera. It is also a good idea to use a logging abstraction layer so that you don't cause problems for people integrating your library.

Stephen C
+1  A: 

You can, in your main class, change System.out and System.err to print streams which take what is printed and pass them on to the logger. It is a little more complicated than it sounds, but it has been done in open source projects (JBoss has an implementation, for example) so you can get how to do it from there.

Yishai
A: 

OK, after more documentation, i came across this solution, to log everything, regardless if the source is in your code of elsewhere :

Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
 @Override
 public void uncaughtException(Thread t, Throwable exc) {
              //do something about it
              }
        });

That way, i was able to catch and report an abnormal state that occurs in an imported library, but was not handled or re-trowed. That is the case of something stupid like

   public double computeValue(int val){
      double a = 15/val;
     return a;
   }

and val was 0, generating an ArithmeticException, not throwed and not catched.

Hypercube