Log4j Implicit String Formatting - java

I am using log4j v1.2.14 for logging in my project and I am also using Java 7 String.format() to put variables in my output. Currently I am writing
LOGGER.info(String.format("Your var is [%s] and you are [%s]", myVar, myVar1));
Is this really the best way to output strings? I feel that log4j should have this implemented implicitly as below:
LOGGER.info("Your var is [%s] and you are [%s]", myVar, myVar1);
Have I missed something? Further, are there any Java logging frameworks that support this?

slf4j's api provides "parameterized logging", which allows you to do exactly that, although with a slightly different syntax. The example there is:
logger.debug("Value {} was inserted between {} and {}.", newVal, below, above);
For an implementation, you can use Logback which implements slf4j natively, or the slf4j bindings to connect with log4j or other loggers. The User Manual explains that, along with a short example.

Using String.format, +, or a string formatter other than the one provided by your logging system (log4j for example) is considered as a bad practice.
Usually, in the code there are lots of low level logs (debug, info) you don't want to see in production. If you use for example String.format to format the string to log, then you will allocate on the heap and format a new String, which can be very long and consume resources, even if at the end nothing will be logged (for example if the log4j min level is set to warning or error).
By using the logger formatter system (like the one from log4j), you allow your logger to avoid the generation of the formatted string if it doesn't need to be logged.
This may make a great difference in some cases.

Log4j supports internal formatting. I haven't found it documented anywhere, but I saw an example of it here:
https://logging.apache.org/log4j/2.x/manual/markers.html
I tried it out and it works! I'm on log4j 2.11.2.
int i = 42;
String str1 = "the answer";
String str2 = "life, the universe, and everything";
console.info("{} is {} to {}", i, str1, str2);
Looking at the javadoc for Logger, I'd say it was introduced in Lo4j 2, and supports up to 10 parameters.
https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html

Btw, In this scenario there is not much difference between using + to add your variables to the string and String.format - unless you really want to reuse the "Your var is..." in all your logs.
slf4j lets you log as
log.info("Your var is {} and you are {}", myVar, myVar1);
Note the use of {} instead of print formatters. Also this requires Java >= 1.5

I upvoted the "use logger.debug(format, varargs)" approach first, because it doesn't allocate extra String when it's not needed.
But then it occurred to me that logger.debug(String format, Object... varargs) still allocates an Array for the varargs.
Tried this on https://godbolt.org/noscript/java
class VarargTest {
static boolean debugging = false;
static void debug(String format, Object... args) {
if (debugging) {
System.out.println(String.format(format, args));
}
}
static void logVarargs(int n) {
debug("debug message {}", n);
}
static void logIf(int n) {
if (VarargTest.debugging) {
debug("debug message 2 " + n);
}
}
}
and indeed, the resulting debug() call allocates an Array.
So the fastest code should be
if (logger.isDebugEnabled()) {
logger.debug("format {}", arg);
}
If it's not super performance critical, a more readable and reasonably fast code would be simply
logger.debug("format {}", arg);

Related

How to solve this issue "Standard outputs should not be used directly to log anything "

I need a print statement. But sonar not allowed this type.
String txt="Something";
System.out.println("Print: "+txt);
Expected output:
Print: Something
I tried this format logger.log().its not working for our requirement.
Sonarqube does not like it if you use System.out, or if you have string concatenation or other computation in the arguments you pass to the logging function. Construct the full message on a separate line before logging it:
String txt = "Print: " + "Something";
String logMessage = "Print: " + txt;
logger.log(Level.INFO, logMessage);
If logger is not meeting your requirements, make it to do so by configuring it.
There are some widely used logging frameworks that are highly configurable and I doubt that they will not be able to do whatever you are doing with plain System.out.println
For example, check out very popular SLF4J and Logback as logging provider.
http://www.slf4j.org/
http://logback.qos.ch/

I created my own logger in Java. Is it a bad idea? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I'm developing a web crawler using Java and XPages. I have had a lot of compatibility issues between log libraries like SLF4J, Log4J, java.util.logging, etc. and Lotus Notes.
So, I developed my own very simple logger class.
import java.io.PrintWriter;
import java.util.Calendar;
import org.apache.commons.lang3.exception.ExceptionUtils;
public class Logger {
private static final Calendar calendar = Calendar.getInstance();
public enum Level {NONE, ERROR, WARNING, INFO, DEBUG, TRACE}
private Level level = Level.NONE;
private PrintWriter stream = null;
public void setLevel(Level l) {
level = l;
}
public void setPrintWriter(PrintWriter f) {
stream = f;
}
public void error(String m) {
if (Level.ERROR.compareTo(level) <= 0) {
format("ERROR", m);
}
}
public void error(String m, Throwable e) {
if (Level.ERROR.compareTo(level) <= 0) {
StringBuilder sb = new StringBuilder(1024);
sb.append(m);
sb.append(ExceptionUtils.getStackTrace(e));
format("ERROR", sb.toString());
}
}
public void warn(String m) {
if (Level.WARNING.compareTo(level) <= 0) {
format("WARNING", m);
}
}
public void warn(String m, Throwable e) {
if (Level.WARNING.compareTo(level) <= 0) {
StringBuilder sb = new StringBuilder(1024);
sb.append(m);
sb.append(ExceptionUtils.getStackTrace(e));
format("WARNING", sb.toString());
}
}
public void debug(String m) {
if (Level.DEBUG.compareTo(level) <= 0) {
format("DEBUG", m);
}
}
public void info(String m) {
if (Level.INFO.compareTo(level) <= 0) {
format("INFO", m);
}
}
public void trace(String m) {
if (Level.TRACE.compareTo(level) <= 0) {
format("TRACE", m);
}
}
public void format(String type, String m) {
StringBuilder sb = new StringBuilder(1024);
sb.append("[");
sb.append(type);
sb.append("] ");
sb.append(calendar.getTime().toString());
sb.append(" - ");
sb.append(m);
String line = sb.toString();
System.out.println(line);
if (stream != null) {
stream.println(line);
stream.flush();
}
}
}
My questions:
Is it a bad idea to write a logger? Do I have to insist in make it works one of the existent log libraries?
If I decide to use always this logger, what features I will miss from the log libraries like SLF4J, Log4J, java.util.logging, etc.?
Edit: Thanks to your answers, this would be the recommendation so far:
If I can avoid to write a custom logger, perfect. If I have a good reason to do it, I will hope that no one will kill me.
There are a lot of features that I would miss when the application be pushed live.
So far, the Chain-of-responsibility pattern looks like a good idea to implement in short time and give me more control over the output.
Anyway, I'm going to check again the code just in case I miss something and I can make it work with a well-known log library. It's worth trying.
I had have lot of compatibility problems between the log libraries like SLF4J, Log4J, java.util.logging, etc. and Lotus Notes.
Without knowing what those issues were, it is difficult to advise you. There may have been a simple fix or work-around.
Is it a bad idea to write a logger?
Reinventing the wheel is a bad idea. Unless you really need a different kind of wheel.
Do I have to insist in make it works one of the existent log libraries?
Your choice.
If I decide to use always this logger, what features I will miss from the log libraries like SLF4J, Log4J, java.util.logging, etc.?
Hard to say what you will miss, because it depends on what you need.
However, some of the things that are missing from your attempted solution include:
Logging to multiple files, file rotation, etcetera.
Logging to a variety of kinds of event handling systems.
Coarse / fine-grain configurability of logging ... without changing code.
Compatibility; e.g. if you need to integrate with 3rd party software that uses a standard logging framework.
And then there is the revulsion that other Java programmers are liable to express if they need to maintain your code.
Well I haven't worked extensively with loggers but one thing which comes to my mind is that with log libraries you have certain configuration which comes really handy.
For example - you can set the size of your log file, suppose 200 MB, as soon as file reaches 200MB it will create a new log file and on top of that you can set a maximum limit for example 10 GB or 10 backup files, Hence if all the log files reaches 10GB it would start deleting oldest file.
Also some log libraries provides different log level applied on different packages. That's really useful specially in development.
If you can handle those on your own I don't see much problem in using custom loggers.
Why would it be a bad idea to write your own logger if the other log libraries don't meet your needs? But to be honest. This is a log class around system.out.println(). I would hardly call that a logger.
The problem with this is that I will be very hard for an admin / you to troubleshoot this. Now all messages will be placed in the log.nsf and for that when you are debugging your log will be unclear and hard to read.
Please take a look at openlog project and add your own wrapper around it to make it work as you want it (if needed). This way you can have your own log database (or log in the same database? ) which will be easier to troubleshoot.

Encrypted logger for Java

I'll put the question upfront:
Is there a logger available in Java that does encryption(preferably 128-bit AES or better)?
I've done a lot of searching for this over the last couple of days. There's a few common themes to what I've found:
Dissecting information between log4j and log4j2 is giving me headaches(but mostly unrelated to the task at hand)
Most threads are dated, including the ones here on SO. This one is probably the best I've found on SO, and one of the newer answers links to a roll-your-own version.
The most common answer is "roll-your-own", but these answers are also a few years old at this point.
A lot of people question why I or anyone would do this in Java anyway, since it's simple enough to analyze Java code even without the source.
For the last point, it's pretty much a moot point for my project. We also use a code obfuscator and could employ other obfuscation techniques. The point of using encryption is simply to raise the bar of figuring out our logs above "trivially easy", even if it's only raised to "mildly time-consuming". A slightly relevant aside - the kind of logging we're going to encrypt is intended merely for alpha/beta, and will likely only include debug, warn, and error levels of logging(so the number of messages to encrypt should be fairly low).
The best I've found for Log4j2 is in their documentation:
KeyProviders
Some components within Log4j may provide the ability to perform data encryption. These components require a secret key to perform the encryption. Applications may provide the key by creating a class that implements the SecretKeyProvider interface.
But I haven't really found anything other than wispy statements along the lines of 'plug-ins are able of doing encryption'. I haven't found a plug-in that actually has that capability.
I have also just started trying to find other loggers for Java to see if they have one implemented, but nothing is really jumping out for searches like 'java logging encryption'.
Basically log encryption is not best practise there are limited situations where you can need this functionality. As mainly people which have access to logs have also access to JVM, and in JVM all the logs are at least generated as Strings so even if you encrypt them in the log file or console the real values will be available in JVM String Pool, so if anyone will every need to hack your logs it will be as easy as have a look in string pool.
But anyway if you need a way to encrypt the logs, and as there is no generic way for this, the best way in my opinion is to go with Aspect J. This will have minimum impact on you sources, you will write code as you have done before, but the logs will be encrypted. Following is a simple application code which will encrypt all the logs from all the compiled sources using Aspctj, and Slf4j as logging facade and Log4j2 as logging implementation.
The simple class which logs the "Hello World"
public class Main {
private static final transient Logger LOG = LoggerFactory
.getLogger(Main.class);
public static void main(String[] args) {
LOG.info("Hello World");
LOG.info("Hello {0}", "World 2");
}
}
Aspect which encrypts (in this case just edits the text)
#Aspect
public class LogEncryptAspect {
#Around("call(* org.slf4j.Logger.info(..))")
public Object encryptLog (ProceedingJoinPoint thisJoinPoint) throws Throwable{
Object[] arguments = thisJoinPoint.getArgs();
if(arguments[0] instanceof String){
String encryptedLog = encryptLogMessage ((String) arguments[0], arguments.length > 1 ? Arrays.copyOfRange(arguments, 1, arguments.length) : null);
arguments[0] = encryptedLog;
}
return thisJoinPoint.proceed(arguments);
}
// TODO change this to apply some kind of encryption
public final String encryptLogMessage (String message, Object... args){
if(args != null){
return MessageFormat.format(message, args) + " encrypted";
}
return message + " encrypted";
}
}
The output is :
[main] INFO xxx.Main - Hello World encrypted
[main] INFO xxx.Main - Hello World 2 encrypted

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