Videos
java.util.logging keeps you from having to tote one more jar file around with your application, and it works well with a good Formatter.
In general, at the top of every class, you should have:
Copyprivate static final Logger LOGGER = Logger.getLogger( ClassName.class.getName() );
Then, you can just use various facilities of the Logger class.
Use Level.FINE for anything that is debugging at the top level of execution flow:
CopyLOGGER.log( Level.FINE, "processing {0} entries in loop", list.size() );
Use Level.FINER / Level.FINEST inside of loops and in places where you may not always need to see that much detail when debugging basic flow issues:
CopyLOGGER.log( Level.FINER, "processing[{0}]: {1}", new Object[]{ i, list.get(i) } );
Use the parameterized versions of the logging facilities to keep from generating tons of String concatenation garbage that GC will have to keep up with. Object[] as above is cheap, on the stack allocation usually.
With exception handling, always log the complete exception details:
Copytry {
...something that can throw an ignorable exception
} catch( Exception ex ) {
LOGGER.log( Level.SEVERE, ex.toString(), ex );
}
I always pass ex.toString() as the message here, because then when I "grep -n" for "Exception" in log files, I can see the message too. Otherwise, it is going to be on the next line of output generated by the stack dump, and you have to have a more advanced RegEx to match that line too, which often gets you more output than you need to look through.
Should declare logger like this:
Copyprivate final static Logger LOGGER = Logger.getLogger(MyClass.class.getName());
so if you refactor your class name it follows.
I wrote an article about java logger with examples here.
See this short introduction to log4j.
The issue is in using System.out to print debugging or diagnostic information. It is a bad practice because you cannot easily change log levels, turn it off, customize it, etc.
However if you are legitimately using System.out to print information to the user, then you can ignore this warning.
If you are using System.out|err.println(..) to print out user-information on console in your application's main()-method, you do nothing wrong. You can get rid of the message via inserting a comment "//NOPMD".
System.out.println("Fair use of System.out.println(..).");// NOPMD
There is a "Mark as reviewed"-Option in the PMD-Violations Outline for this purpose.
Of course you can trick PMD with following code snippet:
PrintStream out=System.out;
out.println("I am fooling PMD.");
Outside of your main()-Method use a Log-System like eg Log4j.
UPDATE:
You can also modify the PMD-Rule "SystemPrintln" to use the following XPath:
//MethodDeclaration[@MethodName!="main"]//Name[
starts-with(@Image, 'System.out.print')
or
starts-with(@Image, 'System.err.print')
] | //Initializer//Name[
starts-with(@Image, 'System.out.print')
or
starts-with(@Image, 'System.err.print')
]
This will ignore System.out.println etc in any method named 'main' in your code, but check for System.out.println in initializer code. I like this, because from my point of view, System.out.println is safe in method 'main(String args[])'. But use with caution, I have to check, where in the AST a System.out.println can occur also and have to adapt the XPath.
There are so many logging frameworks for Java. Pulling in deps (FOSS or otherwise) can mean you have that team's logger choice to deal with too :(
| Logging Framework | Year of Creation | Pros | Cons |
|---|---|---|---|
| java.util.logging (JUL) | 2002 | - Built-in (no external dependencies) - Simple to use | - Limited features - Complex configuration |
| Apache Log4j 2 | 2014 | - High performance - Asynchronous logging | - Larger footprint - Configuration complexity |
| Logback | 2006 | - Modern design - Native SLF4J integration | - Documentation gaps - Complex configuration |
| SLF4J | 2005 | - Abstraction layer - Flexibility | - Not a logger itself - Binding confusion |
| Jakarta Commons Logging (JCL) | 2002 | - Abstraction layer - Automatic discovery | - ClassLoader issues - Unpredictable behavior? |
| TinyLog | 2012 | - Lightweight - Easy to use | - Less established - Limited ecosystem |
| java.lang.System.Logger | 2017 | - Built-in (no external dependencies) - Simple API | - Limited features - Less flexible configuration |
| Google Flogger | 2014 | - High performance - Fluent API - Contextual logging | - Requires Java 8+ - Less widespread adoption |
| bunyan-java-v2 | 2018 | - Structured JSON logging - Compatible with Bunyan format | - Less mature - Smaller community |
| Log4j 2 with SLF4J | - Combines simplicity and advanced features - Flexibility | - Configuration complexity - Increased dependencies | |
| Logback with SLF4J | - High performance - Advanced configuration | - Complex setup - Learning curve | |
| java.util.logging to SLF4J Bridge | - Unified logging - Flexibility | - Additional layer - Performance overhead |
I personally wish for something that can participate in a unit testing agenda. That would be to capture output to strings for assertContains(..). It would also be a setup/configuration that was 100% Java (no config files), that supported a fresh setup/config per test method.
| Logging Framework | Configurations Supported | Can Be Unit Tested (Reset Between Tests) |
|---|---|---|
| java.util.logging (JUL) | Properties file (logging.properties), programmatic configuration | Yes, but resetting is challenging |
| Apache Log4j 2 | XML, JSON, YAML, properties files, programmatic configuration | Yes, supports resetting between tests |
| Logback | XML, Groovy scripts, programmatic configuration | Yes, supports resetting between tests |
| SLF4J | N/A (depends on underlying framework) | Depends on underlying framework |
| Jakarta Commons Logging (JCL) | N/A (depends on underlying framework) | Depends on underlying framework |
| TinyLog | Properties file (tinylog.properties), programmatic configuration | Yes, supports resetting between tests |
| java.lang.System.Logger |
Programmatic configuration, depends on System.LoggerFinder SPI
| Yes, but resetting is challenging |
| Google Flogger | Programmatic configuration, properties files | Yes, supports resetting between tests |
| bunyan-java-v2 | JSON configuration, programmatic configuration | Yes, supports resetting between tests |
| Log4j 2 with SLF4J | XML, JSON, YAML, properties files, programmatic configuration (via Log4j 2) | Yes, supports resetting between tests |
| Logback with SLF4J | XML, Groovy scripts, programmatic configuration (via Logback) | Yes, supports resetting between tests |
| java.util.logging to SLF4J Bridge | N/A (depends on underlying SLF4J implementation) | Depends on underlying framework |
-
SLF4J and Jakarta Commons Logging (JCL) are abstraction layers. Their configuration methods and unit testing capabilities depend on the underlying logging framework they are paired with.
-
java.util.logging (JUL) can be unit tested, but resetting its configuration between tests can be challenging because it's globally configured within the JVM. Workarounds involve programmatic resets or using custom class loaders.
-
Apache Log4j 2, Logback, and TinyLog provide robust programmatic configuration options, facilitating resetting configurations between unit tests.
-
When using bridges like java.util.logging to SLF4J Bridge, unit testing capabilities depend on the SLF4J binding (e.g., Logback, Log4j 2) you choose.
What would you wish for?