davidchang168 (8) [Avatar] Offline
#1
Hi

I'm using JDK logging API in my codes as below and control the log level with logging.properties

if (mylogger.isLoggable(Level.FINE)) {
// do FINE level log here
}

if (mylogger.isLoggable(Level.FINER)) {
// do FINER leve log here
}

And now I'd like to make Logger.isLoggable(Level) always return true(without touching logging.properties) with the aspect listed below

aspect MakeAllLoggable {
boolean around(Level level): target(java.util.logging.Logger) &&
call(boolean isLoggable(..)) && args(level) {
return true;
}
}

But I found this does not work because it still reads logging.properties to determine the log level which means the returned boolean value of isLoggable(Level) still controlled by logging.properties.
So I was wondering what is the right way to make isLoggable(Level) return true ?

Thanks,
David
davidchang168 (8) [Avatar] Offline
#2
Re: How to make "Logger.isLoggable()" always return true ?
I just modified the aspect a little bit as below:

aspect MakeAllLoggable {
boolean around(Level level): target(java.util.logging.Logger) &&
call(boolean isLoggable(..)) && args(level) {
boolean returnValue = proceed(level);
return true;
}
}

The change is due to a statement in page 279 of the 1st edition "AspectJ in Action" (which has been removed from the 2nd edition) it reads "... unless the around advice calls proceed(), the captured join point will not be executed at all" but even with the change the isLoggable(..) call still not always return true as I expected.

David
davidchang168 (8) [Avatar] Offline
#3
Re: How to make "Logger.isLoggable()" always return true ?
More information on the ajc generated class file ..

The Java code below with the aspect mentioned before
if (m_logger.isLoggable(Level.FINEST)) {
...
}


will be compiled as below:

Level localLevel1 = Level.FINEST;
Logger localLogger1 = this.m_logger;
if (isLoggable_aroundBody25$advice(this, localLogger1, localLevel1, MakeAllLoggable.aspectOf(), localLevel1, null))
ramnivas (171) [Avatar] Offline
#4
Re: How to make "Logger.isLoggable()" always return true ?
How does your code for the
// do FINE level log here
part look?

I suspect that you are calling something like
logger.log(LogLevel.FINE, ...), in which case log4j will be checking for the log level and even if your advise returns true, the re-test by log4j will fail. In that case, you will need to weave into log4j jar to get the effect you are looking for.

-Ramnivas
davidchang168 (8) [Avatar] Offline
#5
Re: How to make "Logger.isLoggable()" always return true ?
Hi Ramnivas

Thanks for your question, actually I'm not using Log4J, I'm using java.util.logging.Logger instead. And the code snippet below use Logger.logp(...) to log runtime information.

if (m_logger.isLoggable(Level.FINEST)) {
m_logger.logp(Level.FINEST, toString() + ":" + CLASS_NAME, "handleMessage()", "Received " + msg.getClass().getName() + " from " + header.getSender());
}

Note: m_logger is an instance of java.util.logging.Logger

Below is the part of the aspect I use for testing purpose ..

// DynamicLogSetting.java
// =================
package cmp.aspect.util;

import java.util.logging.Level;
import java.util.logging.Logger;

import java.io.PrintStream;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.SourceLocation;
import com.hazelcast.core.Hazelcast;
import java.util.Map;

aspect MakeAllLoggable {
pointcut isEnabledLog(Level level): call (boolean Logger.isLoggable(..)) && args(level) && !within(MakeAllLoggable) && !within(ut..*);

boolean around(Level level): isEnabledLog(level) {
boolean ret = proceed(level);
return true;
}
}

public aspect DynamicLogSetting {
...
declare precedence: MakeAllLoggable, DynamicLogSetting;

pointcut logpCall(Level level, String cn, String mn, String msg): call(void Logger.logp(..)) && args(level, cn, mn, msg);

void around(Level level, String cn, String mn, String msg): logpCall(level, cn, mn, msg)
{
if (loggable(level)) {
getFormattedLogParms(thisJoinPointStaticPart.getSourceLocation(),
thisEnclosingJoinPointStaticPart.getSignature(), msg, false);
proceed(level, "[" + clazz, method + "]", lineMsg);
}
}

private void getFormattedLogParms(final SourceLocation loc, final Signature sig, final String msg, boolean skip)
{
clazz = sig.getDeclaringType().getSimpleName();
method = sig.getName();
if (skip) {
lineMsg = "line " + loc.getLine() + ": ";
} else {
lineMsg = "line " + loc.getLine() + ": " + msg;
}
}

public boolean loggable(Level level) {
String obj = map.get("level");

if (obj == null) {
// Default to ALL.
return true;
} else {
logLevel = Level.parse(obj).intValue();
}

if (level.intValue() < logLevel || logLevel == Level.OFF.intValue()) {
return false;
}

return true;
}
...
}
davidchang168 (8) [Avatar] Offline
#6
Re: How to make "Logger.isLoggable()" always return true ?
And you are right, the source code for Logger.logp(...) below do re-test the level ... smilie

public void logp(Level level, String sourceClass, String sourceMethod, String msg) {
if (level.intValue() < levelValue || levelValue == offValue) {
return;
}

LogRecord lr = new LogRecord(level, msg);
lr.setSourceClassName(sourceClass);
lr.setSourceMethodName(sourceMethod);
doLog(lr);
}
ramnivas (171) [Avatar] Offline
#7
Re: How to make "Logger.isLoggable()" always return true ?
How about doing something along the following lines (along with you advice):

void around(Level level) : call(* Logger.logp(Level, ..) && args(level, ..) {
proceed(Level.DEBUG);
}

This way you are replacing the first argument to make the re-test pass.

-Ramnivas
davidchang168 (8) [Avatar] Offline
#8
Re: How to make "Logger.isLoggable()" always return true ?
Thanks Raminivas, I thought about this yesterday and believe this is better then the approach of weaving into Java logging jar. But since my intention is to bypass the setting in logging.properties, in order to make the re-test pass I have to set the level to its hightest so that no matter what the level get changed in logging.properties the logp() still can proceed, but the drawback is my log output show all the log entries at one level(such as SEVERE in my case) which means you won't be able to see the different level of log (FINE/FINER/FINEST/INFO/WARNING/SEVERE) in the log output.

Thanks,
David