user2218825
user2218825

Reputation:

Make my logger very effective to my Java-application

I am struggling with the following problem and ask for help.

My application has a logger module. This takes the trace level and the message (as string).

Often should be messages constructed from different sources and/or different ways (e.G. once using String.format in prior of logging, other times using .toString methods of different objects etc). Therefore: the construction method of the error messages cannot be generalized.

What I want is, to make my logger module effective. That means: the trace messages would only then be constructed if the actual trace level gets the message. And this by preventing copy-paste code in my application.

With C/C++, by using macros it was very easy to achive:

#define LOG_IT(level, message) if(level>=App.actLevel_) LOG_MSG(message);

The LOG_MSG and the string construction was done only if the trace level enabled that message.

With Java, I don't find any similar possibility for that. That to prevent: the logging would be one line (no if-else copy-pastes everywhere), and the string construction (expensive operation) only be done if necessary.

The only solution I know, is to surrond every logger-calls with an IF-statement. But this is exactly what I avoided previously in the C++ app, and what I want to avoid in my actual Java-implementation.

My problem is, on the target system only Java 1.6 is available. Therefore the Supplier is not a choice.

What can I do in Java? How can this C/C++ method easily be done?

Upvotes: 1

Views: 525

Answers (3)

jste89
jste89

Reputation: 446

Firstly, I would encourage you to read this if you're thinking about implementing your own logger.

Then, I'd encourage you to look at a well-established logging API such as SLF4j. Whilst it is possible to create your own, using a pre-existing API will save you time, effort and above all else provide you with more features and flexibility out of the box (I.e file based configuration, customisability (look at Mapped Diagnostic Context)).

To your specific question, there isn't a simple way to do what you're trying to do. C/C++ are fundamentally different to java in that the preprocessor allows for macros like you've created above. Java doesn't really have an easy-to-use equivalent, though there are examples of projects that do make use of compile time code generation which is probably the closest equivalent (i.e. Project Lombok, Mapstruct).

The simplest way I know of to avoid expensive string building operations whilst logging is to surround the building of the string with a simple conditional:

if ( logger.isTraceEnabled() )
{
    // Really expensive operation here
}

Or, if you're using Java 8, the standard logging library takes a java.util.function.Supplier<T> argument which will only be executed if the current log level matches that of the logging method being called:

 log.fine(()-> "Value is: " + getValue());

There is also currently a ticket open for SLF4j to implement this functionality here.

If you're really really set on implementing your own logger, the two above features are easy enough to implement yourself, but again I'd encourage you not to.

Edit: Aspectj compile time weaving can be used to achieve something similar to what you're trying to achieve. It would allow you to wrap all your logging statements with a conditional statement in order to remove the boilerplate checking.

Upvotes: 1

Angelos Asonitis
Angelos Asonitis

Reputation: 194

I think the most important thing to understand here is that the C/C++ macro solution, does not save computational effort by not constructing the logged message, in case the log level was such that the message would not be logged. Why is so? Simply because the macro method would make the pre-processor substitute every usage of the macro:

LOG_IT(level, message)

with the code:

if(level>=App.actLevel_) LOG_MSG(message);

Substituting anything you passed as level and anything you passed as message along with the macro itself. The resulting code to be compiled will be exactly the same as if you copied and pasted the macro code everywhere in your program. The only thing macros help you with, is to avoid the actual copying and pasting, and to make the code more readable and maintainable.
Sometimes they manage to do it, other times they make the code more cryptic and thus harder to maintain as a result. In any case, macros do not provide deferred execution to save you from actually constructing the string, as Java8 Logger class does by using lambda expressions. Java defers the execution of the body of a lambda until the last possible time. In other words, the body of the lambda is executed after the if statement.
To go back to your example in C\C++, you as a developer, would probably want the code to work regardless of the log level, so you would be forced to construct a valid string message and pass it to the macro. Otherwise in certain log levels, the program would crash! So, since the message string construction code must be before the call to the macro, you will execute it every time, regardless of the log level.

So, to make the equivalent to your code is quite simple in Java 6! You just use the built-in class: Logger. This class provides support for logging levels automatically, so you do not need to create a custom implementation of them.
If what you are asking is how to implement deferred execution without lambdas, though, I do not think it is possible.

If you wanted to make real deferred execution in C\C++ you would have to make the logging code such, as to take a function pointer to a function returning the message string, you would make your code execute the function passed to you by the function pointer inside the if statement and then you would call your macro passing not a string but a function that creates and returns the string! I believe the actual C\C++ code to do this is out of scope for this question... The key concept here, is that C\C++ provide you the tools to make deferred execution, simply because they support function pointers. Java does not support function pointers, until Java8.

Upvotes: 0

QuentinC
QuentinC

Reputation: 14752

Newest logging libraryies, including java.util.logging, have a second form of methods, taking a Supplier<String>.

e.g. log.info( ()->"Hello"); instead of log.info("Hello");.

The get() method of the supplier is only called if the message has effectively to be logged, therefore your string is only constructed in that case.

Upvotes: 0

Related Questions