Enabling method parameters logging in Java logging
I know that it is possible to log some information without access to the source code of the classes just by running java with
java -Djava.util.logging.config.file=myLoggingConfigFilePath
However, I can't configure that config file to log the entry parameters and/or return values. If I hard code in the method like below:
public int dodaj(float p1, float p2){
// Log entry
Logger logger = Logger.getLogger("sorter");
if (logger.isLoggable(Level.FINER)) {
logger.entering(this.getClass().getName(), "dodaj",
new Object[]{new Float(p1), new Float(p2)});
}
if (logger.isLoggable(Level.FINER)) {
logger.exiting(this.getClass().getName(), "dodaj",
new Integer( (int) (p1+ p2)));
}
return (int)(p1 + p2);
}
this works. However, I would like not to put lines:
if (logger.isLoggable(Level.FINER)) {
logger.entering(this.getClass().getName(), "dodaj",
new Object[]{new Float(p1), new Float(p2)});
}
if (logger.isLoggable(Level.FINER)) {
logger.exiting(this.getClass().getName(), "dodaj",
new Integer( (int) (p1+ p2)));
}
into the source code of method 'dodaj'. I would like to have the source of my method 'dodaj' like that:
public int dodaj(float p1, float p2){
return (int)(p1 + p2);
}
And I would like to write in my 'myLoggingConfigFilePath' write something:
.level=ALL
or .level=ENTRY
or .level=RETURN
But I don't know what should I put in my config file exactly and where. I can't deduce from the documentation if it possible to enable logging method and parameters method and returned results by the config file (properties).
Thank you for any help...
[1777 byte] By [
Saer] at [2007-9-30 15:01:41]

Eh, what?You can't enable logging on a class/method that doesn't use logging. So you have to sprinkle your code with logging statements, or you could look into aspect programming./Kaj
kajbj at 2007-7-5 21:34:24 >

And here is a link that shows how to use aspects for logging: http://www-106.ibm.com/developerworks/java/library/j-aspectj/?loc=j/Kaj
kajbj at 2007-7-5 21:34:24 >

Yes, I can log some information at
// Generator.java
package sorter;
import java.util.Vector;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Generator extends Remote{
public Vector generate(int num) throws RemoteException;
}
// -- end Generator.java
// -- start GeneratorImpl.java
package sorter;
import java.rmi.*;
import java.util.Vector;
import java.util.Random;
import java.rmi.server.UnicastRemoteObject;
public class GeneratorImpl
extends UnicastRemoteObject implements Generator{
public GeneratorImpl() throws RemoteException{
}
public Vector generate(int num)
throws RemoteException{
Random r = new Random(
java.util.Calendar.getInstance().getTime().getTime());
Vector vec = new Vector();
for(int i = 0; i < num; i ++){
vec.add(i, new Integer(r.nextInt() % 100) );
}
return vec;
}
}
// -- end GeneratorImpl.java
// start Servers.java
package sorter;
import java.rmi.RMISecurityManager;
import java.rmi.Naming;
import java.util.logging.*;
public class Servers {
public static void main(String args[]) {
// Create and install a security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
// run generator
GeneratorImpl gen = new GeneratorImpl();
// Bind this object instance to the name "HelloServer"
Naming.rebind("//localhost/GeneratorServer", gen);
} catch (Exception e) {
System.out.println("Servers err: " + e.getMessage());
e.printStackTrace();
}
}
}
// Client.java
package sorter;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.Date;
import java.util.Vector;
import java.util.logging.*;
public class Client{
public static void main(String[] argv) {
String serverName = "";
Generator gen = null;
Vector vec = null;
System.setSecurityManager(new RMISecurityManager());
if (argv.length != 1) {
try {
serverName = java.net.InetAddress.getLocalHost().getHostName();
}
catch(Exception e) {
e.printStackTrace();
}
}
try {
//bind server object to object in client
gen = (Generator) Naming.lookup("//" + serverName + "/GeneratorServer");
//Print success message
System.out.println("RMI connection to GeneratorServer successful...");
//Call method on server and put result into String message
vec = gen.generate(11);
//Print message on server
System.out.println("Value " + vec);
}
catch(Exception e) {
System.out.println("Exception occurred: " + e);
System.exit(0);
}
}
}
// end Client.java
// clientlog.props
handlers=java.util.logging.FileHandler
.level=ALL
sorter.level=ALL
sorter.Client.level=ALL
java.util.logging.FileHandler.pattern=/tmp/log/client%u.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
// end clientlog.props
// start serverlog.props
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
.level=ALL
sorter.level=ALL
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=/tmp/log/server%u.log
java.util.logging.FileHandler.limit=50000
java.util.logging.FileHandler.count=1
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
// end severlog.props
// start rmi.policy
grant {
// Allow everything for now
permission java.net.SocketPermission "*:1024-65535", "accept, connect, listen";
permission java.security.AllPermission;
permission java.io.FilePermission "/tmp/log/*", "write, read";
permission java.util.logging.LoggingPermission "control";
};
// end rmi.policy
I wrote a very simple RMI program I showed above that generates random vector of integer numbers and ran it with command:
$rmiregistry
$java -Djava.security.policy=file:/jbproject/sorter/rmi.policy -Djava.util.logging.config.file=/jbproject/sorter/serverlog.props sorter.Servers
$java -Djava.security.policy=file:/jbproject/sorter/rmi.policy -Djava.util.logging.config.file=/jbproject/sorter/clientlog.props sorter.Client
And I got the following log output in /tmp/log:
///-- client0.log
..........................
2004-08-10 11:12:53 sun.rmi.server.UnicastRef invoke
FINER: main: method: public abstract java.util.Vector sorter.Generator.generate(int) throws java.rmi.RemoteException
2004-08-10 11:12:53 sun.rmi.server.UnicastRef logClientCall
FINER: main: outbound call: [endpoint:[192.168.0.1:36424](remote),objID:[19ee1ac:fe47f89fa9:-8000, 0]] : sorter.GeneratorImpl_Stub[19ee1ac:fe47f89fa9:-8000, 0]: public abstract java.util.Vector sorter.Generator.generate(int) throws java.rmi.RemoteException
2004-08-10 11:12:53 sun.rmi.transport.tcp.TCPChannel newConnection
FINE: main: reuse connection
2004-08-10 11:12:53 sun.rmi.server.UnicastRef invoke
FINER: main: opnum = 7324764122476237606
2004-08-10 11:12:53 sun.rmi.transport.StreamRemoteCall <init>
FINER: main: write remote call header...
................
// -- end client0.log
// -- server0.log
.....................
2004-08-10 11:12:49 sun.rmi.transport.ObjectTable putTarget
FINER: main: add object [0]
2004-08-10 11:12:49 sun.rmi.server.UnicastServerRef computeMethodHash
FINER: main: string used for method hash: "generate(I)Ljava/util/Vector;"
2004-08-10 11:12:49 sun.rmi.server.UnicastRef newCall
FINE: main: get connection
2004-08-10 11:12:49 sun.rmi.transport.tcp.TCPChannel createConnection
FINE: main: create connection
........................
// -- end server0.log
You can see that sorter.Generator.generate(int) is invoked. And please notice that there is no single line in the source code with Logger, or log() apart from config files.
As far as I can understand logging mechanisms is embedded in JDK so I only need to enable it and configure. Of course I can put log() statements in the soruce code if I wish. But I can get some information about execution without any additional instruction put into my source code. Please notice, that this is the FINER level and you can read in documentation that entering() and exiting() also log information at FINER level (as I remember). So if I configure to log ALL actions why I cannot enable also logging method invocation with parameter values without additional instrumentation of my source code.
However, thank you 'kajbj'.
Saer at 2007-7-5 21:34:24 >

Hi,
Sorry to say, but look at the logging statements. They are all logged by sun classes. Look at the documentation, and learn how to read the log. The statement:
2004-08-10 11:12:53 sun.rmi.server.UnicastRef invoke
FINER: main: method: public abstract java.util.Vector sorter.Generator.generate(int) throws java.rmi.RemoteException
Is logged by the class sun.rmi.server.UnicastRef. I.e. sun has logging statements in their code, and that is what you have turned on. So your code hasn't written, or caused anything to be written to the log.
I'm pretty sure that you can't log without adding logging to your code (which an aspect can do)
/Kaj
kajbj at 2007-7-5 21:34:24 >

Hi Kaj,
I am just looking around the matter since I am not familiar with logging facility (I have been just learning it since Sunday) - I try log4j but it seems not so powerful as JDK 1.4 logging (by the way it looks as JDK incorporated main concepts from log4j).
It helps me a lot since it means that I need to write a proxy object and instrument code at the proxy level or it means that I should instrument the rmi stubs in order to log parameters or returned values.
Currently I am reading and exercising the AspectJ as you suggested. However, I am not pretty sure if I can use it for RMI applications since AspectJ uses its own compiler. Do you know anything if RMI works fine with AspectJ?
Thanks.
Saer at 2007-7-5 21:34:24 >

> I am just looking around the matter since I am not familiar with logging facility (I have been just
> learning it since Sunday) -
Then you still have a lot to learn.
> I try log4j but it seems not so powerful as JDK 1.4 logging (by the way it
> looks as JDK incorporated main concepts from log4j).
Yes, indeed, log4j is older, and the JDK for logging did borrow liberally from log4j. (Some folks would take offence at your statement that log4j isn't as powerful. They'd argue that the JDK logging facility watered down log4j.)
> It helps me a lot since it means that I need to write
> a proxy object and instrument code at the proxy level
> or it means that I should instrument the rmi stubs in
> order to log parameters or returned values.
If the Sun classes are putting out logging info, then Sun put the logging statements in there to do it. You can see them at any time by looking at the source code you downloaded with your JDK.
> Currently I am reading and exercising the AspectJ as
> you suggested. However, I am not pretty sure if I can
> use it for RMI applications since AspectJ uses its own
> compiler. Do you know anything if RMI works fine with
> AspectJ?
Since you're writing the RMI classes, I'd say that it'd be easy for you to add logging to your stuff. I'd be surprised if you needed more than that.
My advice would be to learn logging without AspectJ for now. Aspects might be very important someday, but you can solve your fundamental logging problem without it today.
%
> My advice would be to learn logging without AspectJ
> for now. Aspects might be very important someday, but
> you can solve your fundamental logging problem without
> it today.
>
I totally agree with duffymo, you probably don't want to use Aspects, it's just an interesting concept. It's better if you add logging statements to your code.
/Kaj
kajbj at 2007-7-5 21:34:24 >

> However, I can't configure that config file to log the entry parameters and/or return values. If I hard code
> in the method like below:
It's possible to do what you want. You just need to study the logging docs some more.
It's not necessary to write lines of code like this. All you have to do is have the logger.entering() call. The level is set in the file external to your program.
> And I would like to write in my 'myLoggingConfigFilePath' write something:>
> .level=ALL
>
> or .level=ENTRY
> or .level=RETURN
Yes, you can turn on logging at a class level if you wish.
You need to set the following JVM arg when you run your Java app:
-Djava.util.logging.config.file=<path-to-your-logging-config-file>
And here's what the logging config should look like to write to a file:
# Global defaults
.level= ALL
handlers= java.util.logging.FileHandler
java.util.logging.FileHandler.level = ALL
java.util.logging.FileHandler.pattern= /path/to/LogTester-%g.log
java.util.logging.FileHandler.limit = 100000
java.util.logging.FileHandler.formatter= java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append= false
# Turn on class-specific logging levels here
com.foo.bar.LogTester.level= ALL
com.foo.bar.LogClass1.level= FINEST
com.foo.bar.LogClass2.level= INFO
%
The ONLY time you might need stuff like
if (logger.isLoggable(Level.FINEST))
{
}
would be if you were concatenating some strings to create a logging message. This can be an expensive operation, especially if your code is peppered with them. Surrounding those cases with this IF test gives the Hotspot compiler a chance to eliminate the operation.
%
Thanks Guys,
'duffymo': Yes, I can do that but I need as Kaj and you say first to log something i.e. put lines with logger.entering() and logger.exiting(). And I would like not to instrument the source code and I would like to have a log. It means that:
1. I should have an instrumented JVM (but it would be probably much more slower). However, I should do it on my own.
2. I should put log instructions somewhere (to stubs, source code, or proxies, etc.) and then use logging facility supported by JDK 1.4.
As Kaj has said: it is not possible to log without logging.
Concerning log4j, I think that I will be politically correct if I say that log4j is so good that it has been incorporated into official JDK 1.4 release. Make peace not war.
Thanks again Guys. I reward Kaj since (s)he was first who interested in this subject.
Saer at 2007-7-5 21:34:24 >

> 'duffymo': Yes, I can do that but I need as Kaj and
> you say first to log something i.e. put lines with
> logger.entering() and logger.exiting(). And I would
> like not to instrument the source code and I would
> like to have a log. It means that:
You have no choice. You have to instrument the source code. Can't have logging without it. Even AspectJ isn't magic that way. You have to tell the JVM where to do it somewhere. All AspectJ does is move it out of your code and into deployment descriptors.
Rod Johnson says that writing a class means instrumenting it for logging. I take that advice to heart. That's what I do from now on.
> 1. I should have an instrumented JVM (but it would be
> probably much more slower). However, I should do it
> on my own.
But you don't.
> 2. I should put log instructions somewhere (to stubs,
> source code, or proxies, etc.) and then use logging
> facility supported by JDK 1.4.
Right.
> As Kaj has said: it is not possible to log without logging.
Indeed.
> Concerning log4j, I think that I will be politically
> correct if I say that log4j is so good that it has
> been incorporated into official JDK 1.4 release. Make
> peace not war.
Fine with me. I'm not one of those folks who's upset about log4j. They all work at IBM. ;) I'm just pointing out the history.
%
