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.NullMonitorFactorySimply throws the logging messages away.
org.apache.tuscany.common.monitor.impl.JavaLoggingMonitorFactoryUses 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.