SonarQube: Invoke method(s) only conditionally - java

The following part of code raises a major bug at SonarQube :
"Invoke method(s) only conditionally."
How am I supposed to fix this?
if(us != null){
logger.info("Log this: {}", us.toString());
}

The call to us.toString() is redundant, toString() method will be called regardless the configured log level. You should pass only us as an argument to info without an if statement.
logger.info("Log this: {}", us);

As stated at the comments of the question, another working answer is:
if(logger.isInfoEnabled() && us != null){
logger.info("Log this: {}", us.toString());
}

You can just ignore this but it might be good to handle this scenario if possible, It would help us to handle and cutoff unnecessary computations.
One thing what it suggests here is to check if the Log Level that you are going to use is enabled or not.
if(logger.isInfoEnabled() && us != null){
// this inner code will only get executed if the above is true
logger.info("Log this: {}", us.toString());
}
Imagine having a complex task running inside, it would be a waste of time to do that if you are not going to log it anyways, if the log level is disabled. Logger will internally check that for you but doing it now before invoking the .info() will save you some cycles.

Passing message arguments that require further evaluation into a Guava com.google.common.base.Preconditions check can result in a performance penalty. That's because whether or not they're needed, each argument must be resolved before the method is actually called.
Similarly, passing concatenated strings into a logging method can also incur a needless performance hit because the concatenation will be performed every time the method is called, whether or not the log level is low enough to show the message.
Instead, you should structure your code to pass static or pre-computed values into Preconditions conditions check and logging calls.
Specifically, the built-in string formatting should be used instead of a string concatenation, and if the message is the result of a method call, then Preconditions should be skipped altogether, and the relevant exception should be conditionally thrown instead.
Noncompliant Code Example
logger.log(Level.DEBUG, "Something went wrong: " + message);
// Noncompliant; string concatenation performed even when log level too high to show DEBUG messages
logger.fine("An exception occurred with message: " + message);
// Noncompliant
LOG.error("Unable to open file " + csvPath, e); // Noncompliant
Preconditions.checkState(a > 0, "Arg must be positive, but got " + a);
// Noncompliant. String concatenation performed even when a > 0
Preconditions.checkState(condition, formatMessage()); // Noncompliant. formatMessage() invoked regardless of condition
Preconditions.checkState(condition, "message: %s", formatMessage());
// Noncompliant
Compliant Solution
logger.log(Level.SEVERE, "Something went wrong: {0} ", message);
// String formatting only applied if needed
logger.fine("An exception occurred with message: {}", message);
// SLF4J, Log4j
logger.log(Level.SEVERE, () -> "Something went wrong: " + message);
// since Java 8, we can use Supplier , which will be evaluated lazily
LOG.error("Unable to open file {0}", csvPath, e);
if (LOG.isDebugEnabled() {
LOG.debug("Unable to open file " + csvPath, e);
// this is compliant, because it will not evaluate if log level is above debug.
}
Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a); // String formatting only applied if needed
if (!condition) {
throw new IllegalStateException(formatMessage()); /
/ formatMessage() only invoked conditionally
}
if (!condition) {
throw new IllegalStateException("message: " + formatMessage());
}
Exceptions
catch blocks are ignored because the performance penalty is unimportant on exceptional paths (catch block should not be a part of standard program flow). Getters are ignored as well as methods called on annotations which can be considered as getters. This rule accounts for explicit test-level testing with SLF4J methods isXXXEnabled and ignores the bodies of such if statements.

It's related to performance issues. They recomend just putting a pure String or a final variable defined previously.
final var message = "Log this: " + us.toString();
logger.info(message);
https://sonarcloud.io/organizations/default/rules?languages=java&open=java%3AS2629&q=S2629

Short easy answer: just delete the .toString from your code since the formatter will take care of changing it to String for you.

Related

Java - emoji4j static method call ends/vanishes/dies without error

I am writing a plugin which takes a message from discord and sends it to a minecraft server.
Minecraft clients have a hard time rendering emojis. Therefore I opted to use https://github.com/kcthota/emoji4j to convert all emojis into their shortcodes (example: 😃 -> :smile: ..or similar)
The problem:
When calling the static method shortCodify it never returns. Almost as if it kills the code where it is and never continues. No errors in console.
It almost seems as though calling the method kills it right there. Step 1 is never printed.
It is able to run through this multiple times (every time I send a discord message). It has not killed the process completely.
I have tried:
Adding the debug prints all over the place to try to track down the issue.
PS: don't hate me for mixing logger.info and system println, I am removing all of this later xD
Console output
13:35:48 [INFO] [Core] Emoji manager exists.
13:35:48 [INFO] [Core] Attempting shortcodify (contains 1738 emojis)
13:35:48 [INFO] DEBUG: EventChat.java step 0
Yes.... it stops there!
Code snippets:
My code / EventChat.java
Note: msg is a String
The if statement (of which you see the else) just checks that the emoji data was loaded, because I ran the config loading in a separate thread. Knowing it is able to get to here and prints that the data exists, this is not the problem.
...
} else {
logger.info("Emoji manager exists.");
try {
logger.info("Attempting shortcodify (contains " + EmojiManager.data().size() + " emojis)");
System.out.println("DEBUG: EventChat.java step 0");
msg = EmojiUtils.shortCodify(msg);
logger.info("new message: " + msg);
} catch (Exception e) {
logger.info("Catching exception");
e.printStackTrace();
}
}
logger.info("Emoji processed.");
Emoji4j / EmojiUtils.java
public static String shortCodify(String text) {
System.out.println("DEBUG: EmojiUtils.java step 1");
String emojifiedText = emojify(text);
System.out.println("DEBUG: EmojiUtils.java step 2");
for (Emoji emoji : EmojiManager.data()) {
StringBuilder shortCodeBuilder = new StringBuilder();
shortCodeBuilder.append(":").append(emoji.getAliases().get(0)).append(":");
emojifiedText = emojifiedText.replace(emoji.getEmoji(), shortCodeBuilder.toString());
System.out.println("DEBUG: EmojiUtils.java step 2.loop");
}
System.out.println("DEBUG: EmojiUtils.java step 3");
return emojifiedText;
}
I found the answer after what seems to be wayyy too long. (yes, 2 months lol)
NOTE: this only applies to anyone using JDA with emoji4j
JDA catches all Throwables by default and attempts to log it to the console but fails due to bungeecord not using the same logger (or something similar, I don't really know why).
I wasn't too stupid, as I tried catching all exceptions and logging them. BUT it was throwing a throwable instead of an exception.... for whatever reason...
So, long story short, I was catching excpetions and JDA was catching the Throwable that indicated the missing dependency and making the error vanish instead of printing to console.
Fix
try {
} catch (Throwable t) {
// error is now caught and can be logged using bungee's logger
}

How to correctly handle a spark.sql.AnalysisException

I've been using Spark Dataset API to perform operations on a JSON to extract certain fields as needed. However, when the specification that I provide to let spark know what field to extract goes wrong, spark spits out an
org.apache.spark.sql.AnalysisException
How can unchecked runtime exceptions be handled in a distributed processing scenario like this ? I understand that throwing a try-catch would get things sorted but what is the recommended way to handle such a scenario
dataset = dataset.withColumn(current, functions.explode(dataset.col(parent + Constants.PUNCTUATION_PERIOD + child.substring(0, child.length() - 2))));
In scala, you should simply wrap the call in a Try and manage Failure. Something like:
val result = Try(executeSparkCode()) match {
case s: Success(_) => s;
case Failure(error: AnalysisException) => Failure(new MyException(error));
}
Note 1: If your question implies how to manage exception in scala, there are a lot of doc and post about this subject (i.e. don't throw). For example, you can check that answer (of mine)
Note 2: I don't have a scala dev env right here, so I didn't test this code)
In java there is a tricky situation however: the compiler doesn't expect an AnalysisException which is unchecked so you cannot catch this exception specifically. Probably some scala/java misunderstanding because scala doesn't track checked exceptions. What I did was:
try{
return executeSparkCode();
} catch (Exception ex) {
if(ex instanceOf AnalysisException){
throw new MyException(ex);
} else {
throw ex; // unmanaged exceptions
}
}
Note: In my case, I also tested the content of the error message for a specific exception that I must managed (i.e "path does not exist") in which case I return an empty dataset instead of throwing another exception. I was looking for a better solution and happened to get here...

Java Exception.getMessage() returns -1

I am trying to debug a piece of production code. I did not write it, so please do not criticize it. I know it is terrible practice for multiple reasons, and I would change it if I could, but I can't.
The code looks like:
try
{
...
// Multiple lines of code that can throw exceptions
...
}
catch (Exception e)
{
System.out.println("Exception: " + e.getMessage());
}
Nowhere in those multiple lines of code is an exception thrown manually.
Despite this, the following is the entirety of what is printed in the case I'm trying to debug:
Exception: -1
The Java documentation for Throwable.getMessage() says
getMessage
public String getMessage()
Returns the detail message string of this throwable.
Returns:
the detail message string of this Throwable instance (which may be null).
All non-native library methods that can throw exceptions are correctly caught around where they are called within the larger try-catch block. So, speaking specifically in regards to Exceptions in the standard JDK, are there any possible Exceptions whose messages are simply "-1"?
The exception name is often a really important part of the exception. Sometimes it's the only thing in the exception. See javadoc of getMessage():
Returns the detail message string of this Throwable instance (which may be null).
Examples of exceptions with no message:
NullPointerException
StackOverflowError
So, println(e.getMessage()) is often meaningless, because it's nothing or entirely without context.
Examples of exceptions where message is meaningless without exception name:
-1 ArrayIndexOutOfBoundsException
foo.txt FileNotFoundException
Always include the exception name too, e.g. using toString():
System.out.println("Exception: " + e.toString());
String concatenation will automatically use toString(), so it can also be just:
System.out.println("Exception: " + e);
Most of the time, it is better to print the stacktrace, so you can see where in the code the exception occurred:
e.printStackTrace(System.out);
use
e.printStackTrace()
you will see where the exception is thrown, and why. My guess is it's going to be an array IndexOutOfBound exception

How to avoid creating unused String instances when logging in Java

I define a Util class to log using log4j, and a method is like below:
public static void info(String msg) {
if (logger.isInfoEnabled())
logger.info(msg);
}
But if the log level is set to "Error" (and thus logger.isInfoEnabled() is false), and I use the below to log, will the two String instances be created?
LogUtil.info("String instance" + stringParam);
even though these two String instances may be collected by gc, construct a instance will be still costs.
I guess if I check the log level before calling logger.info(msg) may help , but this may the code not clean, that's the reason why I define Util class.
How can I to write a clean code that if I do not set the level to info, it will neither log anything nor create the String instance?
Don't define the Util class and you can avoid some of the string creation overhead.
Quite apart from anything else you're isInfoEnabled check is completely pointless as logger itself will do the check.
Use:
logger.log(Level.INFO, "Message {0} with {1}", new Object[] { param1, param2 });
And you will avoid most of the string creation overhead as it will only toString() param1 and param2 and construct the final message if the INFO level is turned on.
There is the cost of allocating the new Object[] array but that is tiny
I'm afraid your util class is completely useless, as the logger already does the log level check internally. The idea of doing these checks manually outside is to reduce the overhead caused by unnecessary code that prepares the log message (mostly String concatenation) even if then it is not logged due to the current log level:
Suppose you want to log the user input - which is a Date object. Here, date-to-string conversion and string concatenation is done even if the log level is lower than info:
logger.info("user input: " + dateFormatter.format(userInput));
So the solution is to check the log level first:
if (logger.isInfoEnabled()) {
logger.info("user input: " + dateFormatter.format(userInput));
}
But if you now move the check into a utililty method that gets the full message as parameter, you'll end up having the same problem than above!
Others already explained in their answers how to use the extended log methods to let the logger internally do the formatting:
logger.log(Level.INFO, "Current user input: {0}", userInput);
But also here, if the parameter has to be specially formatted first, it does not solve the overhead problem. Again, you'll have to do an additional log-level check first.
Since Java 8, you could write a utility-method that can be used effeciently and smartly by using lambda expressions:
public static void info(Supplier<String> messageSupplier) {
if (logger.isInfoEnabled()) logger.info(messageSupplier.get());
}
Usage:
LoggerUtil.info(() -> "user input: " + dateFormatter.format(userInput));
Here, the lambda expression will only be executed if info logging is enabled!
You could also profit from Slf4J logger (defacto new standard in the industry), that has friendlier syntax:
logger.info("Configuration directory: {}, {}", param1, param2);
It is also possible to configure Slf4J with different back-ends including Log4J. Hope this helps
even though this String instance will be collected by gc, -
In logger.info("String instance"); , "String instance" is a String Literal and will be created on the String Pool. Unfortunately, it will persist as long as the program runs (i.e, until the JVM shuts down). Use a StringBuilder or String(char[]) to prevent adding the String to the String Pool.

Is Log4j isDebugEnabled() necessary before using logger.debug()? [duplicate]

This question already has answers here:
Is there a need to do a if(log.isDebugEnabled()) { ... } check? [duplicate]
(5 answers)
Closed 7 years ago.
When I was going through some code, I noticed the use of logger as follows,
if(logger.isDebugEnabled())
logger.debug("Something..");
But in some codes, I observed like this.
logger.debug("Something..");
When I looked at the source of log4j, in the debug() method of Logger itself if(logger.isDebugEnabled()) was checked. Then why do we need this unnecessary overhead if(logger.isDebugEnabled())??
It's useful when the String your passing to logger.debug(...) takes time to evaluate, in that case you can skip this evaluation if debug is not enabled.
if(logger.isDebugEnabled()) {
logger.debug("The meaning of life is " + calculateMeaningOfLife());
}
IMO this makes the code a lot less readable so it should only be used when there's a significant performance improvement.
isDebugEnabled is typically used to avoid unnecessary String concatination, eg this call
logger.debug("Line number = " + n);
first invokes Strings concatination then debug() and only then Logger detects that debug is not enabled and simply returns. This may significantly affect app performance.
This problem is solved in SLF4J which has formatted logging methods like this
public void debug(String format, Object arg);
Java must first resolve the string parameter passed to the debug method before it can call it.
logger.debug("Something.. var1=" + variable1 + " var2=" + variable2);
The above code will result in multiple String objects being created as each + creates another String so you'll have about 5 or more objects created before calling the method.
Debugging will mostly not be enabled so it's more efficient to check if debugging is enabled than to resolve the parameters all the time.
The statement:
if(log.isDebugEnabled()){
Is used just for performance reasons. It's use is optional since it is called by the log method internally.
But now you ask if this check is made internally, so why should I use it? It's very simple: if you log something as simple as this:
log.debug("ResultSet rs is retrieved from OracleTypes");
Then you don't need to do any check. If you compose a string to log using the append operator (+) like this:
log.debug("[" + System.getTimeInMillis() + "] ResultSet rs is retrieved from OracleTypes");
In this case you should check if the log is enabled or not, because if it isn't, even if the log is not made, the string composition is. And I must remind you that the use of operator "+" to concatenate strings is very inefficient.
SLF4J implementation (checked on version 1.7.10) calls isDebugEnabled() in some methods like:
public void debug(String format, Object... arguments) {
if(this.log.isDebugEnabled()) {
FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
this.log.debug(ft.getMessage(), ft.getThrowable());
}
}
but there are also method overloads which doesn't internally check whether given loggin level is enabled, like in:
public void debug(String msg, Throwable t) {
this.log.debug(msg, t);
}
Another thing is that Logger implementation can be changed so if you want to be always sure your logger is called according to it's logging level, then you might want to consider using isDebugEnabled() method.

Categories

Resources