You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@sling.apache.org by GitBox <gi...@apache.org> on 2020/07/15 16:48:41 UTC

[GitHub] [sling-org-apache-sling-discovery-support] stefan-egli opened a new pull request #1: SLING-9589 : expose discovery metrics

stefan-egli opened a new pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1


   Metrics are exposed via dropwizard, and only using a TopologyEventListener without any further changes needed. Hence the change is actually in the support bundle, not in the main implementation bundle.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] stefan-egli commented on a change in pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
stefan-egli commented on a change in pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#discussion_r468711823



##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {
+        changingEvents.incrementAndGet();
+        topologyIsUndefined.set(1);
+    }
+
+    private void handleChanged(TopologyView oldView, TopologyView newView) {
+        changedEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(oldView, newView);
+
+        updateLeaderSwitch(oldView, newView);
+
+        updateProperties(newView);
+        updateRemote(newView);
+    }
+
+    private void handlePropertiesChanged(TopologyView oldView, TopologyView newView) {
+        propertyChangedEvents.incrementAndGet();
+
+        updateProperties(newView);
+    }
+
+    private void updateLocalClusterInstances(TopologyView oldViewOrNull, TopologyView newView) {
+        final ClusterView newLocalClusterView = newView.getLocalInstance().getClusterView();
+        localClusterInstances.set(newLocalClusterView.getInstances().size());
+
+        if (oldViewOrNull == null) {
+            localClusterJoins.addAndGet(newLocalClusterView.getInstances().size());
+        } else {
+            final ClusterView oldLocalClusterView = oldViewOrNull.getLocalInstance().getClusterView();
+            final InstancesDiff diff = new InstancesDiff(oldLocalClusterView, newLocalClusterView);
+            final Collection<InstanceDescription> added = diff.added().get();
+            final Collection<InstanceDescription> removed = diff.removed().get();
+
+            if (added != null && added.size() > 0) {

Review comment:
       +1, removed

##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {
+        changingEvents.incrementAndGet();
+        topologyIsUndefined.set(1);
+    }
+
+    private void handleChanged(TopologyView oldView, TopologyView newView) {
+        changedEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(oldView, newView);
+
+        updateLeaderSwitch(oldView, newView);
+
+        updateProperties(newView);
+        updateRemote(newView);
+    }
+
+    private void handlePropertiesChanged(TopologyView oldView, TopologyView newView) {
+        propertyChangedEvents.incrementAndGet();
+
+        updateProperties(newView);
+    }
+
+    private void updateLocalClusterInstances(TopologyView oldViewOrNull, TopologyView newView) {
+        final ClusterView newLocalClusterView = newView.getLocalInstance().getClusterView();
+        localClusterInstances.set(newLocalClusterView.getInstances().size());
+
+        if (oldViewOrNull == null) {
+            localClusterJoins.addAndGet(newLocalClusterView.getInstances().size());
+        } else {
+            final ClusterView oldLocalClusterView = oldViewOrNull.getLocalInstance().getClusterView();
+            final InstancesDiff diff = new InstancesDiff(oldLocalClusterView, newLocalClusterView);
+            final Collection<InstanceDescription> added = diff.added().get();
+            final Collection<InstanceDescription> removed = diff.removed().get();
+
+            if (added != null && added.size() > 0) {
+                localClusterJoins.addAndGet(added.size());
+            }
+            if (removed != null && removed.size() > 0) {

Review comment:
       +1, removed




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] removed a comment on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] removed a comment on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672091175


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] stefan-egli merged pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
stefan-egli merged pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] removed a comment on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] removed a comment on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672092647


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [17 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672091175


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] mreutegg commented on a change in pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
mreutegg commented on a change in pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#discussion_r468523625



##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {
+        changingEvents.incrementAndGet();
+        topologyIsUndefined.set(1);
+    }
+
+    private void handleChanged(TopologyView oldView, TopologyView newView) {
+        changedEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(oldView, newView);
+
+        updateLeaderSwitch(oldView, newView);
+
+        updateProperties(newView);
+        updateRemote(newView);
+    }
+
+    private void handlePropertiesChanged(TopologyView oldView, TopologyView newView) {
+        propertyChangedEvents.incrementAndGet();
+
+        updateProperties(newView);
+    }
+
+    private void updateLocalClusterInstances(TopologyView oldViewOrNull, TopologyView newView) {
+        final ClusterView newLocalClusterView = newView.getLocalInstance().getClusterView();
+        localClusterInstances.set(newLocalClusterView.getInstances().size());
+
+        if (oldViewOrNull == null) {
+            localClusterJoins.addAndGet(newLocalClusterView.getInstances().size());
+        } else {
+            final ClusterView oldLocalClusterView = oldViewOrNull.getLocalInstance().getClusterView();
+            final InstancesDiff diff = new InstancesDiff(oldLocalClusterView, newLocalClusterView);
+            final Collection<InstanceDescription> added = diff.added().get();
+            final Collection<InstanceDescription> removed = diff.removed().get();
+
+            if (added != null && added.size() > 0) {
+                localClusterJoins.addAndGet(added.size());
+            }
+            if (removed != null && removed.size() > 0) {

Review comment:
       Same as above.

##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough

Review comment:
       ```suggestion
        * There shouldn't be an awful lot of properties, so int sounds more than enough
   ```

##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {
+        changingEvents.incrementAndGet();
+        topologyIsUndefined.set(1);
+    }
+
+    private void handleChanged(TopologyView oldView, TopologyView newView) {
+        changedEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(oldView, newView);
+
+        updateLeaderSwitch(oldView, newView);
+
+        updateProperties(newView);
+        updateRemote(newView);
+    }
+
+    private void handlePropertiesChanged(TopologyView oldView, TopologyView newView) {

Review comment:
       Remove `oldView` because it is unused?

##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {
+        changingEvents.incrementAndGet();
+        topologyIsUndefined.set(1);
+    }
+
+    private void handleChanged(TopologyView oldView, TopologyView newView) {
+        changedEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(oldView, newView);
+
+        updateLeaderSwitch(oldView, newView);
+
+        updateProperties(newView);
+        updateRemote(newView);
+    }
+
+    private void handlePropertiesChanged(TopologyView oldView, TopologyView newView) {
+        propertyChangedEvents.incrementAndGet();
+
+        updateProperties(newView);
+    }
+
+    private void updateLocalClusterInstances(TopologyView oldViewOrNull, TopologyView newView) {
+        final ClusterView newLocalClusterView = newView.getLocalInstance().getClusterView();
+        localClusterInstances.set(newLocalClusterView.getInstances().size());
+
+        if (oldViewOrNull == null) {
+            localClusterJoins.addAndGet(newLocalClusterView.getInstances().size());
+        } else {
+            final ClusterView oldLocalClusterView = oldViewOrNull.getLocalInstance().getClusterView();
+            final InstancesDiff diff = new InstancesDiff(oldLocalClusterView, newLocalClusterView);
+            final Collection<InstanceDescription> added = diff.added().get();
+            final Collection<InstanceDescription> removed = diff.removed().get();
+
+            if (added != null && added.size() > 0) {

Review comment:
       `diff.added().get()` is annotated with Nonnull. The null check here is unnecessary.

##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {

Review comment:
       Remove `oldView` because it is unused?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672092647


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [17 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] removed a comment on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] removed a comment on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-671536663


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] removed a comment on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] removed a comment on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-658877784


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] removed a comment on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] removed a comment on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672091044


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] stefan-egli commented on a change in pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
stefan-egli commented on a change in pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#discussion_r468710146



##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {
+        changingEvents.incrementAndGet();
+        topologyIsUndefined.set(1);
+    }
+
+    private void handleChanged(TopologyView oldView, TopologyView newView) {
+        changedEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(oldView, newView);
+
+        updateLeaderSwitch(oldView, newView);
+
+        updateProperties(newView);
+        updateRemote(newView);
+    }
+
+    private void handlePropertiesChanged(TopologyView oldView, TopologyView newView) {

Review comment:
       agreed, can always add it back if needed later, removed now.

##########
File path: src/main/java/org/apache/sling/discovery/impl/support/MetricReporter.java
##########
@@ -0,0 +1,429 @@
+/*
+ * 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.sling.discovery.impl.support;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.commons.InstancesDiff;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * MetricReporter is in charge of listening to TopologyEvents
+ * (by virtue of being a TopologyEventListener) and exposing
+ * metrics (via dropwizard).
+ */
+@Service(value = { TopologyEventListener.class })
+@Component(immediate = true)
+public class MetricReporter implements TopologyEventListener {
+
+    // event counters
+    static final String METRICS_NAME_TOPOLOGY_CHANGING_EVENTS = "discovery.oak.topology.changing.events";
+    static final String METRICS_NAME_TOPOLOGY_INIT_EVENTS = "discovery.oak.topology.init.events";
+    static final String METRICS_NAME_TOPOLOGY_CHANGED_EVENTS = "discovery.oak.topology.changed.events";
+    static final String METRICS_NAME_PROPERTY_CHANGED_EVENTS = "discovery.oak.property.changed.events";
+
+    static final String METRICS_NAME_TOPOLOGY_IS_UNDEFINED = "discovery.oak.topology.is.undefined";
+
+    static final String METRICS_NAME_LOCAL_CLUSTER_INSTANCES = "discovery.oak.local.cluster.instances";
+    static final String METRICS_NAME_LOCAL_CLUSTER_JOINS = "discovery.oak.local.cluster.joins";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEAVES = "discovery.oak.local.cluster.leaves";
+    static final String METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES = "discovery.oak.local.cluster.leader.switches";
+    static final String METRICS_NAME_LOCAL_CLUSTER_PROPERTIES = "discovery.oak.local.cluster.properties";
+
+    static final String METRICS_NAME_OWN_IS_LEADER = "discovery.oak.own.is.leader";
+    static final String METRICS_NAME_OWN_PROPERTIES = "discovery.oak.own.properties";
+
+    static final String METRICS_NAME_REMOTE_CLUSTERS = "discovery.oak.remote.cluster";
+    static final String METRICS_NAME_REMOTE_INSTANCES = "discovery.oak.remote.instances";
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference(target = "(name=sling)")
+    MetricRegistry metricRegistry;
+
+    private final List<String> registeredGaugeNameList = new LinkedList<String>();
+
+    /** 
+     * for init there would really only be 2 values needed: 0 and 1
+     * as there should only ever be 1 TOPOLOGY_INIT event.
+     * But for monitoring reasons it might be interesting to use a 
+     * counter here nevertheless and ensure that it never goes above 1.
+     */
+    private final AtomicInteger initEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGING events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changingEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. An int should be enough,
+     * if there is 1 event per second this lasts 68 years
+     */
+    private final AtomicInteger changedEvents = new AtomicInteger(0);
+
+    /** 
+     * counts number of TOPOLOGY_CHANGED events. 
+     * With a long if there is 1 event per millisecond it lasts 292471208 years
+     */
+    private final AtomicLong propertyChangedEvents = new AtomicLong(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that if the topology is undefined the localLeader is also undefined
+     * (but still represents the previous value).
+     * This one starts off as 1 until TOPOLOGY_INIT is received.
+     */
+    private final AtomicInteger topologyIsUndefined = new AtomicInteger(1);
+
+    /** 
+     * Keeps track of number of instances in local cluster.
+     * There should really only be a small number, but even so an int is certainly enough.
+     */
+    private final AtomicInteger localClusterInstances = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that joined the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances joining per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterJoins = new AtomicInteger(0);
+
+    /**
+     * Counts the number of instances that left the local cluster, over time.
+     * The order of magnitude is number of TOPOLOGY_CHANGED events multiplied
+     * by number of instances leaving per such event. Still, an int sounds more than enough.
+     */
+    private final AtomicInteger localClusterLeaves = new AtomicInteger(0);
+
+    /**
+     * The number of leader changes should be smaller or equal to
+     * TOPOLOGY_CHANGED events +1.
+     * So an int seems more than enough.
+     * Note that this counts only actual changes, not a leader being announced via TOPOLOGY_INIT.
+     */
+    private final AtomicInteger localClusterLeaderSwitches = new AtomicInteger(0);
+
+    /**
+     * The order of magnitude here is number of properties multiplied by number of instances in the local cluster.
+     * So this is an order of magnitude higher than ownPropertiesCount -
+     * but still, an int should be enough, really.
+     */
+    private final AtomicInteger localClusterProperties = new AtomicInteger(0);
+
+    /**
+     * This is either 0 or 1 - but since the Gauge should be as fast as possible
+     * we maintain an int.
+     * Note that localLeader is only valid if the topology is not changing currently
+     * (otherwise it is undefined).
+     */
+    private final AtomicInteger ownIsLeader = new AtomicInteger(0);
+
+    /**
+     * There shouldn't be an aweful lot of properties, so int sounds more than enough
+     */
+    private final AtomicInteger ownProperties = new AtomicInteger(0);
+
+    /**
+     * Attached/remote clusters aren't probably too many, so again, int is enough
+     */
+    private final AtomicInteger remoteClusters = new AtomicInteger(0);
+
+    /**
+     * Attached/remote instances (sum of instances in remote clusters) -
+     * probably aren't too many, int is enough
+     */
+    private final AtomicInteger remoteInstances = new AtomicInteger(0);
+
+    @Activate
+    protected void activate() {
+        logger.debug("activate: start");
+
+        createGauge(METRICS_NAME_TOPOLOGY_INIT_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return initEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGING_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changingEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_CHANGED_EVENTS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return changedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_PROPERTY_CHANGED_EVENTS, new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return propertyChangedEvents.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_TOPOLOGY_IS_UNDEFINED, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return topologyIsUndefined.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterInstances.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_JOINS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterJoins.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEAVES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaves.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_LEADER_SWITCHES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterLeaderSwitches.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_LOCAL_CLUSTER_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return localClusterProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_IS_LEADER, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownIsLeader.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_OWN_PROPERTIES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return ownProperties.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_CLUSTERS, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteClusters.get();
+            }
+        });
+
+        createGauge(METRICS_NAME_REMOTE_INSTANCES, new Gauge<Integer>() {
+            @Override
+            public Integer getValue() {
+                return remoteInstances.get();
+            }
+        });
+
+        logger.info("activate: done.");
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void createGauge(String gaugeName, Gauge gauge) {
+        logger.debug("createGauge: registering gauge : " + gaugeName);
+        this.metricRegistry.register(gaugeName, gauge);
+        registeredGaugeNameList.add(gaugeName);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        logger.debug("deactivate: deactivating.");
+        unregisterGauges();
+        logger.info("deactivate: done.");
+    }
+
+    private void unregisterGauges() {
+        for (String registeredGaugeName : registeredGaugeNameList) {
+            logger.debug("unregisterGauges : unregistering gauge : " + registeredGaugeName);
+            metricRegistry.remove(registeredGaugeName);
+        }
+    }
+
+    @Override
+    public void handleTopologyEvent(TopologyEvent event) {
+        if (event == null) {
+            // this should not occur
+            return;
+        }
+
+        try {
+            switch (event.getType()) {
+            case TOPOLOGY_INIT: {
+                handleInit(event.getNewView());
+                return;
+            }
+            case TOPOLOGY_CHANGING: {
+                handleChanging(event.getOldView());
+                return;
+            }
+            case TOPOLOGY_CHANGED: {
+                handleChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            case PROPERTIES_CHANGED: {
+                handlePropertiesChanged(event.getOldView(), event.getNewView());
+                return;
+            }
+            }
+        } catch (Exception e) {
+            // we should not really see any of those, but just in case..:
+            logger.error("handleTopologyEvent: got Exception " + e, e);
+        }
+    }
+
+    private void handleInit(TopologyView newView) {
+        initEvents.incrementAndGet();
+        topologyIsUndefined.set(0);
+
+        updateLocalClusterInstances(null, newView);
+
+        updateProperties(newView);
+        updateOwnIsLeader(newView);
+        updateRemote(newView);
+    }
+
+    private void handleChanging(TopologyView oldView) {

Review comment:
       agreed, can always add it back if needed later, removed now.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] stefan-egli commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
stefan-egli commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672073971


   Thx @mreutegg for the review, I've applied all findings, incl
   > Did you consider using Sling Metrics instead of introducing a dependency to dropwizard directly?
   
   yea, I agree, it's nicer to depend on sling.commons.metrics, done that now [here](https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1/commits/ae26769f02134aabedada3dcfe5dbf3ed5e64132)


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-671536663


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672091044


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-672094905


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&reso
 lved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [15 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='95.7%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [95.7% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [sling-org-apache-sling-discovery-support] sonarcloud[bot] commented on pull request #1: SLING-9589 : expose discovery metrics

Posted by GitBox <gi...@apache.org>.
sonarcloud[bot] commented on pull request #1:
URL: https://github.com/apache/sling-org-apache-sling-discovery-support/pull/1#issuecomment-658877784


   Kudos, SonarCloud Quality Gate passed!
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=BUG)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=SECURITY_HOTSPOT) to review)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL) [19 Code Smells](https://sonarcloud.io/project/issues?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&resolved=false&types=CODE_SMELL)
   
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/90.png' alt='94.6%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list) [94.6% Coverage](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_coverage&view=list)  
   [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=apache_sling-org-apache-sling-discovery-support&pullRequest=1&metric=new_duplicated_lines_density&view=list)
   
   <img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/message_warning.png' alt='warning' width='16' height='16' /> The version of Java (1.8.0_252) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.
   Read more [here](https://sonarcloud.io/documentation/upcoming/)
   
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org