i've a question about processing log lines according to level like;
logger.debug("hello i am a log line not so neccessary, also log level is setted WARN and some complexProcessResult-> {}",
doSomeWorkwhichTakeslongTime());
If logger level sets to WARN so does it call the process "doSomeWorkwhichTakeslongTime()" because jsonizing some classes, take so much time, and i dont want to run this in production. To achieving this, is it enough setting the log level as "warn"?
No, if you use this specific method call, then changing the log level would have no impact on whether or not doSomeWorkWhichTakesLongTime() is being called or not.
It can't have an effect, because the Java language specifies that the parameter values need to be computed before the method is called.
There are two possible workarounds:
The "ugly" one is to use isDebugEnabled():
if (logger.isDebugEnabled()) {
logger.debug("hello i am a log line not so neccessary, also log level is setted WARN and some complexProcessResult-> {}", doSomeWorkwhichTakeslongTime());
}
The other one is a bit trickier, but potentially nicer: Have the heavy lifting happen in the toString() of an object that is either cheap to construct or that you have accessible anyway:
logger.debug("hello i am a log line not so neccessary, also log level is setted WARN and some complexProcessResult-> {}", someObjectThatDescribesTheOutput)
and have someObjectThatDescribesTheOutput have a toString method a little like this:
public String toString() {
return doSomeWorkwhichTakeslongTime();
}
Related
I am trying to define an aspect to inject a logger.
I am looking to create something like:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public aspect LogInjector {
private pointcut executionJoinPoints(): !within(LogInjector) && execution (* *.*(..));
before(): executionJoinPoints(){
// Get class name of the executed code
clazz = ...
final Logger logger = LogManager.getLogger(clazz);
// Get method name of the executed code
method = ...
// Get params name, type and values triplet or values at least if the previous is not possible, of the executed code
params = ...
// Get call stack of the executed code
stack = ...
logger.trace("{}.{}({}) - {}", clazz.name(), method.name(), params, stack);
}
after(): executionJoinPoints(){
// Get class name of the executed code
clazz = ...
final Logger logger = LogManager.getLogger(clazz);
// Get method name of the executed code
method = ...
// Get return value or exception of the executed code
result = ...
logger.trace("{}.{} = {}", clazz.name(), method.name(), result);
}
}
For this I want to retrieve execution metadata/context data:
exceptions
return values
How can get this metadata/context data?
In order to keep your aspect efficient, I recommend the following:
Limit your pointcut to the target packages and classes you really wish to debug. Don't log/trace the whole world. You could also use an abstract base aspect with an abstract pointcut and extend the aspect into a concrete sub-aspect with a concrete pointcut. The latter can even be provided via XML configuration if you use load time weaving.
Use an around() advice instead of a before() / after() pair. Then you only need to calculate some of the logged values once and use them both before and after the original method call done via proceed().
Simply log thisJoinPoint instead of piecing together bits contained therein by default. This will already give you the type of joinpoint, method signature including parameter types and return value.
Don't log parameter names, the information adds no real value. Furthermore, parameter names are subject to refactoring and are only present if your code is compiled with debug information. Keep it simple and only log the parameter values.
In the around() advice mentioned above you can enclose the proceed() call into try-catch-finally and conveniently handle and log any exceptions and stack traces and/or wrap checked exceptions into AspectJ's SoftException or a simple RuntimeException and re-throw them. Whatever is applicable to your situation.
Method call results are just the results of proceed(), which would you also be what you need to return from the around() advice. You can also return something else instead (but it must have the correct return type) or completely skip proceed() if for whatever reason you wish to skip target method execution.
All of what I just said is written in the AspectJ manual or in any other AspectJ tutorial. You might want to read some of those next time before asking a general question like this one.
This question already has an answer here:
How to configure log4j2.xml log level for specific class only?
(1 answer)
Closed 4 years ago.
(I' new to Java, and I read java.util.logging: how to set level by logger package (or prefix)? already. As it couldn't answer my question, here it is)
I'm developing a system that has private static final Logger log = Logger.getLogger(XXX.class.getName()); attributes in several classes (XXX being the corresponding class).
I use log.setLevel(level) with different levels, but all the log objects seem to work at the same log level. Printing the log itself, seems to indicate that the individual log objects are actually the same. I don't understand why.
Debug output is like this:
[CONFIG ]...Parser init: java.util.logging.Logger#6bc7c054 logging level FINE
[CONFIG ]...Tokenizer init: java.util.logging.Logger#6bc7c054 logging level CONFIG
And still the Parser class logs at level CONFIG...
Printing the log itself, seems to indicate that the individual log objects are actually the same. I don't understand why.
Parser and Tokenizer are calling Logger.getLogger(XXX.class.getName()); with the same name XXX class. Modify your code example to print the name of the logger.
Debug output is like this
This is where a Minimal, Complete, and Verifiable example helps me. Levels are used to qualify messages and levels are used to filter messages. If you don't include at least the code that produced your debug output it is hard to tell what the meaning is for each level listed.
You appear to be confusing the Logger's filtering level and a message's logging level.
Each logger has a logging level that acts as a filter for messages;
messages that are logged at a level which is lower than the filtering level of the logger are ignored.
Here is an example (not actual code):
Logger myLogger = Logger.getLogger("somename");
myLogger.setLevel(FINE);
myLogger.fine("fine grained log message");
myLogger.finest("finest level of logging");
myLogger.info("info level message");
myLogger.fine("second fine message");
The "code" above will produce the following messages in the log file:
fine grained log message
info level message
second fine message
Note that the message "finest level of logging" will not appear in the log because FINEST level is lower than FINE and is thus filtered out by the logger.
If I have a java util logging statement such as
logger.log(LEVEL.FINE, "data buffer = {0}",
CommonUtils.prepareDataBufferString(dataBuffer));
Now even when my log level is not at FINE, the expensive prepare.. method still gets called,
which is not what I want to happen. I end up checking the logger level before this statement
if(logger.isLoggable(LEVEL.FINE)){
bufferString = CommonUtils.prepareDataBufferString(dataBuffer);
}
logger.log(LEVEL.FINE, "data buffer = {0}", bufferString);
this increases the lines of code unnecessarily. Can I avoid having to do this somehow. please help.
A technique like this might help.
logger.log(LEVEL.FINE, "data buffer = {0}",
new Object() {
#Override public String toString() {
return CommonUtils.prepareDataBufferString(dataBuffer));
}
});
FYI, it's not the log framework calling the method, it's Java. Java requires that you evaluate all the parameters to a method before the method can be invoked (this is called eager evaluation, contrast to lazy evaluation).
No you can't avoid it with any Java logging API that I know. There are really only two ways in the language to do what you want:
the if statement that you describe
passing an object that implements an interface into the API to produce the details.
No logging API that I know implements (2), and I'm not sure that you'd find it less verbose to pass in an anonymous object.
In my existing application "org.apache.log4j" API's have been used in java code.
Requirement :
I have to log some statement(say xyz) in log file in any case and should not dependent of log levels.For example : if my log level is error then also xyz should print, if my log level is debug then also xyz should print.
I cannot make log statement of xyz is debug because if i do this, other log statements apart from xyz will also start printing.
For this, I believe, I have to add some custom log level.Please help how to do it and how to set its level ordering so that in any case it should print.
Thanks in advance.
Best Regards
What you could do is create a different Logger for those statements (you are not restricted to use classes names when defining a logger)
// Standard logger
private static Logger log = Logger.getLogger(MyClass.class)
// XYZ logger
private static Logger logXYZ = Logger.getLogger("logs.xyz");
You can access the same logger from several class, you just have to pass the same label.
Then, in the configuration file, you can define a different log level for that category, and even output these logs in a different appender (different file, processing, etc.)
You could "hijack" the protected method Logger#forcedLog() to always print to the log.
You must place the hijacker class in the same package as Logger.
package org.apache.log4j;
/**
* #author maba, 2012-08-23
*/
public class LogOverride {
public static void print(Logger logger, String message) {
logger.forcedLog(logger.getName(), Priority.INFO, message, null);
}
}
And from your calling code
log.setLevel(Level.OFF); // Make sure logging is turned off
log.info("Normal logging"); // Will not be seen anywhere
LogOverride.print(log, "Overriding logger"); // Will still make it to your appender
This is what the log4j FAQ says about custom levels:
How do I add a custom level?
It is possible, but rarely appropriate. The request is commonly for a level named something like "audit" that doesn't obviously fit in the progression "trace", "debug", "info", "warn", "error" and "fatal". In that case, the request for a level is really a request for a mechanism to specify a different audience. The appropriate mechanism is to use a distinct logger name (or tree) for "audit" related messages.
So if you want to go with that suggestion then you should look at the answer from SJuan76.
If you do decide to go with the idea of creating a custom, you would need to create a subclass of Level to do this, because the Level constructor is protected.
/**
* Instantiate a Level object.
*/
protected Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
It looks like you should then chain to the Level constructor, passing it a suitable level value. Note that the larger the level number the higher the priority is. So for a Level that won't be blocked at any of the existing named levels, you want a value that is greater than Priority.FATAL_INT which is 50000.
(However, I'm not convinced that this is the right approach. For a start, you probably won't be able to refer to your custom level by name in a logging config file.)
I'm using java.util.logging.Logger logging in my program. How do I enable FINE logging for a single class, while setting it to WARNING for every other class?
I'd prefer to do this programatically in my main() method rather than needing to set up additional properties files.
I know the OP has asked to do this programatically but here's an example of how to do it in the properties file too.
Caveat: I thought it was worthy of inclusion as the header doesn't indicate programatically and many developers will want to manage it through the logging.properties. Also there isn't really a lot on-line about this, it can be confusing and is slightly different to, say log4j
The root logging level is indicated by the .level config. This dictates which events are by default to be captured and "distributed for" logging. The root logging level is the level used by the "root logger" in the logging hierarchy. See this onjava article for more info on the logging hierarchy.
Below, the root log level is set to WARNING so will ordinarily capture only WARNING events. This is inherited by all child loggers in the hierarchy, unless you configure otherwise (later):
.level=WARNING
This root-logging level only indicates what is captured, not what is "distributed". How a captured event (message) is distributed is down to the handlers associated with the logger. For instance, a ConsoleHandler will output the event to the console. For instance:
java.util.logging.ConsoleHandler.level = WARNING
This ConsoleHandler.level indicates the level for which this handler should distribute - or print - the message. So, if a FINE message is received with the above config then this handler will not print it. It will print any messages with a WARNING log level or above though.
Setting to ALL will ensure that the ConsoleHandler will print all messages to the console (an we also need to configure the root level to ensure all are captured):
.level=ALL
java.util.logging.ConsoleHandler.level = ALL
However, this would create a lot of noise which we also don't want. So, to reduce the FINE-level events to those classes we're interested in, we change the logging level of those specific loggers only:
com.level = WARNING
com.mypackage.MyClass1.level = FINE
com.mypackage.MyClass2.level = FINE
com.mypackage.mysubpackage.MyClass3.level = FINE
Note that in the above, I've explicitly set the level for the "com" logger to WARNING.
Logger log = Logger.getLogger(this.getClass().getName()).setLevel(Level.FINE);
If you do not want to have a logger defined for every single class in question but rather want to share loggers between classes, you can alternatively implement your own java.util.logging.Handler that has its own way of filtering for class names using the information provided by LogRecord.getSourceClassName().
I believe that you can set your log level for your Handler and your specific class Logger to FINE, while keeping all the other Loggers for the rest of your code base to WARNING should do the trick. Both the Logger and the Handler need to pass the level filter for a message to be logged.
Well, here's a method I added to my mail class that's actually working. I would still welcome any improvements from others.
private static void setupLogging() {
// To enable FINE logging in a single class, apparently this bewildering
// maze of statements is required.
Logger.getLogger("").setLevel(Level.FINE);
for (Handler handler : Logger.getLogger("").getHandlers()) {
handler.setLevel(Level.FINE);
}
MyOtherClass.logger.setLevel(Level.FINE);
Logger.getLogger("").setLevel(Level.WARNING);
}