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.