• The registry, opening a lease for the server named Alex
• The client, opening a lease for the server named Bob
• The client, opening a lease for the server named Alex
However, I was able to figure that out because I had a fair amount of other information about the servers which I used when looking at the log.
Method call logs are not particularly useful unless youre watching them under carefully controlled conditions e.g., in which you do something and then immediately see associated entries in the
log.
16.3.2 The Specialized Logs
In addition to the standard log, RMI has five different specialized logs that track particular aspects of the runtime. These logs are: the transport log, the proxy log, the loader log, the DGC log, and
the TCP log. Each of these specialized logs is associated with an instance of java.rmi.server.LogStream
. To get the Logstream
associated with a log, call a static method defined in the
LogStream class:
public static LogStream logString name Once you have the instance of
LogStream associated with a particular log, you can set the
actual output stream for the log using the setOutputStream
method. Here, for example, is code that sets up five different log files, one for each of the five specialized logs:
private static void setLogFiles { try {
FileOutputStream transportLogFile = new FileOutputStream
C:\\temp\\transportLogFile; FileOutputStream proxyLogFile = new
FileOutputStream C:\\temp\\proxyLogFile;
FileOutputStream tcpLogFile = new FileOutputStream
C:\\temp\\tcpLogFile; FileOutputStream dgcLogFile = new
FileOutputStream C:\\temp\\dgcLogFile;
FileOutputStream loaderLogFile = new FileOutputStream
C:\\temp\\loaderLogFile; LogStream.logtransport.setOutputStreamtransportLogFile;
LogStream.logproxy.setOutputStreamproxyLogFile; LogStream.logtcp.setOutputStreamtcpLogFile;
LogStream.logdgc.setOutputStreamdgcLogFile; LogStream.logloader.setOutputStreamloaderLogFile;
} catch Exception e {
System.out.printlnFailed to open log files; }
}
As with the standard log, you need to define these logs before logging actually begins. Otherwise, logging information
might be sent to the wrong destination which is usually
System.err .
Note that LogStream
, and the entire specialized logging system, has been deprecated in JDK 1.3. This means that, in a future version of RMI, it may not be supported. Until then, however, it
can be very useful. Its not entirely obvious how these logs are broken down; much of the information recorded in one
log is related to information stored in another log in particular, the transport and TCP logs are very strongly correlated. This is partially because the logs themselves arent turned on or off.
Instead, they are manipulated by six system properties, each of which takes one of three settings: silent, brief, or verbose. Silent means that no information is recorded, brief means that too
little information is recorded, and verbose means that far too much information is recorded.
Note that you can actually use the values s, b, and v as abbreviations for silent, brief, and verbose. The logging facility only checks the first character. I prefer to use the whole word
because doing so makes the code easier to read.
The six system properties are the following: sun.rmi.server.dgcLevel
When turned on, this stores information about distributed garbage collection. In particular, it stores information about lease grants, lease expirations, and lease renewals to the
distributed garbage collection DGC log. sun.rmi.server.logLevel
When turned on, this logs information about outgoing calls, including connection-reuse information. This information gets recorded in the transport log.
sun.rmi.loader.logLevel When turned on, this records information about class names and codebases whenever
RMI attempts to load a class as a result of unmarshalling an argument or return value. The information gets recorded in the loader log.
sun.rmi.transport.logLevel This logs information about communications at an abstract level, in terms of streams and
connections. The information is recorded in the transport log. sun.rmi.transport.tcp.logLevel
This records detailed information about the TCPIP protocol e.g., the ports being used. This information is record in the TCP log.
sun.rmi.transport.proxy.logLevel This records information about the creation of
Sockets and
ServerSockets . It sends
some of the information to the proxy log and some to the transport log.
java. Versus sun.
You may wonder why one of the logging parameters begins with java.
and the others all begin with sun.
. The answer is simple: java.rmi.server.logCalls
is part of the Java platform. Any
implementation of Java, including the clean room implementations from other vendors, must implement this property.
The properties that begin with sun.
, however, are not part of the Java platform. Theyre specific to Sun Microsystems implementation of RMI,
and theres no guarantee that Sun will continue to support them in future releases.
You may also wonder why Sun doesnt follow a more standard naming convention for example, using
com.sun. as a prefix to Sun-specific
properties. All I can say: they dont.
When we set each of these to verbose, either by using command-line arguments or setting system properties programmatically, we get quite a bit of information about what RMI is doing.
For example, starting the bank server going with two accounts Bob and Alex, and then requesting the balance for both accounts generated 15 KB of log files, is broken down as follows:
1 KB in the standard log 3 KB in the DGC log
5 KB in the transport log 6 KB in the TCP log
Incidentally, the volume of logging information that can be generated is one of the main reasons to use command-line properties rather than hardwiring logging in your code. You often want
logging information when tracking down a subtle problem; you rarely want it for a production server. Turning logs on and off via the command line is often the easiest way to achieve this. On
the other hand, setting the
LogStream is something that must be done in code.
Heres a little bit of the TCP log: Sun Oct 29 20:49:14 PST 2000:tcp:main:TCPEndpoint.clinit:
localHostKnown = false, localHost = 127.0.0.1
Sun Oct 29 20:49:14 PST 2000:tcp:main:TCPTransport.init: Ver sion = 2, ep = [127.0.
0.1:0] Sun Oct 29 20:49:14 PST 2000:tcp:main:TCPEndpoint.getLocalEndpoint:
created local endpoint for socket factory null on port 0
Sun Oct 29 20:49:14 PST 2000:tcp:main:TCPTransport0.listen: create socket, port = 0
Sun Oct 29 20:49:14 PST 2000:tcp:main:TCPEndpoint.newServerSocket: creating server
socket on [127.0.0.1:0] Sun Oct 29 20:49:14 PST 2000:tcp:main:TCPEndpoint.setDefaultPort:
default port for server socket factory null and client socket factory null set to 1946
The entries themselves arent defined in any of the RMI specifications but arent terribly hard to decipher either. For example, these entries deal with RMI setting up a default server socket so
that new connections can be established. Port 1946 was used, and RMI began listening on it soon after.
It can be interesting to try and correlate the method calls in the standard log with the more detailed information presented in the specialized logs. For example, the standard log had this to
say about garbage collection:
Sun Oct 29 20:49:15 PST 2000:RMI:RMI TCP Connection2 - 127.0.0.1:[127.0.0.1: sun.rmi.
transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease dirtyjava.rmi.server.ObjID[], long,
java.rmi.dgc.Lease] Sun Oct 29 20:49:15 PST 2000:RMI:RMI TCP Connection2 -
127.0.0.1:[127.0.0.1: sun.rmi. transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease
dirtyjava.rmi.server.ObjID[], long, java.rmi.dgc.Lease]
Sun Oct 29 20:49:23 PST 2000:RMI:RMI TCP Connection3 - 127.0.0.1:[127.0.0.1: sun.rmi.
transport.DGCImpl[0:0:0, 2]: java.rmi. dgc.Lease dirtyjava.rmi.server.ObjID[], long,
java.rmi.dgc.Lease] Sun Oct 29 20:49:27 PST 2000:RMI:RMI TCP Connection3 -
127.0.0.1:[127.0.0.1: sun.rmi. transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease
dirtyjava.rmi.server.ObjID[], long, java.rmi.dgc.Lease]
Sun Oct 29 20:54:15 PST 2000:RMI:RMI TCP Connection4 - 127.0.0.1:[127.0.0.1: sun.rmi.
transport.DGCImpl[0:0:0, 2]: java.rmi.dgc.Lease dirtyjava.rmi.server.ObjID[], long,
java.rmi.dgc.Lease]
The DGC log, on the other hand, provides a much more detailed view of what occurs. The first two lines of the standard log, in which the registry established leases on the two servers that were
launched, correspond to the following DGC log entries: Sun Oct 29 20:49:14 PST 2000:dgc:main:WeakRef.pin:strongRef =
sun.rmi.transport. DGCImpl42719c
Sun Oct 29 20:49:14 PST 2000:dgc:main:ObjectTable.putTarget: add object [0:0:0, 2]
Sun Oct 29 20:49:14 PST 2000:dgc:main:ObjectTable.putTarget: add object [0]
Sun Oct 29 20:49:15 PST 2000:dgc:main:WeakRef.pin: strongRef = com.ora.rmibook.
chapter9.Account_Impl[RemoteStub [ref: [endpoint:[127.0.0.1:1946]local,objID:[0]]]]
Sun Oct 29 20:49:15 PST 2000:dgc:RMI TCP Connection2 - 127.0.0.1:DGCImpl.dirty: vmid
= 11d1def534ea1be0:5ffb18:e2843f60ce: -7fff Sun Oct 29 20:49:15 PST 2000:dgc:RMI TCP Connection2-
127.0.0.1:DGCImpl.dirty: id = [0], vmid = 11d1def534ea1be0:5ffb18:e2843f60ce: -7fff, duration = 600000
Sun Oct 29 20:49:15 PST 2000:dgc:RMI TCP Connection2 - 127.0.0.1:Target.referenced:
add to dirty set: 11d1def534ea1be0:5ff b18:e2843f60ce:-7fff Sun Oct 29 20:49:15 PST 2000:dgc:main:ObjectTable.putTarget: add object
[1] Sun Oct 29 20:49:15 PST 2000:dgc:main:WeakRef.pin: strongRef =
com.ora.rmibook. chapter9.Account_Impl[RemoteStub [ref:
[endpoint:[127.0.0.1:1946]local,objID: [1]]]] Sun Oct 29 20:49:15 PST 2000:dgc:RMI TCP Connection2 -
127.0.0.1:DGCImpl.dirty: vmid
= 11d1def534ea1be0:5ffb18:e2843f60ce: -7fff Sun Oct 29 20:49:15 PST 2000:dgc:RMI TCP Connection2 -
127.0.0.1:DGCImpl.dirty: id = [1], vmid = 11d1def534ea1be0:5ffb1 8:e2843f60ce:-7fff, duration = 600000
Sun Oct 29 20:49:15 PST 2000:dgc:RMI TCP Connection2 - 127.0.0.1:Target.referenced:
add to dirty set: 11d1def534ea1be0:5ffb18:e2843f60ce: -7fff
This may not seem terribly useful. In fact, most log entries arent useful at all until theyre suddenly needed to diagnose some weird problem. The main thing to take away from this
example is that the log files are correlated and can be compared. If you watch the method calls, you see on which connection they came in, in this case
Connection 2 . Then you can
correlate entries in the more detailed logs. For example, the DGC log will tell you which JVM is associated with a particular connection via the
VMID argument.
Going further, heres what the transport log has to say about that same connection: Sun Oct 29 20:49:15 PST 2000:transport:RMI TCP Connection2 -127.0.0.1:
StreamRemoteCall.getInputStream Sun Oct 29 20:49:15 PST 2000:transport:RMI TCP Connection2 -
127.0.0.1:Transport. serviceCall: call dispatcher
Sun Oct 29 20:49:15 PST 2000:transport:RMI TCP Connection2 -127.0.0.1: StreamRemoteCall.getOutputStream
Sun Oct 29 20:49:15 PST 2000:transport:main:StreamRemoteCall.getInputStream
Sun Oct 29 20:49:15 PST 2000:transport:main:UnicastRef.done: free connection reuse =
true
And heres what the TCP log records: Sun Oct 29 20:49:15 PST 2000:tcp:RMI TCP Connection2 -
127.0.0.1:TCPTransport.run: accepted socket from [127.0.0.1:1948]
Sun Oct 29 20:49:15 PST 2000:tcp:RMI TCP Connection2 - 127.0.0.1:TCPTransport1946
.run: suggesting 127.0.0.1:1948 Sun Oct 29 20:49:15 PST 2000:tcp:RMI TCP Connection2 -
127.0.0.1:TCPTransport1946 .run: client using 127.0.0.1:0
Sun Oct 29 20:49:15 PST 2000:tcp:RMI TCP Connection2 - 127.0.0.1:TCPTransport1946
.handleMessages: op = 80 Sun Oct 29 20:49:15 PST 2000:tcp:main:TCPChannel.free: reuse connection
Sun Oct 29 20:49:15 PST 2000:tcp:main:TCPChannel.free: create reaper Sun Oct 29 20:49:15 PST 2000:tcp:main:TCPChannel.newConnection: reuse
connection Sun Oct 29 20:49:15 PST 2000:tcp:RMI TCP Conne ction2-
127.0.0.1:TCPTransport1946 .handleMessages: op = 82
Sun Oct 29 20:49:15 PST 2000:tcp:RMI TCP Connection2 - 127.0.0.1:TCPTransport1946
.handleMessages: op = 80 Sun Oct 29 20:49:15 PST 2000:tcp:main:TCPChannel.free: reuse connection
The net effect is that, if youre diligent enough, you can deduce an enormous amount of information about your systems runtime behavior from the RMI logs.
16.3.3 The Debugging Log