Manually (clients)

To handle manually the interception you need to import sirona-aop. Then you can rely on org.apache.sirona.aop.SironaProxyFactory.

org.apache.commons.proxy.ProxyFactory key defines the proxy factory to use to create proxies For instance to use javassist you set it to org.apache.commons.proxy.factory.javassist.JavassistProxyFactory (and you’ll include javassist in your application).

Then the API is quite simple:

final MyClient client = SironaProxyFactory.monitor(MyClient.class, getMyClientInstance());

CDI

You just need to decorate your CDI bean/method with the interceptor binding org.apache.sirona.cdi.Monitored.

For instance:

@Monitored
@ApplicationScoped
public class MyMonitoredBean {
    public void myMethod() {
        // ...
    }
}

Note: in some (old) CDI implementation you’ll need to activate the monitoring interceptor: org.apache.sirona.cdi.SironaInterceptor.

You can configure it (without adding the @Monitored annotation) using org.apache.sirona.cdi.performance key. The value is a list of predicate (regex:<regex>, prefix:<prefix>, suffix:<suffix>).

For instance:

org.apache.sirona.cdi.performance = prefix:org.superbiz.MyService,regex:.*Bean

Spring

Using org.apache.sirona.spring.BeanNameMonitoringAutoProxyCreator you can automatically add monitoring to selected beans.

<bean class="org.apache.sirona.spring.BeanNameMonitoringAutoProxyCreator">
  <property name="beanNames">
    <list>
      <value>*Service</value>
    </list>
  </property>
</bean>

An alternative is to use org.apache.sirona.spring.PointcutMonitoringAutoProxyCreator which uses a org.springframework.aop.Pointcut to select beans to monitor.

AspectJ

To use AspectJ weaver (it works with build time enhancement too but it is often less relevant) just configure a custom concrete aspect defining the pointcut to monitor:

<aspectj>
  <aspects>
    <concrete-aspect name="org.apache.commons.aspectj.MyMonitoringAspectJ"
                     extends="org.apache.sirona.aspectj.SironaAspect">
      <pointcut name="pointcut" expression="execution(* org.apache.sirona.aspectj.AspectJMonitoringTest$MonitorMe.*(..))"/>
    </concrete-aspect>
  </aspects>

  <weaver>
    <include within="org.apache.sirona.aspectj.AspectJMonitoringTest$MonitorMe"/>
  </weaver>
</aspectj>

See AspectJ documentation for more information.

Note on interceptor configuration (experimental)

Few global configuration (sirona.properties) is available for all interceptors:

Note: threshold and forced-iteration parameters can be specialized appending to org.apache.sirona. the method qualified name.

Here a sample of the behavior associated with these properties. Let say you configured forced-iteration to 5 and threshold to 100 milliseconds. If xxx ms represent an invocation of xxx milliseconds and * represent a call which was measured, here is an invocation sequence:

500 ms*
5 ms*
500 ms
500 ms
500 ms
500 ms
500 ms
20 ms*
200 ms
200 ms
200 ms
200 ms
200 ms
500 ms*
500 ms*

Note: the idea is to reduce the overhead of the interception. This is pretty efficient in general but particularly with AspectJ. Note 2: if your invocations are pretty unstable this is not really usable since since you’ll not get a good threshold value.

JavaAgent

Usage

First add to your JVM the sirona javaagent:

-javaagent:/path/to/sirona-javaagent.jar

Note: ensuring sirona-core and sirona-aop are no more delivered in your binaries or the container is not mandatory but better.

Configuration

The javaagent supports “include” configuration:

-javaagent:/path/to/sirona-javaagent.jar=includes=XXX

The value of includes (XXX in previous example) is a comma separated list of predicates (prefix:org.superbix, regex:org.superbiz.*Service).

Symmetrically excludes is supported.

Adding others jars with libs=paths to a directory containing jars

What does the javaagent

It basically convert the following code:

public class Foo {
    public void run() {
    }

    public void run2() {
        System.out.println("hello");
    }

    public String out() {
        return "output";
    }

    public void npe() {
        try {
            throw new NullPointerException();
        } catch (final NullPointerException npe) {
            npe.printStackTrace();
        }
    }
}

in

package org.apache.test.sirona.javaagent;

import org.apache.sirona.aop.AbstractPerformanceInterceptor;
import org.apache.sirona.counters.Counter;
import org.apache.sirona.javaagent.AgentPerformanceInterceptor;

public class Foo {
    private static final String out_$_$IRONA_$_INTERNAL_$_KEY;
    private static final String npe_$_$IRONA_$_INTERNAL_$_KEY;
    private static final String run_$_$IRONA_$_INTERNAL_$_KEY;
    private static final String run2_$_$IRONA_$_INTERNAL_$_KEY;

    public void run() {
        AbstractPerformanceInterceptor.Context localContext = AgentPerformanceInterceptor.start(run_$_$IRONA_$_INTERNAL_$_KEY, this);
        try {
            run_$_$irona_$_internal_$_original_$_();
            localContext.stop();
        } catch (Throwable localThrowable) {
            localContext.stopWithException(localThrowable);
            throw localThrowable;
        }
    }

    private void run_$_$irona_$_internal_$_original_$_() {
    }

    public void run2() {
        AbstractPerformanceInterceptor.Context localContext = AgentPerformanceInterceptor.start(run2_$_$IRONA_$_INTERNAL_$_KEY, this);
        try {
            run2_$_$irona_$_internal_$_original_$_();
            localContext.stop();
        } catch (Throwable localThrowable) {
            localContext.stopWithException(localThrowable);
            throw localThrowable;
        }
    }

    private void run2_$_$irona_$_internal_$_original_$_() {
        System.out.println("hello");
    }

    public String out() {
        AbstractPerformanceInterceptor.Context localContext = AgentPerformanceInterceptor.start(out_$_$IRONA_$_INTERNAL_$_KEY, this);
        try {
            String str = out_$_$irona_$_internal_$_original_$_();
            localContext.stop();
            return str;
        } catch (Throwable localThrowable) {
            localContext.stopWithException(localThrowable);
            throw localThrowable;
        }
    }

    private String out_$_$irona_$_internal_$_original_$_() {
        return "output";
    }

    public void npe() {
        AbstractPerformanceInterceptor.Context localContext = AgentPerformanceInterceptor.start(npe_$_$IRONA_$_INTERNAL_$_KEY, this);
        try {
            npe_$_$irona_$_internal_$_original_$_();
            localContext.stop();
        } catch (Throwable localThrowable) {
            localContext.stopWithException(localThrowable);
            throw localThrowable;
        }
    }

    private void npe_$_$irona_$_internal_$_original_$_() {
        try {
            throw new NullPointerException();
        } catch (NullPointerException npe) {
            npe.printStackTrace();
        }
    }

    private static void _$_$irona_static_merge0() {
        out_$_$IRONA_$_INTERNAL_$_KEY = "org.apache.test.sirona.javaagent.Foo.out";
        npe_$_$IRONA_$_INTERNAL_$_KEY = "org.apache.test.sirona.javaagent.Foo.npe";
        run_$_$IRONA_$_INTERNAL_$_KEY = "org.apache.test.sirona.javaagent.Foo.run";
        run2_$_$IRONA_$_INTERNAL_$_KEY = "org.apache.test.sirona.javaagent.Foo.run2";
    }

    static {
        _$_$irona_static_merge0();
        // if other static blocks it will be added here as _$_$irona_static_merge1(), _$_$irona_static_merge2()...
    }
}

Go further with sirona javaagent

Javaagent is extensible using InvocationListener API:

public interface InvocationListener {
    void before(AgentContext context);
    void after(AgentContext context, Object result, Throwable error);
    boolean accept(String key);
}
  • accept method allows to filter by key/instance what is intercepted.
  • before and after allows to put code around method executions. AgentContext supports contextual data through put/get methods.

Note on contextual data: key is an int for performances reasons, ensure you don’t conflict with another listener. Prefer to use negative int if possible.

InvocationListeners can be sorted using @Order annotation.

org.apache.sirona.javaagent.listener.ConfigurableListener is an utility class to simplify the writing of InvocationListener classes.

They are picked using a plain old service provider interface (META-INF/services/org.apache.sirona.javaagent.spi.InvocationListener).