Tuscany
Monitoring
Overview
Monitoring or as it's more commonly called logging
is a means of providing runtime diagnostic
statements in the code. In Tuscany the means to do
this has changed from the conventional factory
pattern to an IOC type pattern of having the container provide or also
known as inject the monitor (logger) class. The following
sections will first describe how a component goes about obtaining the
monitor class, how the framework is extended, and how it's
initialized.
Monitoring
The
first step is to provide an interface that you want to use
to call monitor statements (logging). Here is an
example:
import org.apache.tuscany.common.monitor.LogLevel;
public interface MyLoggingInterface{
@LogLevel("INFO") public void methodCalled(String methodName, Object[] args);
@LogLevel("SEVERE") public void methodReturned(String methodName, Object args);
}
|
Was
there something special about the name of this class ? No.
Did
it need to implement another interface ? No that's why it didn't :-)
Was
the names of the methods special? No, just personal preference.
Was
the signature of the methods special? No could have been anything.
The
interface may have to have a public accessor if its to
be used in other packages and as well as the methods. It can
even be a static interface that in another class (inner interface?).
The @LogLevel("SEVERE")
is Java annotation that sets level the logging
should be at for this method to actually write log records; otherwise, the method produces no logging.
Next
is to obtain an instance of this interface to do the actual monitoring.
This follows the same pattern as how a
reference to another SCA component is obtained when implementing an SCA
component; specifically, create a field and a setter method for the
field, but instead of using the @Reference annotation you use the
@Monitor annotation. Here is an example:
... import
org.apache.tuscany.core.system.annotation.Monitor;
//this
needs to be included for the annotation. ... public
class foobar { @Monitor
protected MyLoggingInterface myLogger; //Instance is inserted by
container
@Monitor
public void setMyLoggingClass(MyLoggingInterface myLoggingClass) {
this.myLogger = myLoggingClass;
} ... }
|
There is no need to supply in the SCDL wiring for this reference.
To
actually log, simply use the methods of the interface. As an example
... public
String getGreetings(String name) {
myLogger.methodCalled(
"getGreetings", new
Object[]{name}); //log
this "event"
String ret="Hello " + name;
myLogger.methodReturned("getGreetings",
ret); // log this event
return ret; } ... |
That
pretty much wraps up how you actually add monitoring (logging)
to your code.
Extending Monitoring
To
extend or define your own monitor you need to simply create a
class that implements one interface: org.apache.tuscany.common.monitor.MonitorFactory
that has only one method getMonitor
. Couldn't be
simpler. Here is the interface:
public
interface MonitorFactory { /**
* Return a monitor for a component's monitor interface.
* * @param
monitorInterface the component's monitoring interface
* @return an implementation of the monitoring interface; will not be
null */
<T> T getMonitor(Class<T> monitorInterface); } |
Oh,
forgot to mention it uses Java 1.5 generics. For those not
familar with this.. simply said for the getMonitor
method given any interface (or class) as a
parameter return an instance of that interface.
Side line: <T>
notifies you that T can be any Object. So the T before
getMonitor means return an instance of type object T given
the parameter type Class<T>. Class<T>
is an interface (or class) that is of type T .
|
There
are two implementations currently in Tuscany that demonstrate
how to extend the Monitoring infrastructure:
org.apache.tuscany.common.monitor.impl.NullMonitorFactory | Simply
throws the logging messages away. |
org.apache.tuscany.common.monitor.impl.JavaLoggingMonitorFactory | Uses
Java logging infrastructure to back the Monitoring
implementation. |
Looking at the simple one
first NullMonitorFactory
all the
getMonitor
method returns is a Java proxy that implements the interface
passed in.
public
class NullMonitorFactory implements MonitorFactory { public
<T> T getMonitor(Class<T> monitorInterface)
{
return
monitorInterface.cast(Proxy.newProxyInstance(monitorInterface.getClassLoader(),
new Class<?>[]{monitorInterface}, NULL_MONITOR));
} private static final
InvocationHandler NULL_MONITOR = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
return null;
} }; } |
The
monitorInterface.cast(...)
is just a cast of
the proxy to the interface passed in and what is expected to be
returned.
The first argument is to use the same
classloader
that loaded the interface.
The second argument new
Class<?>[]{monitorInterface)
is just an array
of any
Classes or Interfaces (type as any is signified by the
<?>) that contains one interface
.. monitorInterface that needs to
be implemented by the handler.
The last argument
NULL_MONITOR is a Java Invocation handler for a proxy.
Regardless of the method called it just does a return null
(i.e nothing).
Looking at
JavaLoggingMonitorFactory you can see it's a wrapper for the Java logging utility.
In principle it's not much more than
the NullMonitorFactory with the following enhancements:
The proxies field simply provides
a cache for instances of the interfaces. There doesn't need to
be a unique instance for each interface since there is no state differences kept between
an two of the same interfaces.
The logging levels on the
methods on any interface can be initialized with a Properties object.
that has keys with the format <className>#<methodName>
and value of the level (eq
MyLoggingInterface#
methodReturned=
SEVERE)
If the Properties
does not have a level for the method the interface is introspected for
the LogLevel annotation to determine its logging level.
The Invocation handler uses Java
logging to log a record. The actual text message is
gotten from a resource bundle passed into
the JavaLoggingMonitorFactory class when it was
created. The key in that resource bundle is the same as the
for the properties file that defines the level and the value is the
actual message.
Monitor Initialization
The only aspect that is directly
Tuscany specific with regard to initializing monitoring is
providing the org.apache.tuscany.core.client.TuscanyRuntime a
monitoring factory when the runtime is created. By default it
will use the previously discussed NullMonitorFactory. Of course
each specific monitor factory implementation may have it's own
initialization requirements. The default, NullMonitorFactory
requires no additional initialization.
The JavaLoggingMonitorFactory monitor requires the
previously discussed Java Properties to initialization the logging
levels of the interface methods, a default Java logging
level that is used if none is found in the Properties, or through
introspection of the Interface LogLevel annotations, and the name
of the resource bundle to be used.