What is the correct way to handle debug output in Java?

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):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public MyClass {
private static final boolean DEBUG = false;
... some code ...
public void myMethod(String s) {
if (DEBUG) {
System.out.println(s);
}
}
}
</code>
<code>public MyClass { private static final boolean DEBUG = false; ... some code ... public void myMethod(String s) { if (DEBUG) { System.out.println(s); } } } </code>
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?

4

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

  • Java Logging Framework (part of the JDK),
  • Apache Log4J (bit old, but still going strong and actively maintained),
  • LogBack (created to provide a more modern approach than Log4J, by one of the creators of Log4J).

Some Logging Facades

  • SLF4J (by the creator of LogBack, but has adapters for other frameworks),
  • Apache Commons Logging (but a bit dated now).

Usage

Basic Example

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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");
}
}
</code>
<code>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"); } } </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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!");
}
}
}
</code>
<code>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!"); } } } </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
</code>
<code>private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class); </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Logger logger = LoggerFactory.getLogger("MyModuleName");
</code>
<code>Logger logger = LoggerFactory.getLogger("MyModuleName"); </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>if (LOGGER.isDebugEnabled()) {
// lots of debug logging here, or even code that
// is only used in a debugging context.
LOGGER.debug(" result: " + heavyComputation());
}
</code>
<code>if (LOGGER.isDebugEnabled()) { // lots of debug logging here, or even code that // is only used in a debugging context. LOGGER.debug(" result: " + heavyComputation()); } </code>
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><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>
</code>
<code><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> </code>
<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:

  • the LogBack manual on configuration,
  • the Log4J 2 manual on configuration.

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.

9

use a logging framework

most of the time there’s a static factory method

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>private static final Logger logger = Logger.create("classname");
</code>
<code>private static final Logger logger = Logger.create("classname"); </code>
private static final Logger logger = Logger.create("classname");

then you can output your logging code with different levels:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");
</code>
<code>logger.warning("error message"); logger.info("informational message"); logger.trace("detailed message"); </code>
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)

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.

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.

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật