You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2018/02/17 14:36:27 UTC
[1/2] incubator-unomi git commit: UNOMI-161 Add metrics to analyze
internal performance
Repository: incubator-unomi
Updated Branches:
refs/heads/master 189687478 -> a1574bf53
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
----------------------------------------------------------------------
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
index d5f85cf..153e19a 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
+++ b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
@@ -25,6 +25,8 @@ import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.query.DateRange;
import org.apache.unomi.api.query.IpRange;
import org.apache.unomi.api.query.NumericRange;
+import org.apache.unomi.metrics.MetricAdapter;
+import org.apache.unomi.metrics.MetricsService;
import org.apache.unomi.persistence.elasticsearch.conditions.*;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.aggregate.*;
@@ -148,6 +150,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
private String aggregateQueryBucketSize = "5000";
+ private MetricsService metricsService;
private Map<String, Map<String, Map<String, Object>>> knownMappings = new HashMap<>();
public void setBundleContext(BundleContext bundleContext) {
@@ -243,10 +246,13 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
this.aggregateQueryBucketSize = aggregateQueryBucketSize;
}
+ public void setMetricsService(MetricsService metricsService) {
+ this.metricsService = metricsService;
+ }
public void start() throws Exception {
// on startup
- new InClassLoaderExecute<Object>() {
+ new InClassLoaderExecute<Object>(null, null) {
public Object execute(Object... args) throws Exception {
bulkProcessorConcurrentRequests = System.getProperty(BULK_PROCESSOR_CONCURRENT_REQUESTS, bulkProcessorConcurrentRequests);
@@ -452,7 +458,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
public void stop() {
- new InClassLoaderExecute<Object>() {
+ new InClassLoaderExecute<Object>(null, null) {
protected Object execute(Object... args) {
logger.info("Closing ElasticSearch persistence backend...");
if (bulkProcessor != null) {
@@ -553,7 +559,15 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public <T extends Item> PartialList<T> getAllItems(final Class<T> clazz, int offset, int size, String sortBy) {
- return query(QueryBuilders.matchAllQuery(), sortBy, clazz, offset, size, null, null);
+ long startTime = System.currentTimeMillis();
+ try {
+ return query(QueryBuilders.matchAllQuery(), sortBy, clazz, offset, size, null, null);
+ } finally {
+ if (metricsService != null && metricsService.isActivated()) {
+ metricsService.updateTimer(this.getClass().getName() + ".getAllItems", startTime);
+ }
+ }
+
}
@Override
@@ -563,17 +577,23 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public <T extends Item> T load(final String itemId, final Date dateHint, final Class<T> clazz) {
- return new InClassLoaderExecute<T>() {
+ return new InClassLoaderExecute<T>(metricsService, this.getClass().getName() + ".loadItem") {
protected T execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
if (itemsMonthlyIndexed.contains(itemType) && dateHint == null) {
- PartialList<T> r = query(QueryBuilders.idsQuery(itemType).addIds(itemId), null, clazz, 0, 1, null, null);
- if (r.size() > 0) {
- return r.get(0);
- }
- return null;
+ return new MetricAdapter<T>(metricsService, ".loadItemWithQuery") {
+ @Override
+ public T execute(Object... args) throws Exception {
+ PartialList<T> r = query(QueryBuilders.idsQuery(itemType).addIds(itemId), null, clazz, 0, 1, null, null);
+ if (r.size() > 0) {
+ putInCache(itemId, r.get(0));
+ return r.get(0);
+ }
+ return null;
+ }
+ }.execute();
} else {
String index = indexNames.containsKey(itemType) ? indexNames.get(itemType) :
(itemsMonthlyIndexed.contains(itemType) ? getMonthlyIndexName(dateHint) : indexName);
@@ -609,7 +629,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public boolean save(final Item item, final boolean useBatching) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".saveItem") {
protected Boolean execute(Object... args) throws Exception {
try {
String source = ESCustomObjectMapper.getObjectMapper().writeValueAsString(item);
@@ -650,7 +670,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public boolean update(final String itemId, final Date dateHint, final Class clazz, final Map source) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".updateItem") {
protected Boolean execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
@@ -681,7 +701,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public boolean updateWithQueryAndScript(final Date dateHint, final Class<?> clazz, final String[] scripts, final Map<String, Object>[] scriptParams, final Condition[] conditions) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".updateWithQueryAndScript") {
protected Boolean execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
@@ -736,7 +756,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public boolean updateWithScript(final String itemId, final Date dateHint, final Class<?> clazz, final String script, final Map<String, Object> scriptParams) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".updateWithScript") {
protected Boolean execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
@@ -769,10 +789,11 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public <T extends Item> boolean remove(final String itemId, final Class<T> clazz) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".removeItem") {
protected Boolean execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
+ deleteFromCache(itemId, clazz);
client.prepareDelete(getIndexNameForQuery(itemType), itemType, itemId)
.execute().actionGet();
@@ -790,7 +811,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
public <T extends Item> boolean removeByQuery(final Condition query, final Class<T> clazz) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".removeByQuery") {
protected Boolean execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
@@ -845,7 +866,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
public boolean indexTemplateExists(final String templateName) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".indexTemplateExists") {
protected Boolean execute(Object... args) {
GetIndexTemplatesResponse getIndexTemplatesResponse = client.admin().indices().prepareGetTemplates(templateName).execute().actionGet();
return getIndexTemplatesResponse.getIndexTemplates().size() == 1;
@@ -859,7 +880,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
public boolean removeIndexTemplate(final String templateName) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".removeIndexTemplate") {
protected Boolean execute(Object... args) {
DeleteIndexTemplateResponse deleteIndexTemplateResponse = client.admin().indices().deleteTemplate(new DeleteIndexTemplateRequest(templateName)).actionGet();
return deleteIndexTemplateResponse.isAcknowledged();
@@ -873,7 +894,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
public boolean createMonthlyIndexTemplate() {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".createMonthlyIndexTemplate") {
protected Boolean execute(Object... args) {
PutIndexTemplateRequest putIndexTemplateRequest = new PutIndexTemplateRequest("context-monthly-indices")
.template(indexName + "-*")
@@ -912,7 +933,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
public boolean createIndex(final String indexName) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".createItem") {
protected Boolean execute(Object... args) {
IndicesExistsResponse indicesExistsResponse = client.admin().indices().prepareExists(indexName).execute().actionGet();
boolean indexExists = indicesExistsResponse.isExists();
@@ -937,7 +958,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
public boolean removeIndex(final String indexName) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".removeIndex") {
protected Boolean execute(Object... args) {
IndicesExistsResponse indicesExistsResponse = client.admin().indices().prepareExists(indexName).execute().actionGet();
boolean indexExists = indicesExistsResponse.isExists();
@@ -1010,7 +1031,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public Map<String, Map<String, Object>> getPropertiesMapping(final String itemType) {
- return new InClassLoaderExecute<Map<String, Map<String, Object>>>() {
+ return new InClassLoaderExecute<Map<String, Map<String, Object>>>(metricsService, this.getClass().getName() + ".getPropertiesMapping") {
@SuppressWarnings("unchecked")
protected Map<String, Map<String, Object>> execute(Object... args) throws Exception {
GetMappingsResponse getMappingsResponse = client.admin().indices().prepareGetMappings().setTypes(itemType).execute().actionGet();
@@ -1085,7 +1106,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
public boolean saveQuery(final String queryName, final String query) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".saveQuery") {
protected Boolean execute(Object... args) throws Exception {
//Index the query = register it in the percolator
try {
@@ -1118,7 +1139,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public boolean removeQuery(final String queryName) {
- Boolean result = new InClassLoaderExecute<Boolean>() {
+ Boolean result = new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".removeQuery") {
protected Boolean execute(Object... args) throws Exception {
//Index the query = register it in the percolator
try {
@@ -1140,18 +1161,30 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public boolean testMatch(Condition query, Item item) {
+ long startTime = System.currentTimeMillis();
try {
return conditionEvaluatorDispatcher.eval(query, item);
} catch (UnsupportedOperationException e) {
logger.error("Eval not supported, continue with query", e);
+ } finally {
+ if (metricsService != null && metricsService.isActivated()) {
+ metricsService.updateTimer(this.getClass().getName() + ".testMatchLocally", startTime);
+ }
+ }
+ startTime = System.currentTimeMillis();
+ try {
+ final Class<? extends Item> clazz = item.getClass();
+ String itemType = Item.getItemType(clazz);
+
+ QueryBuilder builder = QueryBuilders.boolQuery()
+ .must(QueryBuilders.idsQuery(itemType).addIds(item.getItemId()))
+ .must(conditionESQueryBuilderDispatcher.buildFilter(query));
+ return queryCount(builder, itemType) > 0;
+ } finally {
+ if (metricsService != null && metricsService.isActivated()) {
+ metricsService.updateTimer(this.getClass().getName() + ".testMatchInElasticSearch", startTime);
+ }
}
- final Class<? extends Item> clazz = item.getClass();
- String itemType = Item.getItemType(clazz);
-
- QueryBuilder builder = QueryBuilders.boolQuery()
- .must(QueryBuilders.idsQuery(itemType).addIds(item.getItemId()))
- .must(conditionESQueryBuilderDispatcher.buildFilter(query));
- return queryCount(builder, itemType) > 0;
}
@Override
@@ -1213,7 +1246,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
private long queryCount(final QueryBuilder filter, final String itemType) {
- return new InClassLoaderExecute<Long>() {
+ return new InClassLoaderExecute<Long>(metricsService, this.getClass().getName() + ".queryCount") {
@Override
protected Long execute(Object... args) {
@@ -1229,7 +1262,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
}
private <T extends Item> PartialList<T> query(final QueryBuilder query, final String sortBy, final Class<T> clazz, final int offset, final int size, final String[] routing, final String scrollTimeValidity) {
- return new InClassLoaderExecute<PartialList<T>>() {
+ return new InClassLoaderExecute<PartialList<T>>(metricsService, this.getClass().getName() + ".query") {
@Override
protected PartialList<T> execute(Object... args) throws Exception {
@@ -1353,7 +1386,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public <T extends Item> PartialList<T> continueScrollQuery(final Class<T> clazz, final String scrollIdentifier, final String scrollTimeValidity) {
- return new InClassLoaderExecute<PartialList<T>>() {
+ return new InClassLoaderExecute<PartialList<T>>(metricsService, this.getClass().getName() + ".continueScrollQuery") {
@Override
protected PartialList<T> execute(Object... args) throws Exception {
@@ -1390,7 +1423,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public Map<String, Long> aggregateQuery(final Condition filter, final BaseAggregate aggregate, final String itemType) {
- return new InClassLoaderExecute<Map<String, Long>>() {
+ return new InClassLoaderExecute<Map<String, Long>>(metricsService, this.getClass().getName() + ".aggregateQuery") {
@Override
protected Map<String, Long> execute(Object... args) {
@@ -1525,7 +1558,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public void refresh() {
- new InClassLoaderExecute<Boolean>() {
+ new InClassLoaderExecute<Boolean>(metricsService, this.getClass().getName() + ".refresh") {
protected Boolean execute(Object... args) {
if (bulkProcessor != null) {
bulkProcessor.flush();
@@ -1540,7 +1573,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public void purge(final Date date) {
- new InClassLoaderExecute<Object>() {
+ new InClassLoaderExecute<Object>(metricsService, this.getClass().getName() + ".purgeWithDate") {
@Override
protected Object execute(Object... args) throws Exception {
IndicesStatsResponse statsResponse = client.admin().indices().prepareStats(indexName + "-*")
@@ -1582,7 +1615,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public void purge(final String scope) {
- new InClassLoaderExecute<Void>() {
+ new InClassLoaderExecute<Void>(metricsService, this.getClass().getName() + ".purgeWithScope") {
@Override
protected Void execute(Object... args) {
QueryBuilder query = termQuery("scope", scope);
@@ -1627,7 +1660,7 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
@Override
public Map<String, Double> getSingleValuesMetrics(final Condition condition, final String[] metrics, final String field, final String itemType) {
- return new InClassLoaderExecute<Map<String, Double>>() {
+ return new InClassLoaderExecute<Map<String, Double>>(metricsService, this.getClass().getName() + ".getSingleValuesMetrics") {
@Override
protected Map<String, Double> execute(Object... args) {
@@ -1691,23 +1724,38 @@ public class ElasticSearchPersistenceServiceImpl implements PersistenceService,
public abstract static class InClassLoaderExecute<T> {
+ private String timerName;
+ private MetricsService metricsService;
+
+ public InClassLoaderExecute(MetricsService metricsService, String timerName) {
+ this.timerName = timerName;
+ this.metricsService = metricsService;
+ }
+
protected abstract T execute(Object... args) throws Exception;
public T executeInClassLoader(Object... args) throws Exception {
+
+ long startTime = System.currentTimeMillis();
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
return execute(args);
} finally {
+ if (metricsService != null && metricsService.isActivated()) {
+ metricsService.updateTimer(timerName, startTime);
+ }
Thread.currentThread().setContextClassLoader(tccl);
}
}
public T catchingExecuteInClassLoader(boolean logError, Object... args) {
try {
- return executeInClassLoader(args);
+ return executeInClassLoader(timerName, args);
} catch (Exception e) {
- logger.error("Error while executing in class loader", e);
+ if (logError) {
+ logger.error("Error while executing in class loader", e);
+ }
}
return null;
}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionEvaluatorDispatcher.java
----------------------------------------------------------------------
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionEvaluatorDispatcher.java b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionEvaluatorDispatcher.java
index dff7ecb..6394300 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionEvaluatorDispatcher.java
+++ b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionEvaluatorDispatcher.java
@@ -19,6 +19,8 @@ package org.apache.unomi.persistence.elasticsearch.conditions;
import org.apache.unomi.api.Item;
import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.metrics.MetricAdapter;
+import org.apache.unomi.metrics.MetricsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,6 +36,12 @@ public class ConditionEvaluatorDispatcher {
private Map<String, ConditionEvaluator> evaluators = new ConcurrentHashMap<>();
+ private MetricsService metricsService;
+
+ public void setMetricsService(MetricsService metricsService) {
+ this.metricsService = metricsService;
+ }
+
public void addEvaluator(String name, ConditionEvaluator evaluator) {
evaluators.put(name, evaluator);
}
@@ -59,11 +67,21 @@ public class ConditionEvaluatorDispatcher {
if (evaluators.containsKey(conditionEvaluatorKey)) {
ConditionEvaluator evaluator = evaluators.get(conditionEvaluatorKey);
- Condition contextualCondition = ConditionContextHelper.getContextualCondition(condition, context);
- if (contextualCondition != null) {
- return evaluator.eval(contextualCondition, item, context, this);
- } else {
- return true;
+ final ConditionEvaluatorDispatcher dispatcher = this;
+ try {
+ return new MetricAdapter<Boolean>(metricsService, this.getClass().getName() + ".conditions." + conditionEvaluatorKey) {
+ @Override
+ public Boolean execute(Object... args) throws Exception {
+ Condition contextualCondition = ConditionContextHelper.getContextualCondition(condition, context);
+ if (contextualCondition != null) {
+ return evaluator.eval(contextualCondition, item, context, dispatcher);
+ } else {
+ return true;
+ }
+ }
+ }.runWithTimer();
+ } catch (Exception e) {
+ logger.error("Error executing condition evaluator with key=" + conditionEvaluatorKey, e);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index ad095c1..92871eb 100644
--- a/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -53,6 +53,7 @@
</cm:default-properties>
</cm:property-placeholder>
+ <reference id="metricsService" interface="org.apache.unomi.metrics.MetricsService" />
<service id="elasticSearchPersistenceService" ref="elasticSearchPersistenceServiceImpl">
<interfaces>
<value>org.apache.unomi.persistence.spi.PersistenceService</value>
@@ -65,6 +66,7 @@
<bean id="conditionEvaluatorDispatcherImpl"
class="org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluatorDispatcher">
+ <property name="metricsService" ref="metricsService" />
</bean>
<bean id="elasticSearchPersistenceServiceImpl"
@@ -107,6 +109,7 @@
<property name="maximalElasticSearchVersion" value="${es.maximalElasticSearchVersion}" />
<property name="aggregateQueryBucketSize" value="${es.aggregateQueryBucketSize}" />
+ <property name="metricsService" ref="metricsService" />
</bean>
<!-- We use a listener here because using the list directly for listening to proxies coming from the same bundle didn't seem to work -->
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0d8a61e..99ff1b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -846,6 +846,8 @@
<modules>
<module>api</module>
+ <module>common</module>
+ <module>metrics</module>
<module>persistence-spi</module>
<module>lifecycle-watcher</module>
<module>persistence-elasticsearch</module>
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/services/pom.xml
----------------------------------------------------------------------
diff --git a/services/pom.xml b/services/pom.xml
index d62771c..a479b13 100644
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -123,6 +123,13 @@
<artifactId>org.apache.karaf.cellar.config</artifactId>
<scope>provided</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-metrics</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
----------------------------------------------------------------------
diff --git a/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java b/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
index 89f7d05..6306be6 100644
--- a/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
+++ b/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
@@ -23,6 +23,8 @@ import org.apache.unomi.api.Event;
import org.apache.unomi.api.actions.Action;
import org.apache.unomi.api.actions.ActionExecutor;
import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.metrics.MetricAdapter;
+import org.apache.unomi.metrics.MetricsService;
import org.mvel2.MVEL;
import org.mvel2.ParserConfiguration;
import org.mvel2.ParserContext;
@@ -41,6 +43,11 @@ public class ActionExecutorDispatcher {
private final Map<String, Serializable> mvelExpressions = new ConcurrentHashMap<>();
private final Map<String, ValueExtractor> valueExtractors = new HashMap<>(11);
private Map<String, ActionExecutor> executors = new ConcurrentHashMap<>();
+ private MetricsService metricsService;
+
+ public void setMetricsService(MetricsService metricsService) {
+ this.metricsService = metricsService;
+ }
public ActionExecutorDispatcher() {
valueExtractors.put("profileProperty", new ValueExtractor() {
@@ -177,7 +184,16 @@ public class ActionExecutorDispatcher {
if (executors.containsKey(actionKey)) {
ActionExecutor actionExecutor = executors.get(actionKey);
- return actionExecutor.execute(getContextualAction(action, event), event);
+ try {
+ return new MetricAdapter<Integer>(metricsService, this.getClass().getName() + ".action." + actionKey) {
+ @Override
+ public Integer execute(Object... args) throws Exception {
+ return actionExecutor.execute(getContextualAction(action, event), event);
+ }
+ }.runWithTimer();
+ } catch (Exception e) {
+ logger.error("Error executing action with key=" + actionKey, e);
+ }
}
return EventService.NO_CHANGE;
}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 3d9b181..98a921a 100644
--- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -50,6 +50,7 @@
<reference id="karafCellarEventProducer" interface="org.apache.karaf.cellar.core.event.EventProducer" />
<reference id="karafCellarGroupManager" interface="org.apache.karaf.cellar.core.GroupManager" />
<reference id="osgiConfigurationAdmin" interface="org.osgi.service.cm.ConfigurationAdmin"/>
+ <reference id="metricsService" interface="org.apache.unomi.metrics.MetricsService" />
<!-- Service definitions -->
@@ -100,6 +101,7 @@
<bean id="actionExecutorDispatcherImpl"
class="org.apache.unomi.services.actions.ActionExecutorDispatcher">
+ <property name="metricsService" ref="metricsService" />
</bean>
<bean id="rulesServiceImpl" class="org.apache.unomi.services.services.RulesServiceImpl"
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 38ec572..60018d9 100644
--- a/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/tools/shell-commands/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -56,6 +56,8 @@
<list>
<value>org.apache.unomi.lifecycle-watcher</value>
<value>org.apache.unomi.api</value>
+ <value>org.apache.unomi.common</value>
+ <value>org.apache.unomi.metrics</value>
<value>org.apache.unomi.persistence-spi</value>
<value>org.apache.unomi.persistence-elasticsearch-core</value>
<value>org.apache.unomi.services</value>
[2/2] incubator-unomi git commit: UNOMI-161 Add metrics to analyze
internal performance
Posted by sh...@apache.org.
UNOMI-161 Add metrics to analyze internal performance
Project: http://git-wip-us.apache.org/repos/asf/incubator-unomi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-unomi/commit/a1574bf5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/a1574bf5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/a1574bf5
Branch: refs/heads/master
Commit: a1574bf53415d450c7b7f32096d873dcd3dc4b34
Parents: 1896874
Author: Serge Huber <sh...@apache.org>
Authored: Sat Feb 17 15:36:22 2018 +0100
Committer: Serge Huber <sh...@apache.org>
Committed: Sat Feb 17 15:36:22 2018 +0100
----------------------------------------------------------------------
common/pom.xml | 46 +++++
.../java/org/apache/unomi/common/DataTable.java | 193 +++++++++++++++++++
.../org/apache/unomi/common/DataTableTest.java | 67 +++++++
kar/pom.xml | 10 +
kar/src/main/feature/feature.xml | 2 +
metrics/README.md | 38 ++++
metrics/pom.xml | 88 +++++++++
.../org/apache/unomi/metrics/CalleeCount.java | 29 +++
.../java/org/apache/unomi/metrics/Metric.java | 33 ++++
.../org/apache/unomi/metrics/MetricAdapter.java | 45 +++++
.../apache/unomi/metrics/MetricsService.java | 40 ++++
.../unomi/metrics/commands/ActivateCommand.java | 33 ++++
.../metrics/commands/CalleeStatusCommand.java | 64 ++++++
.../metrics/commands/DeactivateCommand.java | 33 ++++
.../unomi/metrics/commands/ListCommand.java | 87 +++++++++
.../metrics/commands/MetricsCommandSupport.java | 28 +++
.../unomi/metrics/commands/ResetCommand.java | 32 +++
.../unomi/metrics/commands/ViewCommand.java | 47 +++++
.../unomi/metrics/internal/CalleeCountImpl.java | 66 +++++++
.../unomi/metrics/internal/MetricImpl.java | 66 +++++++
.../metrics/internal/MetricsServiceImpl.java | 118 ++++++++++++
.../resources/OSGI-INF/blueprint/blueprint.xml | 77 ++++++++
.../metrics/internal/MetricsServiceTest.java | 150 ++++++++++++++
persistence-elasticsearch/core/pom.xml | 7 +
.../ElasticSearchPersistenceServiceImpl.java | 128 ++++++++----
.../ConditionEvaluatorDispatcher.java | 28 ++-
.../resources/OSGI-INF/blueprint/blueprint.xml | 3 +
pom.xml | 2 +
services/pom.xml | 7 +
.../actions/ActionExecutorDispatcher.java | 18 +-
.../resources/OSGI-INF/blueprint/blueprint.xml | 2 +
.../resources/OSGI-INF/blueprint/blueprint.xml | 2 +
32 files changed, 1543 insertions(+), 46 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/common/pom.xml
----------------------------------------------------------------------
diff --git a/common/pom.xml b/common/pom.xml
new file mode 100644
index 0000000..1b25d6d
--- /dev/null
+++ b/common/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-root</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>unomi-common</artifactId>
+ <name>Apache Unomi :: Common Classes</name>
+ <description>Common Classes used in the Apache Unomi Context server</description>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-csv</artifactId>
+ <version>1.5</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/common/src/main/java/org/apache/unomi/common/DataTable.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/unomi/common/DataTable.java b/common/src/main/java/org/apache/unomi/common/DataTable.java
new file mode 100644
index 0000000..06839fc
--- /dev/null
+++ b/common/src/main/java/org/apache/unomi/common/DataTable.java
@@ -0,0 +1,193 @@
+/*
+ * 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.unomi.common;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An in memory table structure for storing data and performing operations such as sorting it, or generating JSON or
+ * CSV outputs.
+ */
+public class DataTable {
+
+ List<Row> rows = new ArrayList<>();
+ int maxColumns = 0;
+
+ public static final EmptyCell EMPTY_CELL = new EmptyCell();
+
+ public DataTable() {
+ }
+
+ public List<Row> getRows() {
+ return rows;
+ }
+
+ public void addRow(Comparable... rowData) {
+ if (rowData == null) {
+ return;
+ }
+ if (rowData.length > maxColumns) {
+ maxColumns = rowData.length;
+ }
+ Row row = new Row();
+ for (Comparable dataObject : rowData) {
+ row.addData(dataObject);
+ }
+ rows.add(row);
+ }
+
+ public int getMaxColumns() {
+ return maxColumns;
+ }
+
+ public static enum SortOrder {
+ ASCENDING,
+ DESCENDING;
+ }
+
+ public static class SortCriteria {
+ Integer columnIndex;
+ SortOrder sortOrder;
+
+ public SortCriteria(Integer columnIndex, SortOrder sortOrder) {
+ this.columnIndex = columnIndex;
+ this.sortOrder = sortOrder;
+ }
+ }
+
+ public class Row {
+ List<Comparable> rowData = new ArrayList<>();
+
+ public void addData(Comparable data) {
+ rowData.add(data);
+ }
+
+ public Comparable getData(int index) {
+ if (index >= maxColumns) {
+ throw new ArrayIndexOutOfBoundsException("Index on row data (" + index + ") is larger than max columns (" + maxColumns + ")");
+ }
+ if (index >= rowData.size()) {
+ return EMPTY_CELL;
+ }
+ return rowData.get(index);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("[");
+ sb.append(rowData);
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+
+ public void sort(SortCriteria... sortCriterias) {
+ rows.sort(new Comparator<Row>() {
+ @Override
+ public int compare(Row row1, Row row2) {
+ int i = 0;
+ while (i < sortCriterias.length) {
+ Comparable row1Data = row1.getData(sortCriterias[i].columnIndex);
+ Comparable row2Data = row2.getData(sortCriterias[i].columnIndex);
+ if (row1Data == EMPTY_CELL && row2Data != EMPTY_CELL) {
+ if (sortCriterias[i].sortOrder == SortOrder.ASCENDING) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ if (row2Data == EMPTY_CELL && row1Data != EMPTY_CELL) {
+ if (sortCriterias[i].sortOrder == SortOrder.ASCENDING) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ int rowComparison = row1Data.compareTo(row2Data);
+ if (rowComparison == 0) {
+ // rows are equal on this criteria, let's go to the next criteria if it exists
+ if (i < sortCriterias.length) {
+ i++;
+ } else {
+ return 0;
+ }
+ } else {
+ if (sortCriterias[i].sortOrder == SortOrder.ASCENDING) {
+ return rowComparison;
+ } else {
+ return -rowComparison;
+ }
+ }
+ }
+ return 0;
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("[");
+ sb.append(rows);
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static class EmptyCell implements Comparable {
+ @Override
+ public int compareTo(Object o) {
+ if (o instanceof EmptyCell) {
+ return 0;
+ }
+ return -1;
+ }
+
+ @Override
+ public String toString() {
+ return "";
+ }
+ }
+
+ public String toCSV(String... headers) {
+ StringBuilder stringBuilder = new StringBuilder();
+ CSVFormat csvFormat = CSVFormat.DEFAULT;
+ if (headers != null && headers.length > 0) {
+ csvFormat = CSVFormat.DEFAULT.withHeader(headers);
+ }
+ try {
+ CSVPrinter printer = csvFormat.print(stringBuilder);
+ for (Row row : rows) {
+ List<String> values = new ArrayList<>();
+ for (int i = 0; i < maxColumns; i++) {
+ values.add(row.getData(i).toString());
+ }
+ printer.printRecord(values.toArray(new String[values.size()]));
+ }
+ printer.close();
+ } catch (IOException e) {
+ e.printStackTrace(); // this will probably never happen as we are writing to a String.
+ }
+ return stringBuilder.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/common/src/test/java/org/apache/unomi/common/DataTableTest.java
----------------------------------------------------------------------
diff --git a/common/src/test/java/org/apache/unomi/common/DataTableTest.java b/common/src/test/java/org/apache/unomi/common/DataTableTest.java
new file mode 100644
index 0000000..2ed4b9d
--- /dev/null
+++ b/common/src/test/java/org/apache/unomi/common/DataTableTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.unomi.common;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class DataTableTest {
+
+ @Test
+ public void testTableSorting() {
+ DataTable dataTable = new DataTable();
+
+ dataTable.addRow("Row1", 1, 2, 1);
+ dataTable.addRow("Row2", 3, 2, 2);
+ dataTable.addRow("Row3", 2, 1, 1);
+
+ dataTable.sort(new DataTable.SortCriteria(0, DataTable.SortOrder.ASCENDING));
+ assertEquals("Row 1 should be first", "Row1", dataTable.getRows().get(0).getData(0));
+ assertEquals("Row 2 should be second", "Row2", dataTable.getRows().get(1).getData(0));
+ assertEquals("Row 3 should be third", "Row3", dataTable.getRows().get(2).getData(0));
+
+ dataTable.sort(new DataTable.SortCriteria(1, DataTable.SortOrder.ASCENDING));
+ assertEquals("Row 1 should be first", "Row1", dataTable.getRows().get(0).getData(0));
+ assertEquals("Row 3 should be second", "Row3", dataTable.getRows().get(1).getData(0));
+ assertEquals("Row 2 should be third", "Row2", dataTable.getRows().get(2).getData(0));
+
+ dataTable.sort(new DataTable.SortCriteria(2, DataTable.SortOrder.ASCENDING),
+ new DataTable.SortCriteria(3, DataTable.SortOrder.DESCENDING));
+ assertEquals("Row 3 should be first", "Row3", dataTable.getRows().get(0).getData(0));
+ assertEquals("Row 2 should be second", "Row2", dataTable.getRows().get(1).getData(0));
+ assertEquals("Row 1 should be third", "Row1", dataTable.getRows().get(2).getData(0));
+
+ System.out.println("CSV version of data table:");
+ System.out.println(dataTable.toCSV("Row name", "\"Value\", 1", "Value 2", "Value 3"));
+ }
+
+ @Test
+ public void testNonSquareTable() {
+ DataTable dataTable = new DataTable();
+ dataTable.addRow("Row1", 1, 2, 1, "Value");
+ dataTable.addRow("Row2", 3, 2, 2);
+
+ Comparable cellData = dataTable.getRows().get(1).getData(4);
+ assertEquals("Excepted cell data is empty cell", DataTable.EMPTY_CELL, cellData);
+
+ dataTable.sort(new DataTable.SortCriteria(4, DataTable.SortOrder.ASCENDING));
+ assertEquals("Row 2 should be first", "Row2", dataTable.getRows().get(0).getData(0));
+
+ dataTable.sort(new DataTable.SortCriteria(4, DataTable.SortOrder.DESCENDING));
+ assertEquals("Row 1 should be first", "Row1", dataTable.getRows().get(0).getData(0));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/kar/pom.xml
----------------------------------------------------------------------
diff --git a/kar/pom.xml b/kar/pom.xml
index 49ad5f8..39d6316 100644
--- a/kar/pom.xml
+++ b/kar/pom.xml
@@ -43,6 +43,16 @@
</dependency>
<dependency>
<groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-common</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-metrics</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
<artifactId>unomi-services</artifactId>
<version>1.3.0-incubating-SNAPSHOT</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/kar/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/kar/src/main/feature/feature.xml b/kar/src/main/feature/feature.xml
index b4bdd90..9c49c1c 100644
--- a/kar/src/main/feature/feature.xml
+++ b/kar/src/main/feature/feature.xml
@@ -43,6 +43,8 @@
<bundle start-level="70" start="false">mvn:org.apache.unomi/unomi-lifecycle-watcher/${project.version}</bundle>
<bundle start-level="75" start="false">mvn:org.apache.unomi/unomi-api/${project.version}</bundle>
+ <bundle start-level="75" start="false">mvn:org.apache.unomi/unomi-common/${project.version}</bundle>
+ <bundle start-level="75" start="false">mvn:org.apache.unomi/unomi-metrics/${project.version}</bundle>
<bundle start-level="75" start="false">mvn:org.apache.unomi/unomi-persistence-spi/${project.version}</bundle>
<bundle start-level="76" start="false">mvn:org.apache.unomi/unomi-persistence-elasticsearch-core/${project.version}</bundle>
<bundle start-level="77" start="false">mvn:org.apache.unomi/unomi-services/${project.version}</bundle>
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/README.md
----------------------------------------------------------------------
diff --git a/metrics/README.md b/metrics/README.md
new file mode 100644
index 0000000..56e7d0e
--- /dev/null
+++ b/metrics/README.md
@@ -0,0 +1,38 @@
+<!--
+ ~ 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.
+ -->
+
+Metrics
+=======
+
+This project makes it easy to add metrics to your project, which will then give you the following features:
+
+- Count the number a of time a metric was executed
+- Get the accumulated time some measured code took
+- See the call stacks to a metric (deactivated by default in order to minimize performance impact)
+
+Adding metrics to your project :
+
+In order to minimize the impact of metrics in your project, we recommend you use the following pattern to integrate
+metrics:
+
+ long startTime = System.currentTimeMillis();
+ // code to be mesured should be here.
+ if (metricsService != null && metricsService.isActivated()) {
+ metricsService.updateTimer(this.getClass().getName() + YOUR_METRIC_NAME, startTime);
+ }
+
+This will handle all the proper cases of metrics being deactivated as well as even the service not being available.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/pom.xml
----------------------------------------------------------------------
diff --git a/metrics/pom.xml b/metrics/pom.xml
new file mode 100644
index 0000000..bf4a708
--- /dev/null
+++ b/metrics/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-root</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>unomi-metrics</artifactId>
+ <name>Apache Unomi :: Metrics</name>
+ <description>Apache Unomi Context Server Metrics</description>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <version>3.0.8</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.table</artifactId>
+ <version>3.0.8</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-persistence-spi</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-common</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Unit tests -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.6.6</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/CalleeCount.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/CalleeCount.java b/metrics/src/main/java/org/apache/unomi/metrics/CalleeCount.java
new file mode 100644
index 0000000..3552b12
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/CalleeCount.java
@@ -0,0 +1,29 @@
+/*
+ * 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.unomi.metrics;
+
+import java.util.List;
+
+public interface CalleeCount {
+
+ String getHash();
+ List<String> getCallee();
+ long getCount();
+ long incCount();
+ long getTotalTime();
+ long addTime(long time);
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/Metric.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/Metric.java b/metrics/src/main/java/org/apache/unomi/metrics/Metric.java
new file mode 100644
index 0000000..556fb20
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/Metric.java
@@ -0,0 +1,33 @@
+/*
+ * 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.unomi.metrics;
+
+import java.util.Map;
+
+public interface Metric {
+
+ String getName();
+
+ long getTotalCount();
+ long incTotalCount();
+
+ long getTotalTime();
+ long addTotalTime(long time);
+
+ Map<String,CalleeCount> getCalleeCounts();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/MetricAdapter.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/MetricAdapter.java b/metrics/src/main/java/org/apache/unomi/metrics/MetricAdapter.java
new file mode 100644
index 0000000..64ffeee
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/MetricAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.unomi.metrics;
+
+/**
+ * Utility method to run code inside a timer.
+ * @param <T> the type to be used as a result type for the method.
+ */
+public abstract class MetricAdapter<T> {
+
+ private MetricsService metricsService;
+ private String timerName;
+
+ public abstract T execute(Object... args) throws Exception;
+
+ public MetricAdapter(MetricsService metricsService, String timerName) {
+ this.metricsService = metricsService;
+ this.timerName = timerName;
+ }
+
+ public T runWithTimer(Object... args) throws Exception {
+ long startTime = System.currentTimeMillis();
+ try {
+ return execute(args);
+ } finally {
+ if (metricsService != null && metricsService.isActivated()) {
+ metricsService.updateTimer(timerName, startTime);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/MetricsService.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/MetricsService.java b/metrics/src/main/java/org/apache/unomi/metrics/MetricsService.java
new file mode 100644
index 0000000..5fd0d3c
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/MetricsService.java
@@ -0,0 +1,40 @@
+/*
+ * 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.unomi.metrics;
+
+import java.util.Map;
+
+public interface MetricsService {
+
+ void setActivated(boolean activated);
+
+ boolean isActivated();
+
+ Map<String,Boolean> getCalleesStatus();
+
+ void setCalleeActivated(String timerName, boolean activated);
+
+ boolean isCalleeActivated(String timerName);
+
+ Map<String,Metric> getMetrics();
+
+ void resetMetrics();
+
+ void updateTimer(String timerName, long startTime);
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/ActivateCommand.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/ActivateCommand.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/ActivateCommand.java
new file mode 100644
index 0000000..ad17c24
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/ActivateCommand.java
@@ -0,0 +1,33 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.unomi.metrics.MetricsService;
+
+@Command(scope = "metrics", name = "activate", description = "This will activate the metrics system.")
+public class ActivateCommand extends MetricsCommandSupport {
+
+ @Override
+ protected Object doExecute() throws Exception {
+ metricsService.setActivated(true);
+ System.out.println("Metrics activated successfully.");
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/CalleeStatusCommand.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/CalleeStatusCommand.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/CalleeStatusCommand.java
new file mode 100644
index 0000000..5aec628
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/CalleeStatusCommand.java
@@ -0,0 +1,64 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.table.Row;
+import org.apache.karaf.shell.table.ShellTable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Command(scope = "metrics", name = "callee-status", description = "This command will list all the callee configurations, or change the callee status of a specific metric")
+public class CalleeStatusCommand extends MetricsCommandSupport {
+
+ @Option(name = "--no-format", description = "Disable table rendered output", required = false, multiValued = false)
+ boolean noFormat;
+
+ @Argument(index = 0, name = "metricName", description = "The identifier for the metric", required = false, multiValued = false)
+ String metricName;
+
+ @Argument(index = 1, name = "status", description = "The new status for the metric's callee tracing", required = false, multiValued = false)
+ Boolean metricStatus;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ if (metricName != null && metricStatus != null) {
+ metricsService.setCalleeActivated(metricName, metricStatus);
+ System.out.println("Metric callees " + metricName + " set to " + metricStatus);
+ return null;
+ }
+ Map<String,Boolean> calleesStatus = metricsService.getCalleesStatus();
+ ShellTable shellTable = new ShellTable();
+ shellTable.column("Metric");
+ shellTable.column("Activated");
+
+ for (Map.Entry<String,Boolean> calleeStatusEntry : calleesStatus.entrySet()) {
+ List<Object> rowData = new ArrayList<Object>();
+ rowData.add(calleeStatusEntry.getKey());
+ rowData.add(calleeStatusEntry.getValue() ? "x" : "");
+ Row row = shellTable.addRow();
+ row.addContent(rowData);
+ }
+
+ shellTable.print(System.out, !noFormat);
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/DeactivateCommand.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/DeactivateCommand.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/DeactivateCommand.java
new file mode 100644
index 0000000..d16eac0
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/DeactivateCommand.java
@@ -0,0 +1,33 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.unomi.metrics.MetricsService;
+
+@Command(scope = "metrics", name = "deactivate", description = "This will de-activate the metrics system.")
+public class DeactivateCommand extends MetricsCommandSupport {
+
+ @Override
+ protected Object doExecute() throws Exception {
+ metricsService.setActivated(false);
+ System.out.println("Metrics de-activated successfully.");
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/ListCommand.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/ListCommand.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/ListCommand.java
new file mode 100644
index 0000000..861bb3c
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/ListCommand.java
@@ -0,0 +1,87 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.karaf.shell.table.Row;
+import org.apache.karaf.shell.table.ShellTable;
+import org.apache.unomi.common.DataTable;
+import org.apache.unomi.metrics.Metric;
+import org.apache.unomi.metrics.MetricsService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Command(scope = "metrics", name = "list", description = "This will list all the metrics")
+public class ListCommand extends MetricsCommandSupport {
+
+ @Option(name = "--no-format", description = "Disable table rendered output", required = false, multiValued = false)
+ boolean noFormat;
+
+ @Option(name = "--csv", description = "Output table in CSV format", required = false, multiValued = false)
+ boolean csv;
+
+ @Override
+ protected Object doExecute() throws Exception {
+
+ System.out.println("Metrics service status: " + (metricsService.isActivated() ? "active" : "inactive"));
+
+ Map<String,Metric> metrics = metricsService.getMetrics();
+
+ String[] headers = {
+ "Name",
+ "Callees",
+ "Count",
+ "Time [ms]"
+ };
+
+ DataTable dataTable = new DataTable();
+ for (Map.Entry<String,Metric> metricEntry : metrics.entrySet()) {
+ Metric metric = metricEntry.getValue();
+ dataTable.addRow(metric.getName(), metric.getCalleeCounts().size(), metric.getTotalCount(), metric.getTotalTime());
+ }
+ dataTable.sort(new DataTable.SortCriteria(3, DataTable.SortOrder.DESCENDING),
+ new DataTable.SortCriteria(2, DataTable.SortOrder.DESCENDING),
+ new DataTable.SortCriteria(0, DataTable.SortOrder.ASCENDING));
+
+ if (csv) {
+ System.out.println(dataTable.toCSV(headers));
+ return null;
+ }
+
+ ShellTable shellTable = new ShellTable();
+ shellTable.column("Name");
+ shellTable.column("Callees");
+ shellTable.column("Count");
+ shellTable.column("Time [ms]");
+
+ for (DataTable.Row dataTableRow :dataTable.getRows()) {
+ List<Object> rowData = new ArrayList<Object>();
+ rowData.add(dataTableRow.getData(0));
+ rowData.add(dataTableRow.getData(1));
+ rowData.add(dataTableRow.getData(2));
+ rowData.add(dataTableRow.getData(3));
+ Row row = shellTable.addRow();
+ row.addContent(rowData);
+ }
+ shellTable.print(System.out, !noFormat);
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/MetricsCommandSupport.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/MetricsCommandSupport.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/MetricsCommandSupport.java
new file mode 100644
index 0000000..6706100
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/MetricsCommandSupport.java
@@ -0,0 +1,28 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.unomi.metrics.MetricsService;
+
+public abstract class MetricsCommandSupport extends OsgiCommandSupport {
+ MetricsService metricsService;
+
+ public void setMetricsService(MetricsService metricsService) {
+ this.metricsService = metricsService;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/ResetCommand.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/ResetCommand.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/ResetCommand.java
new file mode 100644
index 0000000..3d3f40c
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/ResetCommand.java
@@ -0,0 +1,32 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.unomi.metrics.MetricsService;
+
+@Command(scope = "metrics", name = "reset", description = "This will reset all the metrics to zero.")
+public class ResetCommand extends MetricsCommandSupport {
+
+ @Override
+ protected Object doExecute() throws Exception {
+ metricsService.resetMetrics();
+ System.out.println("Metrics reset successfully.");
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/commands/ViewCommand.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/commands/ViewCommand.java b/metrics/src/main/java/org/apache/unomi/metrics/commands/ViewCommand.java
new file mode 100644
index 0000000..295f1dc
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/commands/ViewCommand.java
@@ -0,0 +1,47 @@
+/*
+ * 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.unomi.metrics.commands;
+
+import com.fasterxml.jackson.core.util.DefaultIndenter;
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.unomi.metrics.Metric;
+import org.apache.unomi.persistence.spi.CustomObjectMapper;
+
+@Command(scope = "metrics", name = "view", description = "This will display all the data for a single metric ")
+public class ViewCommand extends MetricsCommandSupport{
+
+ @Argument(index = 0, name = "metricName", description = "The identifier for the metric", required = true, multiValued = false)
+ String metricName;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ Metric metric = metricsService.getMetrics().get(metricName);
+ if (metric == null) {
+ System.out.println("Couldn't find a metric with name=" + metricName);
+ return null;
+ }
+ // by default pretty printer will use spaces between array values, we change this to linefeeds to make
+ // the callee values easier to read.
+ DefaultPrettyPrinter defaultPrettyPrinter = new DefaultPrettyPrinter();
+ defaultPrettyPrinter = defaultPrettyPrinter.withArrayIndenter(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
+ String jsonMetric = CustomObjectMapper.getObjectMapper().writer(defaultPrettyPrinter).writeValueAsString(metric);
+ System.out.println(jsonMetric);
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/internal/CalleeCountImpl.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/internal/CalleeCountImpl.java b/metrics/src/main/java/org/apache/unomi/metrics/internal/CalleeCountImpl.java
new file mode 100644
index 0000000..14f6094
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/internal/CalleeCountImpl.java
@@ -0,0 +1,66 @@
+/*
+ * 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.unomi.metrics.internal;
+
+import org.apache.unomi.metrics.CalleeCount;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class CalleeCountImpl implements CalleeCount {
+
+ private String hash;
+ private List<String> callee;
+ private AtomicLong count = new AtomicLong();
+ private AtomicLong totalTime = new AtomicLong();
+
+ public CalleeCountImpl(String hash, List<String> callee) {
+ this.hash = hash;
+ this.callee = callee;
+ }
+
+ @Override
+ public String getHash() {
+ return hash;
+ }
+
+ @Override
+ public List<String> getCallee() {
+ return callee;
+ }
+
+ @Override
+ public long getCount() {
+ return count.get();
+ }
+
+ @Override
+ public long incCount() {
+ return count.incrementAndGet();
+ }
+
+ @Override
+ public long getTotalTime() {
+ return totalTime.get();
+ }
+
+ @Override
+ public long addTime(long time) {
+ return totalTime.addAndGet(time);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricImpl.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricImpl.java b/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricImpl.java
new file mode 100644
index 0000000..76d6bf5
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricImpl.java
@@ -0,0 +1,66 @@
+/*
+ * 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.unomi.metrics.internal;
+
+import org.apache.unomi.metrics.CalleeCount;
+import org.apache.unomi.metrics.Metric;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class MetricImpl implements Metric {
+
+ private String name;
+ private long totalCount = 0L;
+ private long totalTime = 0L;
+ private Map<String,CalleeCount> calleeCounts = new ConcurrentHashMap<String, CalleeCount>();
+
+ public MetricImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public long getTotalCount() {
+ return totalCount;
+ }
+
+ @Override
+ public long incTotalCount() {
+ return totalCount++;
+ }
+
+ @Override
+ public long getTotalTime() {
+ return totalTime;
+ }
+
+ @Override
+ public long addTotalTime(long time) {
+ return totalTime += time;
+ }
+
+ @Override
+ public Map<String, CalleeCount> getCalleeCounts() {
+ return calleeCounts;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricsServiceImpl.java
----------------------------------------------------------------------
diff --git a/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricsServiceImpl.java b/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricsServiceImpl.java
new file mode 100644
index 0000000..ea16ad3
--- /dev/null
+++ b/metrics/src/main/java/org/apache/unomi/metrics/internal/MetricsServiceImpl.java
@@ -0,0 +1,118 @@
+/*
+ * 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.unomi.metrics.internal;
+
+import org.apache.unomi.metrics.CalleeCount;
+import org.apache.unomi.metrics.Metric;
+import org.apache.unomi.metrics.MetricsService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class MetricsServiceImpl implements MetricsService {
+
+ boolean activated = false;
+ Map<String,Metric> metrics = new ConcurrentHashMap<String,Metric>();
+ Map<String,Boolean> calleesStatus = new ConcurrentHashMap<>();
+
+ public void setActivated(boolean activated) {
+ this.activated = activated;
+ if (!activated) {
+ metrics.clear();
+ }
+ }
+
+ @Override
+ public boolean isActivated() {
+ return activated;
+ }
+
+ @Override
+ public Map<String, Metric> getMetrics() {
+ return metrics;
+ }
+
+ @Override
+ public void resetMetrics() {
+ metrics.clear();
+ }
+
+ public void updateTimer(String timerName, long startTime) {
+ if (!activated) {
+ return;
+ }
+ long totalTime = System.currentTimeMillis() - startTime;
+ Metric metric = metrics.get(timerName);
+ if (metric == null) {
+ metric = new MetricImpl(timerName);
+ metrics.put(timerName, metric);
+ }
+ metric.incTotalCount();
+ metric.addTotalTime(totalTime);
+ if (isCalleeActivated(timerName)) {
+ StackTraceElement[] stackTraceElements = new Throwable().getStackTrace();
+ List<String> stackTraces = new ArrayList<String>();
+ if (stackTraceElements != null && stackTraceElements.length > 2) {
+ // we start at index 2 to remove the internal
+ for (int i = 2; i < stackTraceElements.length; i++) {
+ stackTraces.add(String.valueOf(stackTraceElements[i]));
+ }
+ String stackTraceHash = Integer.toString(stackTraces.hashCode());
+ CalleeCount calleeCount = metric.getCalleeCounts().get(stackTraceHash);
+ if (calleeCount == null) {
+ calleeCount = new CalleeCountImpl(stackTraceHash, stackTraces);
+ calleeCount.incCount();
+ calleeCount.addTime(totalTime);
+ metric.getCalleeCounts().put(stackTraceHash, calleeCount);
+ } else {
+ calleeCount.incCount();
+ calleeCount.addTime(totalTime);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Map<String, Boolean> getCalleesStatus() {
+ return calleesStatus;
+ }
+
+ @Override
+ public void setCalleeActivated(String timerName, boolean activated) {
+ if (!activated) {
+ if (calleesStatus.containsKey(timerName)) {
+ calleesStatus.remove(timerName);
+ }
+ } else {
+ calleesStatus.put(timerName, true);
+ }
+ }
+
+ @Override
+ public boolean isCalleeActivated(String timerName) {
+ if (calleesStatus.containsKey(timerName)) {
+ return calleesStatus.get(timerName);
+ }
+ if (calleesStatus.containsKey("*")) {
+ return calleesStatus.get("*");
+ }
+ return false;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/metrics/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/metrics/src/main/resources/OSGI-INF/blueprint/blueprint.xml
new file mode 100644
index 0000000..7e3b58b
--- /dev/null
+++ b/metrics/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<blueprint xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
+ xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:shell="http://karaf.apache.org/xmlns/shell/v1.1.0"
+ xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
+ http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0 http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd">
+
+ <cm:property-placeholder persistent-id="org.apache.unomi.metrics"
+ update-strategy="reload" placeholder-prefix="${metrics.">
+ <cm:default-properties>
+ <cm:property name="activated" value="false"/>
+ </cm:default-properties>
+ </cm:property-placeholder>
+
+ <bean id="metricsServiceImpl" class="org.apache.unomi.metrics.internal.MetricsServiceImpl">
+ <property name="activated" value="${metrics.activated}"/>
+ </bean>
+
+ <service id="metricsService" ref="metricsServiceImpl">
+ <interfaces>
+ <value>org.apache.unomi.metrics.MetricsService</value>
+ </interfaces>
+ </service>
+
+ <shell:command-bundle>
+ <shell:command>
+ <shell:action class="org.apache.unomi.metrics.commands.ListCommand">
+ <shell:property name="metricsService" ref="metricsServiceImpl" />
+ </shell:action>
+ </shell:command>
+ <shell:command>
+ <shell:action class="org.apache.unomi.metrics.commands.ViewCommand">
+ <shell:property name="metricsService" ref="metricsServiceImpl" />
+ </shell:action>
+ </shell:command>
+ <shell:command>
+ <shell:action class="org.apache.unomi.metrics.commands.ResetCommand">
+ <shell:property name="metricsService" ref="metricsServiceImpl" />
+ </shell:action>
+ </shell:command>
+ <shell:command>
+ <shell:action class="org.apache.unomi.metrics.commands.ActivateCommand">
+ <shell:property name="metricsService" ref="metricsServiceImpl" />
+ </shell:action>
+ </shell:command>
+ <shell:command>
+ <shell:action class="org.apache.unomi.metrics.commands.DeactivateCommand">
+ <shell:property name="metricsService" ref="metricsServiceImpl" />
+ </shell:action>
+ </shell:command>
+ <shell:command>
+ <shell:action class="org.apache.unomi.metrics.commands.CalleeStatusCommand">
+ <shell:property name="metricsService" ref="metricsServiceImpl" />
+ </shell:action>
+ </shell:command>
+ </shell:command-bundle>
+
+
+</blueprint>
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/metrics/src/test/java/org/apache/unomi/metrics/internal/MetricsServiceTest.java
----------------------------------------------------------------------
diff --git a/metrics/src/test/java/org/apache/unomi/metrics/internal/MetricsServiceTest.java b/metrics/src/test/java/org/apache/unomi/metrics/internal/MetricsServiceTest.java
new file mode 100644
index 0000000..b293599
--- /dev/null
+++ b/metrics/src/test/java/org/apache/unomi/metrics/internal/MetricsServiceTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.unomi.metrics.internal;
+
+import org.apache.unomi.metrics.MetricsService;
+import org.junit.Test;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+public class MetricsServiceTest {
+
+ class Worker implements Callable<BigInteger> {
+
+ MetricsService metricsService;
+ String workerName;
+ int nbLoops = 1000;
+
+ public Worker(MetricsService metricsService, String workerName, int nbLoops) {
+ this.metricsService = metricsService;
+ this.workerName = workerName;
+ this.nbLoops = nbLoops;
+ }
+
+ @Override
+ public BigInteger call() {
+ long startTime = System.currentTimeMillis();
+ BigInteger n1=BigInteger.ZERO,n2=BigInteger.ONE,n3=BigInteger.ZERO;
+ for (int i = 0; i < nbLoops; i++) {
+ n3 = n1.add(n2);
+ n1 = n2;
+ n2 = n3;
+ }
+ if (metricsService != null) {
+ metricsService.updateTimer(workerName, startTime);
+ }
+ return n3;
+ }
+ }
+
+ @Test
+ public void testMetricsImpact() throws InterruptedException {
+ int workerCount = 100000;
+ System.out.println("Free memory=" + humanReadableByteCount(Runtime.getRuntime().freeMemory(), false));
+ System.out.println("Testing with metrics deactivated...");
+ ExecutorService executorService = Executors.newFixedThreadPool(1000);
+ List<Callable<BigInteger>> todo = new ArrayList<Callable<BigInteger>>(workerCount);
+ MetricsServiceImpl metricsService = new MetricsServiceImpl();
+ metricsService.setActivated(false);
+ Random random = new Random();
+ long withoutMetricsStartTime = System.currentTimeMillis();
+ for (int i = 0; i < workerCount; i++) {
+ todo.add(new Worker(null, "worker-" + i, 1000+ random.nextInt(1000)));
+ }
+ List<Future<BigInteger>> answers = executorService.invokeAll(todo);
+ long withoutMetricsTotalTime = System.currentTimeMillis() - withoutMetricsStartTime;
+ System.out.println("Total time without metrics=" + withoutMetricsTotalTime + "ms");
+ assertEquals("Metrics should be empty", 0, metricsService.getMetrics().size());
+ System.out.println("Free memory=" + humanReadableByteCount(Runtime.getRuntime().freeMemory(), false));
+
+ System.out.println("Testing with metrics activated (but no callees)...");
+ todo.clear();
+ metricsService.setActivated(true);
+ assertEquals("Callees should be completely empty", metricsService.getCalleesStatus().size(), 0);
+ long withMetricsStartTime = System.currentTimeMillis();
+ for (int i = 0; i < workerCount; i++) {
+ todo.add(new Worker(metricsService, "worker-" + i, 1000+ random.nextInt(1000)));
+ }
+ answers = executorService.invokeAll(todo);
+ long withMetricsTotalTime = System.currentTimeMillis() - withMetricsStartTime;
+ System.out.println("Total time with metrics (no callees) =" + withMetricsTotalTime + "ms");
+ assertEquals("Metrics count is not correct", workerCount, metricsService.getMetrics().size());
+ System.out.println("Free memory=" + humanReadableByteCount(Runtime.getRuntime().freeMemory(), false));
+
+ System.out.println("Testing with metrics activated (all callees activated)...");
+ todo.clear();
+ metricsService.setActivated(true);
+ metricsService.setCalleeActivated("*", true);
+ assertNotEquals("Callees should not be completely empty", metricsService.getCalleesStatus().size(), 0);
+ long withMetricsAndCalleesStartTime = System.currentTimeMillis();
+ for (int i = 0; i < workerCount; i++) {
+ todo.add(new Worker(metricsService, "worker-" + i, 1000+ random.nextInt(1000)));
+ }
+ answers = executorService.invokeAll(todo);
+ long withMetricsAndCalleesTotalTime = System.currentTimeMillis() - withMetricsAndCalleesStartTime;
+ System.out.println("Total time with metrics (with callees)=" + withMetricsAndCalleesTotalTime + "ms");
+ assertEquals("Metrics count is not correct", workerCount, metricsService.getMetrics().size());
+ System.out.println("Free memory=" + humanReadableByteCount(Runtime.getRuntime().freeMemory(), false));
+ }
+
+ @Test
+ public void testStackTraceGenerationSpeed() {
+ long startWithException = System.currentTimeMillis();
+ for (long i = 0; i < 100000; i++) {
+ String stackTrace = Arrays.toString(new Exception().getStackTrace());
+ int stackTraceHash = stackTrace.hashCode();
+ }
+ System.out.println("Total time using exception and getStackTrace = " + (System.currentTimeMillis()
+ - startWithException) + "ms.");
+
+ long startWithThrowable = System.currentTimeMillis();
+ for (long i = 0; i < 100000; i++) {
+ String stackTrace = Arrays.toString(new Throwable().getStackTrace());
+ int stackTraceHash = stackTrace.hashCode();
+ }
+ System.out.println("Total time using throwable and getStackTrace = " + (System.currentTimeMillis()
+ - startWithThrowable) + "ms.");
+
+ long startWithThread = System.currentTimeMillis();
+ for (long i = 0; i < 100000; i++) {
+ String stackTtrace = Arrays.toString(Thread.currentThread().getStackTrace());
+ int stackTraceHash = stackTtrace.hashCode();
+ }
+ System.out.println("Total time using current thread and getStackTrace = " + (System.currentTimeMillis()
+ - startWithThread) + "ms.");
+
+ System.out.println("Free memory=" + humanReadableByteCount(Runtime.getRuntime().freeMemory(), false));
+
+ }
+
+ public static String humanReadableByteCount(long bytes, boolean si) {
+ int unit = si ? 1000 : 1024;
+ if (bytes < unit) return bytes + " B";
+ int exp = (int) (Math.log(bytes) / Math.log(unit));
+ String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
+ return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/a1574bf5/persistence-elasticsearch/core/pom.xml
----------------------------------------------------------------------
diff --git a/persistence-elasticsearch/core/pom.xml b/persistence-elasticsearch/core/pom.xml
index 198392f..143f7bf 100644
--- a/persistence-elasticsearch/core/pom.xml
+++ b/persistence-elasticsearch/core/pom.xml
@@ -133,6 +133,13 @@
</dependency>
<dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-metrics</artifactId>
+ <version>1.3.0-incubating-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>