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

com.yahoo.metrics.simple.MetricReceiver Maven / Gradle / Ivy

// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.metrics.simple;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableMap;
import com.yahoo.concurrent.ThreadLocalDirectory;

/**
 * The reception point for measurements. This is the class users should inject
 * in constructors for declaring instances of {@link Counter} and {@link Gauge}
 * for the actual measurement of metrics.
 *
 * @author Steinar Knutsen
 */
@Beta
public class MetricReceiver {

    public static final MetricReceiver nullImplementation = new NullReceiver();
    private final ThreadLocalDirectory metricsCollection;
    private final AtomicReference currentSnapshot;

    // metricSettings is volatile for reading, the lock is for updates
    private final Object histogramDefinitionsLock = new Object();
    private volatile Map metricSettings;

    private static final class NullCounter extends Counter {

        NullCounter() {
            super(null, null, null);
        }

        @Override
        public void add() {
        }

        @Override
        public void add(long n) {
        }

        @Override
        public void add(Point p) {
        }

        @Override
        public void add(long n, Point p) {
        }

        @Override
        public PointBuilder builder() {
            return super.builder();
        }
    }

    private static final class NullGauge extends Gauge {
        NullGauge() {
            super(null, null, null);
        }

        @Override
        public void sample(double x) {
        }

        @Override
        public void sample(double x, Point p) {
        }

        @Override
        public PointBuilder builder() {
            return super.builder();
        }

    }

    public static final class MockReceiver extends MetricReceiver {

        private final ThreadLocalDirectory collection;

        private MockReceiver(ThreadLocalDirectory collection) {
            super(collection, null);
            this.collection = collection;
        }

        public MockReceiver() {
            this(new ThreadLocalDirectory<>(new MetricUpdater()));
        }

        /** Gathers all data since last snapshot */
        public Bucket getSnapshot() {
            final Bucket merged = new Bucket();
            for (Bucket b : collection.fetch()) {
                merged.merge(b, true);
            }
            return merged;
        }

        /** Utility method for testing */
        public Point point(String dim, String val) {
            return pointBuilder().set(dim, val).build();
        }

    }

    private static final class NullReceiver extends MetricReceiver {

        NullReceiver() {
            super(null, null);
        }

        @Override
        public void update(Sample s) {
        }

        @Override
        public Counter declareCounter(String name) {
            return new NullCounter();
        }

        @Override
        public Counter declareCounter(String name, Point boundDimensions) {
            return new NullCounter();
        }

        @Override
        public Gauge declareGauge(String name) {
            return new NullGauge();
        }

        @Override
        public Gauge declareGauge(String name, Point boundDimensions) {
            return new NullGauge();
        }

        @Override
        public Gauge declareGauge(String name, Optional boundDimensions, MetricSettings customSettings) {
            return null;
        }

        @Override
        public PointBuilder pointBuilder() {
            return null;
        }

        @Override
        public Bucket getSnapshot() {
            return null;
        }

        @Override
        void addMetricDefinition(String metricName, MetricSettings definition) {
        }

        @Override
        MetricSettings getMetricDefinition(String metricName) {
            return null;
        }
    }

    public MetricReceiver(ThreadLocalDirectory metricsCollection, AtomicReference currentSnapshot) {
        this.metricsCollection = metricsCollection;
        this.currentSnapshot = currentSnapshot;
        metricSettings = new ImmutableMap.Builder().build();
    }

    /**
     * Update a metric. This API is not intended for clients for the
     * simplemetrics API, declare a Counter or a Gauge using
     * {@link #declareCounter(String)}, {@link #declareCounter(String, Point)},
     * {@link #declareGauge(String)}, or {@link #declareGauge(String, Point)}
     * instead.
     *
     * @param sample a single simple containing all meta data necessary to update a metric
     */
    public void update(Sample sample) {
        // pass around the receiver instead of histogram settings to avoid reading any volatile if unnecessary
        sample.setReceiver(this);
        metricsCollection.update(sample);
    }

    /**
     * Declare a counter metric without setting any default position.
     *
     * @param name the name of the metric
     * @return a thread-safe counter
     */
    public Counter declareCounter(String name) {
        return declareCounter(name, null);
    }

    /**
     * Declare a counter metric, with default dimension values as given. Create
     * the point argument by using a builder from {@link #pointBuilder()}.
     *
     * @param name the name of the metric
     * @param boundDimensions dimensions which have a fixed value in the life cycle of the metric object or null
     * @return a thread-safe counter with given default values
     */
    public Counter declareCounter(String name, Point boundDimensions) {
        return new Counter(name, boundDimensions, this);
    }

    /**
     * Declare a gauge metric with any default position.
     *
     * @param name the name of the metric
     * @return a thread-safe gauge instance
     */
    public Gauge declareGauge(String name) {
        return declareGauge(name, null);
    }

    /**
     * Declare a gauge metric, with default dimension values as given. Create
     * the point argument by using a builder from {@link #pointBuilder()}.
     *
     * @param name the name of the metric
     * @param boundDimensions dimensions which have a fixed value in the life cycle of the metric object or null
     * @return a thread-safe gauge metric
     */
    public Gauge declareGauge(String name, Point boundDimensions) {
        return declareGauge(name, Optional.ofNullable(boundDimensions), null);
    }

    /**
     * Declare a gauge metric, with default dimension values as given. Create
     * the point argument by using a builder from {@link #pointBuilder()}.
     * MetricSettings instances are built using
     * {@link MetricSettings.Builder}.
     *
     * @param name the name of the metric
     * @param boundDimensions an optional of dimensions which have a fixed value in the life cycle of the metric object
     * @param customSettings any optional settings
     * @return a thread-safe gauge metric
     */
    public Gauge declareGauge(String name, Optional boundDimensions, MetricSettings customSettings) {
        if (customSettings != null) {
            addMetricDefinition(name, customSettings);
        }
        Point defaultDimensions = null;
        if (boundDimensions.isPresent()) {
            defaultDimensions = boundDimensions.get();
        }
        return new Gauge(name, defaultDimensions, this);
    }

    /**
     * Create a PointBuilder instance with no default settings. PointBuilder
     * instances are not thread-safe.
     *
     * @return an "empty" point builder instance
     */
    public PointBuilder pointBuilder() {
        return new PointBuilder();
    }

    /**
     * Fetch the latest metric values, aggregated over all threads for the
     * configured sample history (by default five minutes). The values will be
     * less than 1 second old, and this method has only a memory barrier as side
     * effect.
     *
     * @return the latest five minutes of metrics
     */
    public Bucket getSnapshot() {
        return currentSnapshot.get();
    }

    /**
     * Add how to build a histogram for a given metric.
     *
     * @param metricName the metric where samples should be put in a histogram
     * @param definition settings for a histogram
     */
    void addMetricDefinition(String metricName, MetricSettings definition) {
        synchronized (histogramDefinitionsLock) {
            // read the volatile _after_ acquiring the lock
            Map oldMetricDefinitions = metricSettings;
            Map builderMap = new HashMap<>(oldMetricDefinitions.size() + 1);
            builderMap.putAll(oldMetricDefinitions);
            builderMap.put(metricName, definition);
            metricSettings = ImmutableMap.copyOf(builderMap);
        }
    }

    /**
     * Get how to build a histogram for a given metric, or null if no histogram should be created.
     *
     * @param metricName the name of an arbitrary metric
     * @return the corresponding histogram definition or null
     */
    MetricSettings getMetricDefinition(String metricName) {
        return metricSettings.get(metricName);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy