17

This Mkyong tutorial suggests intialising loggers this way:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

Now presumably every other class you use, that has a logger will initialise their loggers the same way.

My question is - is this the best way to do it? It seems... repetitive.

Martin Schröder
  • 354
  • 1
  • 4
  • 19
dwjohnston
  • 2,543
  • 6
  • 29
  • 49
  • 2
    What do you find verbose about this (setting aside Java's syntax)?. You have to create a variable to hold the logger, and you have to tell `getLogger()` the name of the logger to get. – kdgregory Jun 26 '15 at 11:08
  • That said, I prefer loggers as instance variables rather than class variables, so eliminate the `static` keyword and replace `Foo.class` with `getClass()`. – kdgregory Jun 26 '15 at 11:09
  • 2
    @kdgregory the fact that i'm doing the same thing in every class. – dwjohnston Jun 26 '15 at 12:38
  • 1
    Take a look at this [question on Stack Overflow](http://stackoverflow.com/q/6351082/1407656) – toniedzwiedz Jun 27 '15 at 14:39
  • 3
    Too old to migrate, but this question is off-topic here and more suitable for StackOverflow. – Andres F. Feb 26 '16 at 03:57
  • 1
    @AndresF. I think i put it here because it's more of a question about code style/design patterns, than a technical issue. – dwjohnston Feb 26 '16 at 04:26
  • @dwjohnston you can use a single logger in your whole application and just use it. Convention has it to have a logger pr class because the defacto configuration unit is the class name. Then you need such a line. In these days an annotation could most likely do it. – Thorbjørn Ravn Andersen Sep 02 '16 at 08:35
  • He doesn't say it's verbose, he said it's repetitive. It is repetitive, it's unnecessary, and as Java devs we should be looking for better alternatives. It's funny how many on the Exchange sites are so perfectionist about certain things, but then arbitrarily turn a blind eye to certain obviously improvable situations like this one. Very closed minded. – Manius Apr 28 '19 at 13:38
  • As for putting this on Stack Overflow, I'm sure they'll close it because it's too "subjective". It's sad what these sites have become - more about rules, organization, and cleanup than having useful conversations and helping users. – Manius Apr 28 '19 at 13:39

2 Answers2

18

Your comment says that "verbose" refers to the need to repeat this line of code in every class. My first response is that, in the big picture, adding two lines of code (variable definition plus import statement) to every class isn't that big a deal. Especially since you only need to add them to the classes that have behavior and therefore need to do logging. That said, the specific line of code that you're using is prone to copy-paste errors (more on that later).

But, since you want alternatives, here are a few, with reasons that you might or might not want to use them.

Use a single logger for the entire app

If you don't care about what class is reporting, or are willing to put all necessary context into the message, than a simple singleton logger will do the job:

LoggerSingleton.getInstance().debug("MyController is running")

In my opinion, one of the big benefits of a logging framework is having the context provided by separate logger instances -- if only to direct the log messages to different destinations. I wouldn't give that up just to save one line of code (you still need the import).

Plus, this increases verbosity at the point of use, which will end up in far more keystrokes.

Create your loggers at the point of use

I'm throwing this one out just because it eliminates the variable. I don't think I need to comment on it. Although it does show my preferred technique for getting a logger instance.

Logger.getLogger(getClass()).debug("blah blah blah");

Use a bean post-processor to inject the logger

Your example uses Spring, and Spring lets you hook into the bean initialization code. You could create a post-processor that inspects the bean for a logger member variable, and creates a Logger instance when it finds one.

While such a post-processor is only a few dozen lines of code, it's another moving part in your application, and therefore another potential source of bugs. I prefer to have as few of those as possible.

Use a mixin

Scala and Groovy provide traits, which let you encapsulate behavior. A typical Scala pattern is to create a Logging trait, then add it to the class that needs logging:

class MyController with Logging

Unfortunately, this means that you have to switch languages. Unless you're using Java 8, in which case you can create a Logging interface with a "default method":

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

Now, within your class code, you can simply use

getLogger().debug("blah blah blah");

While easy, this has a couple of disadvantages. For one thing, it pollutes the interface of every class that uses it, because all interface methods are public. Perhaps not that bad if you use it only for classes that are instantiated and injected by Spring, especially if you follow interface/implementation separation.

The bigger problem is that it has to look up the actual logger instance on every call. Which is fast, but unnecessary.

And you still need an import statement.

Move logger to a superclass

I'll repeat: I don't find repeated logger definitions verbose, but if you do I think this the best approach to eliminating them.

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

Now your controller classes inherit from AbstractController, and they have access to the logger variable. Remember that you have to put the @Controller annotation on the concrete class.

Some people will find this a perversion of inheritance. I have tried to mollify them by naming the class AbstractController rather than AbstractProjectClass. You can decide for yourself whether or not there's an is-a relationship.

Other people will object to the use of an instance variable rather than a static variable. IMO static loggers are prone to copy-paste errors, because you have to explicitly reference the classname; getClass() ensures that your logger is always correct.

kdgregory
  • 5,220
  • 23
  • 27
  • I think you should be careful about getClass() in inheritance class, MethodHandles.lookup().lookupClass() is better after jdk 7 – yuxh Jul 15 '19 at 03:16
  • @luxh - do you have an explanation for that? – kdgregory Jul 16 '19 at 10:36
  • check here: https://stackoverflow.com/a/6653577/4652536 – yuxh Aug 01 '19 at 01:44
  • @yuxh - the only mention of MethodHandles in that question (which wasn't in the answer you linked), is a similar unsupported assertion with a comment asking for clarification. Do you have _authoritative_ support for the assertion that `MethodHandles.lookup().lookupClass()` is better than `Object.getClass()`? – kdgregory Aug 01 '19 at 10:38
  • `MethodHandles.lookup().lookupClass()` can be used for static variables, is not prone to copy-paste errors and is fast: https://stackoverflow.com/a/47112323/898747 It does mean an extra inport, but i quite like my loggers bein static so it is at least worth a mention :) – FableBlaze Feb 19 '20 at 11:49
1

To extend the answer provided by @kdgregory, Groovy provides @Slf4j(groovy.util.logging.Slf4j) out of the box as an annotation that performs an AST transformation on the class to tack it with a logger which assumes the variable name log by default if left unspecified.