com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker.java

Source

// Copyright (C) 2015 The Android Open Source Project
//
// 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.google.gerrit.metrics.dropwizard;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.metrics.dropwizard.MetricResource.METRIC_KIND;
import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND;

import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.metrics.CallbackMetric;
import com.google.gerrit.metrics.CallbackMetric0;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Counter2;
import com.google.gerrit.metrics.Counter3;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.FieldOrdering;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram0;
import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.Histogram2;
import com.google.gerrit.metrics.Histogram3;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.metrics.Timer2;
import com.google.gerrit.metrics.Timer3;
import com.google.gerrit.metrics.proc.JGitMetricModule;
import com.google.gerrit.metrics.proc.ProcMetricModule;
import com.google.gerrit.server.cache.CacheMetrics;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * Connects Gerrit metric package onto DropWizard.
 *
 * @see <a href="http://www.dropwizard.io/">DropWizard</a>
 */
@Singleton
public class DropWizardMetricMaker extends MetricMaker {
    public static class ApiModule extends RestApiModule {
        @Override
        protected void configure() {
            bind(MetricRegistry.class).in(Scopes.SINGLETON);
            bind(DropWizardMetricMaker.class).in(Scopes.SINGLETON);
            bind(MetricMaker.class).to(DropWizardMetricMaker.class);

            install(new ProcMetricModule());
            install(new JGitMetricModule());
        }
    }

    public static class RestModule extends RestApiModule {
        @Override
        protected void configure() {
            DynamicMap.mapOf(binder(), METRIC_KIND);
            child(CONFIG_KIND, "metrics").to(MetricsCollection.class);
            get(METRIC_KIND).to(GetMetric.class);
            bind(CacheMetrics.class);
        }
    }

    private final MetricRegistry registry;
    private final Map<String, BucketedMetric> bucketed;
    private final Map<String, ImmutableMap<String, String>> descriptions;

    @Inject
    DropWizardMetricMaker(MetricRegistry registry) {
        this.registry = registry;
        this.bucketed = new ConcurrentHashMap<>();
        this.descriptions = new ConcurrentHashMap<>();
    }

    Iterable<String> getMetricNames() {
        return descriptions.keySet();
    }

    /** Get the underlying metric implementation. */
    public Metric getMetric(String name) {
        Metric m = bucketed.get(name);
        return m != null ? m : registry.getMetrics().get(name);
    }

    /** Lookup annotations from a metric's {@link Description}. */
    public ImmutableMap<String, String> getAnnotations(String name) {
        return descriptions.get(name);
    }

    @Override
    public synchronized Counter0 newCounter(String name, Description desc) {
        checkCounterDescription(name, desc);
        define(name, desc);
        return newCounterImpl(name, desc.isRate());
    }

    @Override
    public synchronized <F1> Counter1<F1> newCounter(String name, Description desc, Field<F1> field1) {
        checkCounterDescription(name, desc);
        CounterImpl1<F1> m = new CounterImpl1<>(this, name, desc, field1);
        define(name, desc);
        bucketed.put(name, m);
        return m.counter();
    }

    @Override
    public synchronized <F1, F2> Counter2<F1, F2> newCounter(String name, Description desc, Field<F1> field1,
            Field<F2> field2) {
        checkCounterDescription(name, desc);
        CounterImplN m = new CounterImplN(this, name, desc, field1, field2);
        define(name, desc);
        bucketed.put(name, m);
        return m.counter2();
    }

    @Override
    public synchronized <F1, F2, F3> Counter3<F1, F2, F3> newCounter(String name, Description desc,
            Field<F1> field1, Field<F2> field2, Field<F3> field3) {
        checkCounterDescription(name, desc);
        CounterImplN m = new CounterImplN(this, name, desc, field1, field2, field3);
        define(name, desc);
        bucketed.put(name, m);
        return m.counter3();
    }

    private static void checkCounterDescription(String name, Description desc) {
        checkMetricName(name);
        checkArgument(!desc.isConstant(), "counters must not be constant");
        checkArgument(!desc.isGauge(), "counters must not be gauge");
    }

    CounterImpl newCounterImpl(String name, boolean isRate) {
        if (isRate) {
            final com.codahale.metrics.Meter m = registry.meter(name);
            return new CounterImpl(name, m) {
                @Override
                public void incrementBy(long delta) {
                    checkArgument(delta >= 0, "counter delta must be >= 0");
                    m.mark(delta);
                }
            };
        }
        final com.codahale.metrics.Counter m = registry.counter(name);
        return new CounterImpl(name, m) {
            @Override
            public void incrementBy(long delta) {
                checkArgument(delta >= 0, "counter delta must be >= 0");
                m.inc(delta);
            }
        };
    }

    @Override
    public synchronized Timer0 newTimer(String name, Description desc) {
        checkTimerDescription(name, desc);
        define(name, desc);
        return newTimerImpl(name);
    }

    @Override
    public synchronized <F1> Timer1<F1> newTimer(String name, Description desc, Field<F1> field1) {
        checkTimerDescription(name, desc);
        TimerImpl1<F1> m = new TimerImpl1<>(this, name, desc, field1);
        define(name, desc);
        bucketed.put(name, m);
        return m.timer();
    }

    @Override
    public synchronized <F1, F2> Timer2<F1, F2> newTimer(String name, Description desc, Field<F1> field1,
            Field<F2> field2) {
        checkTimerDescription(name, desc);
        TimerImplN m = new TimerImplN(this, name, desc, field1, field2);
        define(name, desc);
        bucketed.put(name, m);
        return m.timer2();
    }

    @Override
    public synchronized <F1, F2, F3> Timer3<F1, F2, F3> newTimer(String name, Description desc, Field<F1> field1,
            Field<F2> field2, Field<F3> field3) {
        checkTimerDescription(name, desc);
        TimerImplN m = new TimerImplN(this, name, desc, field1, field2, field3);
        define(name, desc);
        bucketed.put(name, m);
        return m.timer3();
    }

    private static void checkTimerDescription(String name, Description desc) {
        checkMetricName(name);
        checkArgument(!desc.isConstant(), "timer must not be constant");
        checkArgument(!desc.isGauge(), "timer must not be a gauge");
        checkArgument(!desc.isRate(), "timer must not be a rate");
        checkArgument(desc.isCumulative(), "timer must be cumulative");
        checkArgument(desc.getTimeUnit() != null, "timer must have a unit");
    }

    TimerImpl newTimerImpl(String name) {
        return new TimerImpl(name, registry.timer(name));
    }

    @Override
    public synchronized Histogram0 newHistogram(String name, Description desc) {
        checkHistogramDescription(name, desc);
        define(name, desc);
        return newHistogramImpl(name);
    }

    @Override
    public synchronized <F1> Histogram1<F1> newHistogram(String name, Description desc, Field<F1> field1) {
        checkHistogramDescription(name, desc);
        HistogramImpl1<F1> m = new HistogramImpl1<>(this, name, desc, field1);
        define(name, desc);
        bucketed.put(name, m);
        return m.histogram1();
    }

    @Override
    public synchronized <F1, F2> Histogram2<F1, F2> newHistogram(String name, Description desc, Field<F1> field1,
            Field<F2> field2) {
        checkHistogramDescription(name, desc);
        HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2);
        define(name, desc);
        bucketed.put(name, m);
        return m.histogram2();
    }

    @Override
    public synchronized <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(String name, Description desc,
            Field<F1> field1, Field<F2> field2, Field<F3> field3) {
        checkHistogramDescription(name, desc);
        HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2, field3);
        define(name, desc);
        bucketed.put(name, m);
        return m.histogram3();
    }

    private static void checkHistogramDescription(String name, Description desc) {
        checkMetricName(name);
        checkArgument(!desc.isConstant(), "histogram must not be constant");
        checkArgument(!desc.isGauge(), "histogram must not be a gauge");
        checkArgument(!desc.isRate(), "histogram must not be a rate");
        checkArgument(desc.isCumulative(), "histogram must be cumulative");
    }

    HistogramImpl newHistogramImpl(String name) {
        return new HistogramImpl(name, registry.histogram(name));
    }

    @Override
    public <V> CallbackMetric0<V> newCallbackMetric(String name, Class<V> valueClass, Description desc) {
        checkMetricName(name);
        define(name, desc);
        return new CallbackMetricImpl0<>(this, registry, name, valueClass);
    }

    @Override
    public <F1, V> CallbackMetric1<F1, V> newCallbackMetric(String name, Class<V> valueClass, Description desc,
            Field<F1> field1) {
        checkMetricName(name);
        CallbackMetricImpl1<F1, V> m = new CallbackMetricImpl1<>(this, registry, name, valueClass, desc, field1);
        define(name, desc);
        bucketed.put(name, m);
        return m.create();
    }

    @Override
    public synchronized RegistrationHandle newTrigger(Set<CallbackMetric<?>> metrics, Runnable trigger) {
        ImmutableSet<CallbackMetricGlue> all = FluentIterable.from(metrics).transform(m -> (CallbackMetricGlue) m)
                .toSet();

        trigger = new CallbackGroup(trigger, all);
        for (CallbackMetricGlue m : all) {
            m.register(trigger);
        }
        trigger.run();

        return new RegistrationHandle() {
            @Override
            public void remove() {
                for (CallbackMetricGlue m : all) {
                    m.remove();
                }
            }
        };
    }

    synchronized void remove(String name) {
        bucketed.remove(name);
        descriptions.remove(name);
    }

    private synchronized void define(String name, Description desc) {
        if (descriptions.containsKey(name)) {
            ImmutableMap<String, String> annotations = descriptions.get(name);
            if (!desc.getAnnotations().get(Description.DESCRIPTION)
                    .equals(annotations.get(Description.DESCRIPTION))) {
                throw new IllegalStateException(String.format("metric %s already defined", name));
            }
        } else {
            descriptions.put(name, desc.getAnnotations());
        }
    }

    private static final Pattern METRIC_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*");

    private static void checkMetricName(String name) {
        checkArgument(METRIC_NAME_PATTERN.matcher(name).matches(), "metric name must match %s",
                METRIC_NAME_PATTERN.pattern());
    }

    static String name(Description.FieldOrdering ordering, String codeName, String fieldValues) {
        if (ordering == FieldOrdering.PREFIX_FIELDS_BASENAME) {
            int s = codeName.lastIndexOf('/');
            if (s > 0) {
                String prefix = codeName.substring(0, s);
                String metric = codeName.substring(s + 1);
                return prefix + '/' + fieldValues + '/' + metric;
            }
        }
        return codeName + '/' + fieldValues;
    }

    abstract class CounterImpl extends Counter0 {
        private final String name;
        final Metric metric;

        CounterImpl(String name, Metric metric) {
            this.name = name;
            this.metric = metric;
        }

        @Override
        public void remove() {
            descriptions.remove(name);
            registry.remove(name);
        }
    }

    class TimerImpl extends Timer0 {
        private final String name;
        final com.codahale.metrics.Timer metric;

        private TimerImpl(String name, com.codahale.metrics.Timer metric) {
            this.name = name;
            this.metric = metric;
        }

        @Override
        public void record(long value, TimeUnit unit) {
            checkArgument(value >= 0, "timer delta must be >= 0");
            metric.update(value, unit);
        }

        @Override
        public void remove() {
            descriptions.remove(name);
            registry.remove(name);
        }
    }

    class HistogramImpl extends Histogram0 {
        private final String name;
        final com.codahale.metrics.Histogram metric;

        private HistogramImpl(String name, com.codahale.metrics.Histogram metric) {
            this.name = name;
            this.metric = metric;
        }

        @Override
        public void record(long value) {
            metric.update(value);
        }

        @Override
        public void remove() {
            descriptions.remove(name);
            registry.remove(name);
        }
    }
}