Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

As my current Java projects grow bigger and bigger, I feel a likewise growing need to insert debug output in several points of my code.

To enable or disable this feature appropriately, depending on the opening or closure of the test sessions, I usually put a private static final boolean DEBUG = false at the beginning of the classes my tests are inspecting, and trivially use it this way (for example):

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

  public void myMethod(String s) {
    if (DEBUG) {
      System.out.println(s);
    }
  }
}

and the like.

But that doesn't bliss me out, because of course it works but there could be too many classes in which to set DEBUG to true, if you are not staring at just a couple of them.

Conversely, I (like - I think - many others) wouldn't love to put the whole application in debug mode, as the amount of text being output could be overwhelming.

So, is there a correct way to architecturally handle such situation or the most correct way is to use the DEBUG class member?

share|improve this question
10  
in Java, the correct way is NOT to use homebrew code for logging. Pick an established framework, don't reinvent the wheel –  gnat Nov 16 '12 at 13:25
    
I use a boolean DEBUG in some of my more complicated classes, for the same reason as you stated. I don't usually want to debug the whole application, just the class giving me problems. The habit came from my COBOL days, where DISPLAY statements were the only form of debugging available. –  Gilbert Le Blanc Nov 16 '12 at 19:24
1  
I would also recommend relying more on a debugger when possible and not littering your code with debug statements. –  Andrew Finnell Nov 17 '12 at 12:04
1  
Do you practice Test Driven Development (TDD) with unit tests? Once I started doing that, I noticed a massive reduction in 'debug code'. –  JW01 Nov 21 '12 at 9:07
add comment

4 Answers

up vote 38 down vote accepted

You want to look at a logging framework, and maybe at a logging facade framework.

There are multiple logging frameworks out there, often with overlapping functionalities, so much so that over time many evolved to rely on a common API, or have come to be used through a facade framework to abstract their use and allow them to be swapped in place if needed.

Frameworks

Some Logging Frameworks

Some Logging Facades

Usage

Basic Example

Most of these frameworks would allow you to write something of the form (here using slf4j-api and logback-core):

package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// copied from: http://www.slf4j.org/manual.html
public class HelloWorld {

  public static void main(String[] args) {
    final Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.debug("Hello world, I'm a DEBUG level message");
    logger.info("Hello world, I'm an INFO level message");
    logger.warn("Hello world, I'm a WARNING level message");
    logger.error("Hello world, I'm an ERROR level message");
  }
}

Note the use of a the current class to create a dedicated logger, which would allow SLF4J/LogBack to format the output and indicate where the logging message came from.

As noted in the SLF4J manual, a typical usage pattern in a class is usually:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class MyClass {

    final Logger logger = LoggerFactory.getLogger(MyCLASS.class);

    public void doSomething() {
        // some code here
        logger.debug("this is useful");

        if (isSomeConditionTrue()) {
            logger.info("I entered by conditional block!");
        }
    }
}

But in fact, it's even more common to declare the logger with the form:

private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);

This allows the logger to be used from within static methods as well, and it is shared between all instances of the class. This is quite likely to be your preferred form. However, as noted by Brendan Long in comments, you want to be sure to understand the implications and decide accordingly (this applies to all logging frameworks following these idioms).

There are other ways of instantiating loggers, for instance by using a string parameter to create a named logger:

Logger logger = LoggerFactory.getLogger("MyModuleName");

Debug Levels

Debug levels vary from one framework to another, but the common ones are (in order of criticality, from benign to bat-shit bad, and from probably very common to hopefully very rare):

  • TRACE Very detailed information. Should be written to logs only. Used only to track the program's flow at checkpoints.

  • DEBUG Detailed information. Should be written to logs only.

  • INFO Notable runtime events. Should be immediately visible on a console, so use sparingly.

  • WARNING Runtime oddities and recoverable errors.

  • ERROR Other runtime errors or unexpected conditions.

  • FATAL Severe errors causing premature termination.

Blocks and Guards

Now, say you have a code section where you are about to write a number of debug statements. This could quickly impact your performance, both because of the impact of the logging itself and of the generation of any parameters you might be passing to the logging method.

To avoid this sort of issue, your often want to write something of the form:

if (LOGGER.isDebugEnabled()) {
   // lots of debug logging here, or even code that
   // is only used in a debugging context.
   LOGGER.debug(" result: " + heavyComputation());
}

If you hadn't used this guard before your block of debug statements, even though the messages may not be output (if, for instance, your logger is currently configured to print only things above the INFO level), the heavyComputation() method would still have been executed.

Configuration

Configuration is quite dependent on your logging framework, but they offer mostly the same techniques for this:

  • programmatic configuration (at runtime, via an API - allows for runtime changes),
  • static declarative configuration (at start-time, usually via an XML or properties file - likely to be what you need at first).

They also offer mostly the same capabilities:

  • configuration of the output message's format (timestamps, markers, etc...),
  • configuration of the output levels,
  • configuration of fine-grained filters (for instance to include/exclude packages or classes),
  • configuration of appenders to determine where to log (to console, to file, to a web-service...) and possibly what to do with older logs (for instance, with auto rolling files).

Here's a common example of a declarative configuration, using a logback.xml file.

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

As mentioned, this depends on your framework and there may be other alternatives (for instance, LogBack also allows for a Groovy script to be used). The XML configuration format may also vary from one implementation to another.

For more configuration examples, please refer (amongst others) to:

Some Historical Fun

Please note that Log4J is seeing a major update at the moment, transitioning from version 1.x to 2.x. You may want to have a look at both for more historical fun or confusion, and if you pick Log4J probably prefer to go with the 2.x version.

It's worth noting, as Mike Partridge mentioned in comments, that LogBack was created by a former Log4J team member. Which was created to address shortcomings of the Java Logging framework. And that the upcoming major Log4J 2.x version is itself now integrating a few features taken from LogBack.

Recommendation

Bottom line, stay decoupled as much as you can, play around with a few, and see what works best for you. In the end it's just a logging framework. Except if you have a very specific reason, apart from ease of use and personal preference, any of these would do rather OK so there's not point being to hung over it. Most of them can also be extended to your needs.

Still, if I had to pick a combination today, I'd go with LogBack + SLF4J. But if you had asked me a few years later I'd have recommended Log4J with Apache Commons Logging, so keep an eye over your dependencies and evolve with them.

share|improve this answer
1  
SLF4J and LogBack were written by the guy who originally wrote Log4J. –  Mike Partridge Nov 16 '12 at 13:18
    
@MikePartridge: indeed, but I wasn't sure from memory, so I didn't want to go on a limb before checking that. –  haylem Nov 16 '12 at 13:23
4  
For those who might be worried about performance impacts of logging: slf4j.org/faq.html#logging_performance –  Mike Partridge Nov 16 '12 at 13:30
2  
It's worth mentioning that it's not so clear-cut whether you should make your loggers static, since it saves a small amount of memory, but causes problems in certain instances: slf4j.org/faq.html#declared_static –  Brendan Long Nov 16 '12 at 15:45
1  
@MikePartridge: I am aware of the content of the link, but it still won't prevent parameter evaluation for instance. the reason byt parameterized logging is more performant is because the processing of the log message won't occur (string notably concatenation). However, any method call will get executed if it's passed as a parameter. So, depending on your use case, blocks can be useful. And as mentioned in the post, they can also be useful for you just to group other debug related activities (not just logging) to occur when the DEBUG level is enabled. –  haylem Nov 16 '12 at 23:38
show 6 more comments

This is exactly what logging frameworks like log4j or the newer slf4j are meant for. They allow you to control logging in great detail, and configure it even while the application is running.

share|improve this answer
add comment

use a logging framework

most of the time there's a static factory method

private static final Logger logger = Logger.create("classname");

then you can output your logging code with different levels:

logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");

then there will be a single file where you can define which messages for each class should be written to the log output (file or stderr)

share|improve this answer
add comment

A logging framework is definitely the way to go. However, you also must have a good test suite. Good test coverage may often eliminate the need for debug output all together.

share|improve this answer
    
If you use a logging framework and have debug logging available - there will come a time when this will save you from having a really bad day. –  Fortyrunner Nov 17 '12 at 15:59
1  
I did not say you shouldn't have logging. I said you need to have tests first. –  Dima Nov 17 '12 at 18:06
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.