All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.internal.metrics.impl.MetricsRegistryImpl Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed 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 com.hazelcast.internal.metrics.impl;

import com.hazelcast.internal.metrics.DoubleGauge;
import com.hazelcast.internal.metrics.DoubleProbeFunction;
import com.hazelcast.internal.metrics.LongProbeFunction;
import com.hazelcast.internal.metrics.MetricsProvider;
import com.hazelcast.internal.metrics.MetricsRegistry;
import com.hazelcast.internal.metrics.ProbeFunction;
import com.hazelcast.internal.metrics.ProbeLevel;
import com.hazelcast.internal.metrics.renderers.ProbeRenderer;
import com.hazelcast.logging.ILogger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import static com.hazelcast.util.Preconditions.checkNotNull;
import static java.lang.String.format;

/**
 * The {@link MetricsRegistry} implementation.
 */
public class MetricsRegistryImpl implements MetricsRegistry {

    private static final Comparator COMPARATOR = new Comparator() {
        @Override
        public int compare(ProbeInstance o1, ProbeInstance o2) {
            return o1.name.compareTo(o2.name);
        }
    };

    final ILogger logger;
    final ProbeLevel minimumLevel;

    private final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(2);

    private final ConcurrentMap probeInstances = new ConcurrentHashMap();
    private final ConcurrentMap, SourceMetadata> metadataMap
            = new ConcurrentHashMap, SourceMetadata>();
    private final LockStripe lockStripe = new LockStripe();

    private final AtomicReference sortedProbeInstancesRef
            = new AtomicReference(new SortedProbeInstances(0));


    /**
     * Creates a MetricsRegistryImpl instance.
     *
     * @param logger       the ILogger used
     * @param minimumLevel the minimum ProbeLevel. If a probe is registered with a ProbeLevel lower than the minimum ProbeLevel,
     *                     then the registration is skipped.
     * @throws NullPointerException if logger or minimumLevel is null
     */
    public MetricsRegistryImpl(ILogger logger, ProbeLevel minimumLevel) {
        this.logger = checkNotNull(logger, "logger can't be null");
        this.minimumLevel = checkNotNull(minimumLevel, "minimumLevel can't be null");

        if (logger.isFinestEnabled()) {
            logger.finest("MetricsRegistry minimumLevel:" + minimumLevel);
        }
    }

    long modCount() {
        return sortedProbeInstancesRef.get().mod;
    }

    @Override
    public Set getNames() {
        Set names = new HashSet(probeInstances.keySet());
        return Collections.unmodifiableSet(names);
    }

    /**
     * Loads the {@link SourceMetadata}.
     *
     * @param clazz the Class to be analyzed.
     * @return the loaded SourceMetadata.
     */
    private SourceMetadata loadSourceMetadata(Class clazz) {
        SourceMetadata metadata = metadataMap.get(clazz);
        if (metadata == null) {
            metadata = new SourceMetadata(clazz);
            SourceMetadata found = metadataMap.putIfAbsent(clazz, metadata);
            metadata = found == null ? metadata : found;
        }

        return metadata;
    }

    @Override
    public  void scanAndRegister(S source, String namePrefix) {
        checkNotNull(source, "source can't be null");
        checkNotNull(namePrefix, "namePrefix can't be null");

        SourceMetadata metadata = loadSourceMetadata(source.getClass());
        metadata.register(this, source, namePrefix);
    }

    @Override
    public  void register(S source, String name, ProbeLevel level, LongProbeFunction function) {
        checkNotNull(source, "source can't be null");
        checkNotNull(name, "name can't be null");
        checkNotNull(function, "function can't be null");
        checkNotNull(level, "level can't be null");

        registerInternal(source, name, level, function);
    }

    @Override
    public  void register(S source, String name, ProbeLevel level, DoubleProbeFunction function) {
        checkNotNull(source, "source can't be null");
        checkNotNull(name, "name can't be null");
        checkNotNull(function, "function can't be null");
        checkNotNull(level, "level can't be null");

        registerInternal(source, name, level, function);
    }

    ProbeInstance getProbeInstance(String name) {
        checkNotNull(name, "name can't be null");

        return probeInstances.get(name);
    }

     void registerInternal(S source, String name, ProbeLevel probeLevel, ProbeFunction function) {
        if (!probeLevel.isEnabled(minimumLevel)) {
            return;
        }

        synchronized (lockStripe.getLock(source)) {
            ProbeInstance probeInstance = probeInstances.get(name);
            if (probeInstance == null) {
                probeInstance = new ProbeInstance(name, source, function);
                probeInstances.put(name, probeInstance);
            } else {
                logOverwrite(probeInstance);
            }

            if (logger.isFinestEnabled()) {
                logger.finest("Registered probeInstance " + name);
            }

            probeInstance.source = source;
            probeInstance.function = function;
        }

        incrementMod();
    }

    private void incrementMod() {
        for (; ; ) {
            SortedProbeInstances current = sortedProbeInstancesRef.get();
            SortedProbeInstances update = new SortedProbeInstances(current.mod + 1);
            if (sortedProbeInstancesRef.compareAndSet(current, update)) {
                break;
            }
        }
    }

    private void logOverwrite(ProbeInstance probeInstance) {
        if (probeInstance.function != null || probeInstance.source != null) {
            logger.warning(format("Overwriting existing probe '%s'", probeInstance.name));
        }
    }

    @Override
    public LongGaugeImpl newLongGauge(String name) {
        checkNotNull(name, "name can't be null");

        return new LongGaugeImpl(this, name);
    }

    @Override
    public DoubleGauge newDoubleGauge(String name) {
        checkNotNull(name, "name can't be null");

        return new DoubleGaugeImpl(this, name);
    }

    @Override
    public  void deregister(S source) {
        checkNotNull(source, "source can't be null");

        boolean changed = false;
        for (Map.Entry entry : probeInstances.entrySet()) {
            ProbeInstance probeInstance = entry.getValue();

            if (probeInstance.source != source) {
                continue;
            }

            String name = entry.getKey();

            boolean destroyed = false;
            synchronized (lockStripe.getLock(source)) {
                if (probeInstance.source == source) {
                    changed = true;
                    probeInstances.remove(name);
                    probeInstance.source = null;
                    probeInstance.function = null;
                    destroyed = true;
                }
            }

            if (destroyed && logger.isFinestEnabled()) {
                logger.finest("Destroying probeInstance " + name);
            }
        }

        if (changed) {
            incrementMod();
        }
    }

    @Override
    public void render(ProbeRenderer renderer) {
        checkNotNull(renderer, "renderer can't be null");

        for (ProbeInstance probeInstance : getSortedProbeInstances()) {
            render(renderer, probeInstance);
        }
    }

    @Override
    public void collectMetrics(Object... objects) {
        for (Object object : objects) {
            if (object instanceof MetricsProvider) {
                ((MetricsProvider) object).provideMetrics(this);
            }
        }
    }

    List getSortedProbeInstances() {
        for (; ; ) {
            SortedProbeInstances current = sortedProbeInstancesRef.get();
            if (current.probeInstances != null) {
                return current.probeInstances;
            }

            List probeInstanceList = new ArrayList(probeInstances.values());
            Collections.sort(probeInstanceList, COMPARATOR);

            SortedProbeInstances update = new SortedProbeInstances(current.mod, probeInstanceList);
            if (sortedProbeInstancesRef.compareAndSet(current, update)) {
                return update.probeInstances;
            }
        }
    }

    private void render(ProbeRenderer renderer, ProbeInstance probeInstance) {
        ProbeFunction function = probeInstance.function;
        Object source = probeInstance.source;
        String name = probeInstance.name;

        if (function == null || source == null) {
            renderer.renderNoValue(name);
            return;
        }

        try {
            if (function instanceof LongProbeFunction) {
                LongProbeFunction longFunction = (LongProbeFunction) function;
                renderer.renderLong(name, longFunction.get(source));
            } else {
                DoubleProbeFunction doubleFunction = (DoubleProbeFunction) function;
                renderer.renderDouble(name, doubleFunction.get(source));
            }
        } catch (Exception e) {
            renderer.renderException(name, e);
        }
    }

    @Override
    public void scheduleAtFixedRate(final Runnable publisher, long period, TimeUnit timeUnit) {
        scheduledExecutorService.scheduleAtFixedRate(publisher, 0, period, timeUnit);
    }

    public void shutdown() {
        scheduledExecutorService.shutdown();
    }

    private static class SortedProbeInstances {
        private final long mod;
        private final List probeInstances;

        private SortedProbeInstances(long mod) {
            this.mod = mod;
            this.probeInstances = null;
        }

        public SortedProbeInstances(long mod, List probeInstances) {
            this.mod = mod;
            this.probeInstances = probeInstances;
        }
    }
}