You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sirona.apache.org by rm...@apache.org on 2013/10/23 15:12:34 UTC
svn commit: r1535018 - in /incubator/sirona/trunk:
aop/src/main/java/org/apache/sirona/aop/
aop/src/test/java/org/apache/sirona/aop/
aspectj/src/main/java/org/apache/sirona/aspectj/
cdi/src/main/java/org/apache/sirona/cdi/ core/src/main/java/org/apache...
Author: rmannibucau
Date: Wed Oct 23 13:12:33 2013
New Revision: 1535018
URL: http://svn.apache.org/r1535018
Log:
making AbstractPerformanceInterceptor configurable to skip too small invocations
Added:
incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/DynamicInterceptionTest.java
Modified:
incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/AbstractPerformanceInterceptor.java
incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/MonitoringProxyFactory.java
incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/MonitoringProxyFactoryTest.java
incubator/sirona/trunk/aspectj/src/main/java/org/apache/sirona/aspectj/CommonsMonitoringAspect.java
incubator/sirona/trunk/cdi/src/main/java/org/apache/sirona/cdi/CommonsMonitoringInterceptor.java
incubator/sirona/trunk/core/src/main/java/org/apache/sirona/Role.java
incubator/sirona/trunk/core/src/main/java/org/apache/sirona/counters/Unit.java
incubator/sirona/trunk/spring/src/main/java/org/apache/sirona/spring/AopaliancePerformanceInterceptor.java
incubator/sirona/trunk/src/site/markdown/instrumentation.md
Modified: incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/AbstractPerformanceInterceptor.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/AbstractPerformanceInterceptor.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/AbstractPerformanceInterceptor.java (original)
+++ incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/AbstractPerformanceInterceptor.java Wed Oct 23 13:12:33 2013
@@ -17,14 +17,22 @@
package org.apache.sirona.aop;
+import org.apache.sirona.MonitoringException;
import org.apache.sirona.Role;
+import org.apache.sirona.configuration.Configuration;
import org.apache.sirona.counters.Counter;
import org.apache.sirona.repositories.Repository;
import org.apache.sirona.stopwatches.StopWatch;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
+import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.Locale;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A method interceptor that compute method invocation performances.
@@ -34,7 +42,27 @@ import java.lang.reflect.Method;
*
* @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
*/
-public abstract class AbstractPerformanceInterceptor<T> {
+public abstract class AbstractPerformanceInterceptor<T> implements Serializable {
+ // static for performances reasons, all these values are read through getXXX so it is overridable
+ private static final boolean ADAPTIVE = Configuration.is(Configuration.CONFIG_PROPERTY_PREFIX + "performance.adaptive", false);
+ private static final long FORCED_ITERATION = Configuration.getInteger(Configuration.CONFIG_PROPERTY_PREFIX + "performance.forced-iteration", 0);
+ private static final long THRESHOLD = duration(Configuration.getProperty(Configuration.CONFIG_PROPERTY_PREFIX + "performance.threshold", null));
+ private static final ActivationContext ALWAYS_ACTIVE_CONTEXT = new ActivationContext(true, 0, 0);
+
+ protected static final ConcurrentMap<Object, ActivationContext> CONTEXTS = new ConcurrentHashMap<Object, ActivationContext>();
+
+ private static long duration(final String duration) {
+ if (duration == null) {
+ return 0;
+ }
+ final String[] parts = duration.split(" ");
+ if (parts.length == 1) {
+ return Long.parseLong(duration.trim());
+ } else if (parts.length == 2) {
+ return TimeUnit.valueOf(parts[2].trim().toUpperCase(Locale.ENGLISH)).toNanos(Long.parseLong(parts[0].trim()));
+ }
+ return 0;
+ }
protected MonitorNameExtractor monitorNameExtractor;
@@ -51,8 +79,16 @@ public abstract class AbstractPerformanc
return proceed(invocation);
}
- final Counter monitor = Repository.INSTANCE.getCounter(new Counter.Key(getRole(), name));
- final StopWatch stopwatch = Repository.INSTANCE.start(monitor);
+ final ActivationContext context = doFindContext(invocation);
+
+ final StopWatch stopwatch;
+ if (context.isActive() || context.isForcedIteration()) {
+ final Counter monitor = Repository.INSTANCE.getCounter(new Counter.Key(getRole(), name));
+ stopwatch = Repository.INSTANCE.start(monitor);
+ } else {
+ stopwatch = null;
+ }
+
Throwable error = null;
try {
return proceed(invocation);
@@ -60,13 +96,65 @@ public abstract class AbstractPerformanc
error = t;
throw t;
} finally {
- stopwatch.stop();
- if (error != null) {
- final ByteArrayOutputStream writer = new ByteArrayOutputStream();
- error.printStackTrace(new PrintStream(writer));
- Repository.INSTANCE.getCounter(new Counter.Key(Role.FAILURES, writer.toString())).add(stopwatch.getElapsedTime());
+ if (stopwatch != null) {
+ stopwatch.stop();
+
+ final long elapsedTime = stopwatch.getElapsedTime();
+
+ if (error != null) {
+ final ByteArrayOutputStream writer = new ByteArrayOutputStream();
+ error.printStackTrace(new PrintStream(writer));
+ Repository.INSTANCE.getCounter(new Counter.Key(Role.FAILURES, writer.toString())).add(elapsedTime);
+ }
+
+ if (context.isThresholdActive() && elapsedTime < context.getThreshold()) {
+ context.reset();
+ }
+ }
+ }
+ }
+
+ protected boolean isAdaptive() {
+ return ADAPTIVE;
+ }
+
+ protected Object extractContextKey(final T invocation) {
+ return null;
+ }
+
+ protected ActivationContext getOrCreateContext(final Object m) {
+ final ActivationContext c = CONTEXTS.get(m);
+ if (c == null) {
+ final String counterName;
+ if (SerializableMethod.class.isInstance(m)) {
+ counterName = getCounterName(null, SerializableMethod.class.cast(m).method());
+ } else {
+ counterName = m.toString();
}
+ return putAndGetActivationContext(m, new ActivationContext(true, counterName));
}
+ return c;
+ }
+
+ protected ActivationContext putAndGetActivationContext(Object m, ActivationContext newCtx) {
+ final ActivationContext old = CONTEXTS.putIfAbsent(m, newCtx);
+ if (old != null) {
+ newCtx = old;
+ }
+ return newCtx;
+ }
+
+ protected ActivationContext doFindContext(final T invocation) {
+ if (!isAdaptive()) {
+ return ALWAYS_ACTIVE_CONTEXT;
+ }
+
+ final Object m = extractContextKey(invocation);
+ if (m != null) {
+ return getOrCreateContext(m);
+ }
+
+ return ALWAYS_ACTIVE_CONTEXT;
}
protected Role getRole() {
@@ -90,4 +178,94 @@ public abstract class AbstractPerformanc
public void setMonitorNameExtractor(final MonitorNameExtractor monitorNameExtractor) {
this.monitorNameExtractor = monitorNameExtractor;
}
+
+ protected static class SerializableMethod implements Serializable {
+ protected final String clazz;
+ protected final String method;
+ protected Method realMethod;
+
+ public SerializableMethod(final String clazz, final String method, final Method reflectMethod) {
+ this.clazz = clazz;
+ this.method = method;
+ this.realMethod = reflectMethod;
+ }
+
+ public SerializableMethod(final Method m) {
+ this(m.getDeclaringClass().getName(), m.getName(), m);
+ }
+
+ public Method method() {
+ if (realMethod == null) { // try to find it
+ try {
+ Class<?> declaring = Thread.currentThread().getContextClassLoader().loadClass(clazz);
+ while (declaring != null) {
+ for (final Method m : declaring.getDeclaredMethods()) {
+ if (m.getName().equals(method)) {
+ realMethod = m;
+ return realMethod;
+ }
+ }
+ declaring = declaring.getSuperclass();
+ }
+ } catch (final ClassNotFoundException e) {
+ throw new MonitoringException(e);
+ }
+ }
+ return realMethod;
+ }
+ }
+
+ protected static class ActivationContext implements Serializable {
+ protected final long forceIteration;
+ protected final long threshold;
+ protected final boolean thresholdActive;
+
+ protected volatile boolean active = true;
+ protected volatile AtomicInteger iteration = new AtomicInteger(0);
+
+ public ActivationContext(final boolean active, final long th, final long it) {
+ this.active = active;
+
+ if (it >= 0) {
+ forceIteration = it;
+ } else {
+ forceIteration = FORCED_ITERATION;
+ }
+
+ if (th >= 0) {
+ threshold = th;
+ } else {
+ threshold = THRESHOLD;
+ }
+
+ this.thresholdActive = this.threshold > 0;
+ }
+
+ public ActivationContext(final boolean active, final String name) {
+ this(active,
+ duration(Configuration.getProperty(Configuration.CONFIG_PROPERTY_PREFIX + "performance." + name + ".threshold", null)),
+ Configuration.getInteger(Configuration.CONFIG_PROPERTY_PREFIX + "performance." + name + ".forced-iteration", -1));
+ }
+
+ public boolean isForcedIteration() {
+ return iteration.incrementAndGet() > forceIteration;
+ }
+
+ protected long getThreshold() {
+ return threshold;
+ }
+
+ protected boolean isThresholdActive() {
+ return thresholdActive;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void reset() {
+ active = false;
+ iteration.set(0);
+ }
+ }
}
\ No newline at end of file
Modified: incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/MonitoringProxyFactory.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/MonitoringProxyFactory.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/MonitoringProxyFactory.java (original)
+++ incubator/sirona/trunk/aop/src/main/java/org/apache/sirona/aop/MonitoringProxyFactory.java Wed Oct 23 13:12:33 2013
@@ -60,6 +60,11 @@ public final class MonitoringProxyFactor
protected String getCounterName(final Invocation invocation) {
return getCounterName(invocation.target, invocation.method);
}
+
+ @Override
+ protected Object extractContextKey(final Invocation invocation) {
+ return new SerializableMethod(invocation.method);
+ }
}
private static class Invocation {
Added: incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/DynamicInterceptionTest.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/DynamicInterceptionTest.java?rev=1535018&view=auto
==============================================================================
--- incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/DynamicInterceptionTest.java (added)
+++ incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/DynamicInterceptionTest.java Wed Oct 23 13:12:33 2013
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sirona.aop;
+
+import org.apache.sirona.repositories.Repository;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class DynamicInterceptionTest {
+
+ public static final int MAX_IT = 5;
+ public static final int THRESHOLD = 100;
+ private Repository repository;
+
+ @Before
+ @After
+ public void reset() {
+ repository = Repository.INSTANCE;
+ repository.clear();
+ }
+
+ @Test
+ public void playWithInterceptor() throws Throwable {
+ final ConfigurablePerfInterceptor interceptor = new ConfigurablePerfInterceptor();
+ assertFalse(repository.iterator().hasNext());
+
+ interceptor.invoke(THRESHOLD * 5);
+ assertTrue(repository.iterator().hasNext());
+ assertEquals(1, repository.iterator().next().getHits());
+ assertEquals("dynamic", repository.iterator().next().getKey().getName());
+
+ interceptor.invoke(THRESHOLD / 20);
+ assertEquals(2, repository.iterator().next().getHits());
+
+ for (int i = 0; i < MAX_IT; i++) {
+ interceptor.invoke(THRESHOLD * 5);
+ }
+ assertEquals(2, repository.iterator().next().getHits());
+
+ interceptor.invoke(THRESHOLD / 5);
+ assertEquals(3, repository.iterator().next().getHits());
+
+ for (int i = 0; i < MAX_IT; i++) {
+ interceptor.invoke(THRESHOLD * 2);
+ }
+ assertEquals(3, repository.iterator().next().getHits());
+
+ interceptor.invoke(THRESHOLD * 5);
+ interceptor.invoke(THRESHOLD * 5);
+ assertEquals(5, repository.iterator().next().getHits());
+ }
+
+ public static class ConfigurablePerfInterceptor extends AbstractPerformanceInterceptor<Long> {
+ public void invoke(final long duration) throws Throwable {
+ doInvoke(duration);
+ }
+
+ @Override
+ protected Object proceed(final Long duration) throws Throwable {
+ Thread.sleep(duration);
+ return null;
+ }
+
+ @Override
+ protected String getCounterName(final Long invocation) {
+ return "dynamic";
+ }
+
+ @Override
+ protected Method extractContextKey(final Long invocation) {
+ try {
+ return getClass().getMethod("invoke", long.class);
+ } catch (final NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ protected boolean isAdaptive() {
+ return true;
+ }
+
+ @Override
+ protected ActivationContext doFindContext(final Long invocation) {
+ return putAndGetActivationContext(extractContextKey(null), new ActivationContext(true, TimeUnit.MILLISECONDS.toNanos(THRESHOLD), MAX_IT));
+ }
+ }
+}
Modified: incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/MonitoringProxyFactoryTest.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/MonitoringProxyFactoryTest.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/MonitoringProxyFactoryTest.java (original)
+++ incubator/sirona/trunk/aop/src/test/java/org/apache/sirona/aop/MonitoringProxyFactoryTest.java Wed Oct 23 13:12:33 2013
@@ -19,6 +19,8 @@ package org.apache.sirona.aop;
import org.apache.sirona.Role;
import org.apache.sirona.counters.Counter;
import org.apache.sirona.repositories.Repository;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
@@ -28,6 +30,12 @@ import static org.junit.Assert.assertNot
import static org.junit.Assert.fail;
public class MonitoringProxyFactoryTest {
+ @Before
+ @After
+ public void reset() {
+ Repository.INSTANCE.clear();
+ }
+
@Test
public void test() {
final Foo foo = MonitoringProxyFactory.monitor(Foo.class, new FooImpl());
Modified: incubator/sirona/trunk/aspectj/src/main/java/org/apache/sirona/aspectj/CommonsMonitoringAspect.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/aspectj/src/main/java/org/apache/sirona/aspectj/CommonsMonitoringAspect.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/aspectj/src/main/java/org/apache/sirona/aspectj/CommonsMonitoringAspect.java (original)
+++ incubator/sirona/trunk/aspectj/src/main/java/org/apache/sirona/aspectj/CommonsMonitoringAspect.java Wed Oct 23 13:12:33 2013
@@ -49,6 +49,11 @@ public abstract class CommonsMonitoringA
return invocation.getSignature().toLongString();
}
+ @Override
+ protected Object extractContextKey(final ProceedingJoinPoint invocation) {
+ return new SerializableMethod(findMethod(invocation.getSignature()));
+ }
+
private static Method findMethod(final Signature signature) {
if ("org.aspectj.runtime.reflect.MethodSignatureImpl".equals(signature.getClass().getName())) {
try {
Modified: incubator/sirona/trunk/cdi/src/main/java/org/apache/sirona/cdi/CommonsMonitoringInterceptor.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/cdi/src/main/java/org/apache/sirona/cdi/CommonsMonitoringInterceptor.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/cdi/src/main/java/org/apache/sirona/cdi/CommonsMonitoringInterceptor.java (original)
+++ incubator/sirona/trunk/cdi/src/main/java/org/apache/sirona/cdi/CommonsMonitoringInterceptor.java Wed Oct 23 13:12:33 2013
@@ -22,6 +22,7 @@ import javax.interceptor.AroundInvoke;
import javax.interceptor.AroundTimeout;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
+import java.lang.reflect.Method;
@Interceptor
@Monitored
@@ -41,4 +42,9 @@ public class CommonsMonitoringIntercepto
protected String getCounterName(final InvocationContext invocation) {
return getCounterName(invocation.getTarget(), invocation.getMethod());
}
+
+ @Override
+ protected Object extractContextKey(final InvocationContext invocation) {
+ return new SerializableMethod(invocation.getMethod());
+ }
}
Modified: incubator/sirona/trunk/core/src/main/java/org/apache/sirona/Role.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/core/src/main/java/org/apache/sirona/Role.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/core/src/main/java/org/apache/sirona/Role.java (original)
+++ incubator/sirona/trunk/core/src/main/java/org/apache/sirona/Role.java Wed Oct 23 13:12:33 2013
@@ -19,6 +19,8 @@ package org.apache.sirona;
import org.apache.sirona.counters.Unit;
+import java.io.Serializable;
+
import static org.apache.sirona.counters.Unit.Time.NANOSECOND;
/**
@@ -27,7 +29,7 @@ import static org.apache.sirona.counters
*
* @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
*/
-public class Role implements Comparable<Role> {
+public class Role implements Comparable<Role>, Serializable {
public static final Role WEB = new Role("web", NANOSECOND);
public static final Role JSP = new Role("jsp", NANOSECOND);
public static final Role JDBC = new Role("jdbc", NANOSECOND);
Modified: incubator/sirona/trunk/core/src/main/java/org/apache/sirona/counters/Unit.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/core/src/main/java/org/apache/sirona/counters/Unit.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/core/src/main/java/org/apache/sirona/counters/Unit.java (original)
+++ incubator/sirona/trunk/core/src/main/java/org/apache/sirona/counters/Unit.java Wed Oct 23 13:12:33 2013
@@ -17,6 +17,7 @@
package org.apache.sirona.counters;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -38,7 +39,7 @@ import java.util.concurrent.CopyOnWriteA
*
* @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
*/
-public class Unit implements Comparable<Unit> {
+public class Unit implements Comparable<Unit>, Serializable {
private static final Map<String, Unit> UNITS = new ConcurrentHashMap<String, Unit>();
/**
Modified: incubator/sirona/trunk/spring/src/main/java/org/apache/sirona/spring/AopaliancePerformanceInterceptor.java
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/spring/src/main/java/org/apache/sirona/spring/AopaliancePerformanceInterceptor.java?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/spring/src/main/java/org/apache/sirona/spring/AopaliancePerformanceInterceptor.java (original)
+++ incubator/sirona/trunk/spring/src/main/java/org/apache/sirona/spring/AopaliancePerformanceInterceptor.java Wed Oct 23 13:12:33 2013
@@ -21,6 +21,8 @@ import org.aopalliance.intercept.MethodI
import org.aopalliance.intercept.MethodInvocation;
import org.apache.sirona.aop.AbstractPerformanceInterceptor;
+import java.lang.reflect.Method;
+
/**
* Spring-aop implementation of PerformanceInterceptor.
*
@@ -42,4 +44,8 @@ public class AopaliancePerformanceInterc
return invocation.proceed();
}
+ @Override
+ protected Object extractContextKey(final MethodInvocation invocation) {
+ return new SerializableMethod(invocation.getMethod());
+ }
}
\ No newline at end of file
Modified: incubator/sirona/trunk/src/site/markdown/instrumentation.md
URL: http://svn.apache.org/viewvc/incubator/sirona/trunk/src/site/markdown/instrumentation.md?rev=1535018&r1=1535017&r2=1535018&view=diff
==============================================================================
--- incubator/sirona/trunk/src/site/markdown/instrumentation.md (original)
+++ incubator/sirona/trunk/src/site/markdown/instrumentation.md Wed Oct 23 13:12:33 2013
@@ -27,7 +27,9 @@ to use javassist you set it to `org.apac
Then the API is quite simple:
- final MyClient client = MonitoringProxyFactory.monitor(MyClient.class, getMyClientInstance());
+```java
+final MyClient client = MonitoringProxyFactory.monitor(MyClient.class, getMyClientInstance());
+```
# CDI
@@ -91,3 +93,38 @@ concrete aspect defining the pointcut to
</aspectj>
See [AspectJ documentation](http://eclipse.org/aspectj/doc/next/progguide/language-joinPoints.html) for more information.
+
+# Note on interceptor configuration (experimental)
+
+Few global configuration (`sirona.properties`) is available for all interceptors:
+
+* `org.apache.sirona.performance.adaptive`: if this boolean is set to true the following parameter are taken into account
+* `org.apache.sirona.performance.threshold`: if > 0 it is the duration under which calls are skipped (no more monitored). Note: the format supports <duration> <TimeUnit name> too. For instance `100 MILLISECONDS` is valid.
+* `org.apache.sirona.performance.forced-iteration`: the number of iterations a deactivated interceptor (because of threshold rule) will wait before forcing a measure to see if the monitoring should be activated back.
+
+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.