Sonarcloud alerts "not enough arguments" when logging exceptions using SLF4J - java

I manage an open source project in Java and have about 20 places in my code where I log exceptions using the following pattern (slf4j version 1.7.30)
private static final Logger logger = LoggerFactory.getLogger();
...
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException ex) {
logger.error("Socket exception when retrieving interfaces: {}", ex);
}
or similarly
try {
// stuff
} catch (IOException ioe) {
logger.error("Server error: {}", ioe);
}
Starting today, the SonarCloud automated code quality review has begun flagging these with rule java:S2275 (Printf-style format strings should not lead to unexpected behavior at runtime) with the specific message "Not enough arguments."
EDIT: Of note, this appears to consistently happen when an Exception is the final argument. The following pattern does not flag:
try {
// Server connection code
} catch (IOException e) {
logger.error("Server Connection error: {}", e.getMessage());
}
A review of this other StackOverflow question indicates that perhaps an extra argument for the exception is optional and would result in different behavior, so I'm not clear how that would apply here and why it would suddenly change.
Is there something I can/should do to better translate these exceptions to log messages (e.g., use getMessage() on all of them instead of relying on the automated toString() parsing), or is this a false positive?
(Sonar's list of my 20 issues linked here.)

This is pure conjecture, but each of the issues points to a log line that can be generalized as:
LOG.something(format, custom_arguments, exception)
where format has {} appearing count(custom_arguments) + 1 (the 1 reserved for exception).
As you've seen the linked answer, exceptions get treated specially by slf4j, so it's possible that due to some reason SonarCloud is doing the same thing. Unfortunately there's no documentation.
The "fix" would be to remove the final {} intended for the exception, so e.g.
LOG.error("boom: {}", e);
LOG.error("boom2 {}: {}", something, e);
becomes
// exceptions handled in a special way
LOG.error("boom", e);
LOG.error("boom2 {}", something, e);

Related

Logger prints only the log message and not the exception

try {
// some code
}
catch (Exception e) {
Logger.log(Level.WARN, "Unable to complete the job. ID: " + id, e);
}
So, obviously a developer expects if something goes wrong, it would log the exception (exception type & stacktrace)
Here is the log print which I got
[27 May 2019 13:30:07][http-nio-8080-exec-13][WARN]: Unable to complete the job. ID: 123457890
Here is the Log4j config
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=debug.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%d{dd MMM yyyy HH:mm:ss}][%t][%p]: %m%n
I know ways to get the exception details, I want to understand this behavior. How does this happen? Does the logger ignore the passed on parameters?
ERROR level is used to log the stacktrace of an error in log file.
Try using.
try {
// some code
} catch (Exception e) {
Logger.log(Level.ERROR, "Unable to complete the job. ID: " + id, e);
}
if it still don't work, please share your logging library. Will check with that.
My only guess would be that this happens due to hotspot optimization:
The compiler in the server VM now provides correct stack backtraces for all "cold" built-in exceptions. For performance purposes, when such an exception is thrown a few times, the method may be recompiled. After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace. To disable completely the use of preallocated exceptions, use this new flag: -XX:-OmitStackTraceInFastThrow.
This was borrowed from this answer to a related question:
log4j not printing the stacktrace for exceptions

Am I abusing the idea of log levels here?

I have a case where I want to introduce some error logging. But at this point I'm not sure how often exceptions will occur here (experimental feature, errors dependent on user input) and I'm a bit worried about flooding the logs with stacktraces.
So I came up with this solution:
catch (Exception ex) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("Exception during save()", ex); // log with stacktrace
} else {
LOGGER.error("Exception during save(): {}", ex.toString());
}
}
But I'm not really satisfied with this because I feel like abusing the debug level for logging on error level. I use org.slf4j.Logger.
If you're only concerned about not spamming the logs with irrelevant stacktraces, you can still kind of not abuse the idea of log levels but embrace it instead :
catch (Exception ex) {
LOGGER.error("Exception during save(): {}", ex.toString());
LOGGER.debug("Detailed exception output:", ex);
}
Performance-wise your proposed solution using isDebugEnabled scores a little bit better, as the String object (first argument) won't get created for the second call unless you really need it. And you also spare a single level on the call stack.

is it possible to catch a fatal xml exception in java?

When i do DOM XML Schema validation i get this error when there is a syntax error in my deliberately-incorrect test file.
[Fatal Error] :10:3: The element type "gizmos" must be terminated by the matching end-tag "</gizmos>".
However, I want to catch this so that it wouldn't give the red fatal error warning, instead a message that says something like "your xml is not valid." Is it possible?
Thanks,
I believe you can catch Throwable to handle your scenario
try{
....
....
....
}catch (Exception e) {
e.printStackTrace();
}catch (Throwable t) {
t.printStackTrace();
}
Errors can not be catched, only Exceptions can be catched.
According to Java Docs :
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it.
A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur. That is, Error and its subclasses are regarded as unchecked exceptions for the purposes of compile-time checking of exceptions.
But still, you can catch Throwable to catch any error. That is not recommended though.
catch (Throwable e) {
//e.printStackTrace();
}
You can catch a Throwable (Error extends Throwable):
try {
//...
} catch(Throwable e) {
//...
}
Please note that it's often recommended not to do it. You can read more about it here: When to catch java.lang.Error?.
If you are using the JAXP validation API then you can nominate an ErrorHandler to receive notification of errors. With this you can change what messages are displayed and where they are displayed.

What to include in the catch clause of Exception

I have a code that throws a bunch of Exceptions but each of them only contains a printStackTrace() method as shown below
} catch (SecurityException e) {
// TODO Auto-generated catch block
System.err.println(e);
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Is this sufficient or do I need to include additional statements like System.err.println(e)? Usually, if an exception occurs I am able to trace the source with the above alone.
If there is something you can do to solve the problem, do it in the catch, if there is nothing you can do, then it is better to use a logging framework to register the exception than to use e.printStackTrace(); or System.err.println(e);
I personally recommend: http://www.slf4j.org/, but if you have masochistic tendencies you can try the very bad (but official) Java Logging API: http://download.oracle.com/javase/1.4.2/docs/guide/util/logging/ .
One extra advantage of SLF4J is that it can redirect its logging to the awful Java Logging API (that way you can use an elegantly designed API and still comform to the awfully designed (de jure not de facto) "standard"
SLF4J is easy to use, to log an exception all you have to do is write logger.error("some accompanying message", exception);, another of its advantages is that you can, for example, configure it to send you an email each time your application crashes (by using logback as the underlying logging engine)
It depends on the exceptions. Obviously, with printStackTrace() the exception will be printed for you to debug (or users to report to you). However there is no additional error handling.
Example:
If an IOException is thrown, you might want to show the user a error message specifying the exact error cause, or you might want to do another attempt, transparent for the user. Or you might want to abort the whole program if the operation is critical for the success of the whole task... etc.
If you want to trace the source e.printStackTrace() is enough.
Usually I put e.printStackTrace(); at DEBUG level. Also I add meaningful error message at ERROR level for the users.
I think you might be missing a bit about the basics of exceptions and exception handling.
The golden rule of exceptions is that they should be exceptional.
This is why you might have seen or read that you should never catch the base Exception - there is simply no way that your code can handle every time of exception.
So as a general rule you should only catch exceptions if you can handle them in a specific way. For example, if you're reading a user's details from a file and that fails you might choose to return a new user. What you don't want to do is simply catch the exception and log it. This leads to an application that is robust but simply swallows errors which leads to an extremely bad user experience.
If your method can't handle an exception it should simply not catch it and defer the exception handling to a higher level. This usually means an error message will be displayed to the user (at the top level).
If you can afford to use a logging framework like log4j, you'll be able to call
}catch(Exception e){ log.error("Exception occurred:",e}
making the log framework to log your custom message "Exception occurred" followed by the stack trace in your errorlog file

Is it a bad idea to use printStackTrace() for caugt Exceptions?

Is it a bad idea to use printStackTrace() in Android Exceptions like this?
} catch (Exception e) {
e.printStackTrace();
}
I believe this is what you need:
catch (Exception e) {
Log.e(TAG,Log.getStackTraceString(e));
}
Yes, it is a bad idea. You should instead use Android's built-in log class specifically designed for these purposes: http://developer.android.com/reference/android/util/Log.html
It gives you options to log debug messages, warnings, errors etc.
Logging errors with:
Log.e(TAG, "message", e) where the message can be an explanation of what was being attempted when the exception was thrown
or simply Log.e(TAG, e) if you do not wish to provide any message for context
You can then click on the log console at the bottom while running your code and easily search it using the TAG or log message type as a filter
Yes. printStackTrace() is convenient but discouraged, especially on Android where it is visible through logcat but gets logged at an unspecified level and without a proper message. Instead, the proper way to log an exception is...
Log.e(TAG, "Explanation of what was being attempted", e);
Note that the exception is used as a third parameter, not appended to the message parameter. Log handles the details for you – printing your message (which gives the context of what you were trying to do in your code) and the Exception's message, as well as its stack trace.
The question is: is useful at all print to the stack trace in an Andriod application context?
Will the standard output be visible at runtime? Will somebody care about it?
My point is that, if nobody is going to check the standard output and care to debug the error, the call to this method is dead code, and composing the stacktrace message is a worthless expense. If you need it only for debugging at development, you could set an accesible global constant, and check it at runtime:
} catch (Exception e) {
if(com.foo.MyEnvironmentConstants.isDebugging()) {
e.printStackTrace();
} //else do noting
}
I would avoid using printStackTrace(), use a logging system and its support of exceptions.
log.log(Level.SEVERE, "Uncaught exception", e);
So if you want to change how logging is handled it's much easier.

Categories

Resources