LCOV - code coverage report
Current view: top level - metrics/dropwizard - DropWizardMetricMaker.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 165 196 84.2 %
Date: 2022-11-19 15:00:39 Functions: 41 52 78.8 %

          Line data    Source code
       1             : // Copyright (C) 2015 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.metrics.dropwizard;
      16             : 
      17             : import static com.google.common.base.Preconditions.checkArgument;
      18             : import static com.google.gerrit.metrics.dropwizard.MetricResource.METRIC_KIND;
      19             : import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND;
      20             : 
      21             : import com.codahale.metrics.Histogram;
      22             : import com.codahale.metrics.Metric;
      23             : import com.codahale.metrics.MetricRegistry;
      24             : import com.codahale.metrics.Timer;
      25             : import com.google.common.collect.FluentIterable;
      26             : import com.google.common.collect.ImmutableMap;
      27             : import com.google.common.collect.ImmutableSet;
      28             : import com.google.gerrit.extensions.registration.DynamicMap;
      29             : import com.google.gerrit.extensions.registration.RegistrationHandle;
      30             : import com.google.gerrit.extensions.restapi.RestApiModule;
      31             : import com.google.gerrit.metrics.CallbackMetric;
      32             : import com.google.gerrit.metrics.CallbackMetric0;
      33             : import com.google.gerrit.metrics.CallbackMetric1;
      34             : import com.google.gerrit.metrics.Counter0;
      35             : import com.google.gerrit.metrics.Counter1;
      36             : import com.google.gerrit.metrics.Counter2;
      37             : import com.google.gerrit.metrics.Counter3;
      38             : import com.google.gerrit.metrics.Description;
      39             : import com.google.gerrit.metrics.Description.FieldOrdering;
      40             : import com.google.gerrit.metrics.Field;
      41             : import com.google.gerrit.metrics.Histogram0;
      42             : import com.google.gerrit.metrics.Histogram1;
      43             : import com.google.gerrit.metrics.Histogram2;
      44             : import com.google.gerrit.metrics.Histogram3;
      45             : import com.google.gerrit.metrics.MetricMaker;
      46             : import com.google.gerrit.metrics.MetricsReservoirConfig;
      47             : import com.google.gerrit.metrics.Timer0;
      48             : import com.google.gerrit.metrics.Timer1;
      49             : import com.google.gerrit.metrics.Timer2;
      50             : import com.google.gerrit.metrics.Timer3;
      51             : import com.google.gerrit.metrics.proc.JGitMetricModule;
      52             : import com.google.gerrit.metrics.proc.ProcMetricModule;
      53             : import com.google.gerrit.server.cache.CacheMetrics;
      54             : import com.google.gerrit.server.config.MetricsReservoirConfigImpl;
      55             : import com.google.inject.Inject;
      56             : import com.google.inject.Scopes;
      57             : import com.google.inject.Singleton;
      58             : import java.util.Map;
      59             : import java.util.Optional;
      60             : import java.util.Set;
      61             : import java.util.concurrent.ConcurrentHashMap;
      62             : import java.util.concurrent.TimeUnit;
      63             : import java.util.regex.Pattern;
      64             : 
      65             : /**
      66             :  * Connects Gerrit metric package onto DropWizard.
      67             :  *
      68             :  * @see <a href="http://www.dropwizard.io/">DropWizard</a>
      69             :  */
      70             : @Singleton
      71             : public class DropWizardMetricMaker extends MetricMaker {
      72             :   public static class ApiModule extends RestApiModule {
      73             :     private final Optional<MetricsReservoirConfig> metricsReservoirConfig;
      74             : 
      75           1 :     public ApiModule(MetricsReservoirConfig metricsReservoirConfig) {
      76           1 :       this.metricsReservoirConfig = Optional.of(metricsReservoirConfig);
      77           1 :     }
      78             : 
      79          15 :     public ApiModule() {
      80          15 :       this.metricsReservoirConfig = Optional.empty();
      81          15 :     }
      82             : 
      83             :     @Override
      84             :     protected void configure() {
      85          16 :       if (metricsReservoirConfig.isPresent()) {
      86           1 :         bind(MetricsReservoirConfig.class).toInstance(metricsReservoirConfig.get());
      87             :       } else {
      88          15 :         bind(MetricsReservoirConfig.class)
      89          15 :             .to(MetricsReservoirConfigImpl.class)
      90          15 :             .in(Scopes.SINGLETON);
      91             :       }
      92          16 :       bind(MetricRegistry.class).in(Scopes.SINGLETON);
      93          16 :       bind(DropWizardMetricMaker.class).in(Scopes.SINGLETON);
      94          16 :       bind(MetricMaker.class).to(DropWizardMetricMaker.class);
      95             : 
      96          16 :       install(new ProcMetricModule());
      97          16 :       install(new JGitMetricModule());
      98          16 :     }
      99             :   }
     100             : 
     101         138 :   public static class RestModule extends RestApiModule {
     102             :     @Override
     103             :     protected void configure() {
     104         138 :       DynamicMap.mapOf(binder(), METRIC_KIND);
     105         138 :       child(CONFIG_KIND, "metrics").to(MetricsCollection.class);
     106         138 :       get(METRIC_KIND).to(GetMetric.class);
     107         138 :       bind(CacheMetrics.class);
     108         138 :     }
     109             :   }
     110             : 
     111             :   private final MetricRegistry registry;
     112             :   private final Map<String, BucketedMetric> bucketed;
     113             :   private final Map<String, ImmutableMap<String, String>> descriptions;
     114             :   private final MetricsReservoirConfig reservoirConfig;
     115             : 
     116             :   @Inject
     117         140 :   DropWizardMetricMaker(MetricRegistry registry, MetricsReservoirConfig reservoirConfig) {
     118         140 :     this.registry = registry;
     119         140 :     this.bucketed = new ConcurrentHashMap<>();
     120         140 :     this.descriptions = new ConcurrentHashMap<>();
     121         140 :     this.reservoirConfig = reservoirConfig;
     122         140 :   }
     123             : 
     124             :   Iterable<String> getMetricNames() {
     125           0 :     return descriptions.keySet();
     126             :   }
     127             : 
     128             :   /** Get the underlying metric implementation. */
     129             :   public Metric getMetric(String name) {
     130           0 :     Metric m = bucketed.get(name);
     131           0 :     return m != null ? m : registry.getMetrics().get(name);
     132             :   }
     133             : 
     134             :   /** Lookup annotations from a metric's {@link Description}. */
     135             :   public ImmutableMap<String, String> getAnnotations(String name) {
     136           0 :     return descriptions.get(name);
     137             :   }
     138             : 
     139             :   @Override
     140             :   public synchronized Counter0 newCounter(String name, Description desc) {
     141          16 :     checkCounterDescription(name, desc);
     142          16 :     define(name, desc);
     143          16 :     return newCounterImpl(name, desc.isRate());
     144             :   }
     145             : 
     146             :   @Override
     147             :   public synchronized <F1> Counter1<F1> newCounter(
     148             :       String name, Description desc, Field<F1> field1) {
     149          16 :     checkCounterDescription(name, desc);
     150          16 :     CounterImpl1<F1> m = new CounterImpl1<>(this, name, desc, field1);
     151          16 :     define(name, desc);
     152          16 :     bucketed.put(name, m);
     153          16 :     return m.counter();
     154             :   }
     155             : 
     156             :   @Override
     157             :   public synchronized <F1, F2> Counter2<F1, F2> newCounter(
     158             :       String name, Description desc, Field<F1> field1, Field<F2> field2) {
     159          15 :     checkCounterDescription(name, desc);
     160          15 :     CounterImplN m = new CounterImplN(this, name, desc, field1, field2);
     161          15 :     define(name, desc);
     162          15 :     bucketed.put(name, m);
     163          15 :     return m.counter2();
     164             :   }
     165             : 
     166             :   @Override
     167             :   public synchronized <F1, F2, F3> Counter3<F1, F2, F3> newCounter(
     168             :       String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
     169          15 :     checkCounterDescription(name, desc);
     170          15 :     CounterImplN m = new CounterImplN(this, name, desc, field1, field2, field3);
     171          15 :     define(name, desc);
     172          15 :     bucketed.put(name, m);
     173          15 :     return m.counter3();
     174             :   }
     175             : 
     176             :   private static void checkCounterDescription(String name, Description desc) {
     177          16 :     checkMetricName(name);
     178          16 :     checkArgument(!desc.isConstant(), "counter must not be constant");
     179          16 :     checkArgument(!desc.isGauge(), "counter must not be gauge");
     180          16 :   }
     181             : 
     182             :   CounterImpl newCounterImpl(String name, boolean isRate) {
     183          16 :     if (isRate) {
     184          15 :       final com.codahale.metrics.Meter m = registry.meter(name);
     185          15 :       return new CounterImpl(name, m) {
     186             :         @Override
     187             :         public void incrementBy(long delta) {
     188          15 :           checkArgument(delta >= 0, "counter delta must be >= 0");
     189          15 :           m.mark(delta);
     190          15 :         }
     191             :       };
     192             :     }
     193          16 :     final com.codahale.metrics.Counter m = registry.counter(name);
     194          16 :     return new CounterImpl(name, m) {
     195             :       @Override
     196             :       public void incrementBy(long delta) {
     197          16 :         checkArgument(delta >= 0, "counter delta must be >= 0");
     198          16 :         m.inc(delta);
     199          16 :       }
     200             :     };
     201             :   }
     202             : 
     203             :   @Override
     204             :   public synchronized Timer0 newTimer(String name, Description desc) {
     205          16 :     checkTimerDescription(name, desc);
     206          16 :     define(name, desc);
     207          16 :     return newTimerImpl(name);
     208             :   }
     209             : 
     210             :   @Override
     211             :   public synchronized <F1> Timer1<F1> newTimer(String name, Description desc, Field<F1> field1) {
     212          15 :     checkTimerDescription(name, desc);
     213          15 :     TimerImpl1<F1> m = new TimerImpl1<>(this, name, desc, field1);
     214          15 :     define(name, desc);
     215          15 :     bucketed.put(name, m);
     216          15 :     return m.timer();
     217             :   }
     218             : 
     219             :   @Override
     220             :   public synchronized <F1, F2> Timer2<F1, F2> newTimer(
     221             :       String name, Description desc, Field<F1> field1, Field<F2> field2) {
     222          15 :     checkTimerDescription(name, desc);
     223          15 :     TimerImplN m = new TimerImplN(this, name, desc, field1, field2);
     224          15 :     define(name, desc);
     225          15 :     bucketed.put(name, m);
     226          15 :     return m.timer2();
     227             :   }
     228             : 
     229             :   @Override
     230             :   public synchronized <F1, F2, F3> Timer3<F1, F2, F3> newTimer(
     231             :       String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
     232          15 :     checkTimerDescription(name, desc);
     233          15 :     TimerImplN m = new TimerImplN(this, name, desc, field1, field2, field3);
     234          15 :     define(name, desc);
     235          15 :     bucketed.put(name, m);
     236          15 :     return m.timer3();
     237             :   }
     238             : 
     239             :   private static void checkTimerDescription(String name, Description desc) {
     240          16 :     checkMetricName(name);
     241          16 :     checkArgument(!desc.isConstant(), "timer must not be constant");
     242          16 :     checkArgument(!desc.isGauge(), "timer must not be a gauge");
     243          16 :     checkArgument(!desc.isRate(), "timer must not be a rate");
     244          16 :     checkArgument(desc.isCumulative(), "timer must be cumulative");
     245          16 :     checkArgument(desc.getTimeUnit() != null, "timer must have a unit");
     246          16 :   }
     247             : 
     248             :   TimerImpl newTimerImpl(String name) {
     249          16 :     return new TimerImpl(
     250             :         name,
     251          16 :         registry.timer(name, () -> new Timer(DropWizardReservoirProvider.get(reservoirConfig))));
     252             :   }
     253             : 
     254             :   @Override
     255             :   public synchronized Histogram0 newHistogram(String name, Description desc) {
     256           0 :     checkHistogramDescription(name, desc);
     257           0 :     define(name, desc);
     258           0 :     return newHistogramImpl(name);
     259             :   }
     260             : 
     261             :   @Override
     262             :   public synchronized <F1> Histogram1<F1> newHistogram(
     263             :       String name, Description desc, Field<F1> field1) {
     264          15 :     checkHistogramDescription(name, desc);
     265          15 :     HistogramImpl1<F1> m = new HistogramImpl1<>(this, name, desc, field1);
     266          15 :     define(name, desc);
     267          15 :     bucketed.put(name, m);
     268          15 :     return m.histogram1();
     269             :   }
     270             : 
     271             :   @Override
     272             :   public synchronized <F1, F2> Histogram2<F1, F2> newHistogram(
     273             :       String name, Description desc, Field<F1> field1, Field<F2> field2) {
     274           0 :     checkHistogramDescription(name, desc);
     275           0 :     HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2);
     276           0 :     define(name, desc);
     277           0 :     bucketed.put(name, m);
     278           0 :     return m.histogram2();
     279             :   }
     280             : 
     281             :   @Override
     282             :   public synchronized <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(
     283             :       String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
     284           0 :     checkHistogramDescription(name, desc);
     285           0 :     HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2, field3);
     286           0 :     define(name, desc);
     287           0 :     bucketed.put(name, m);
     288           0 :     return m.histogram3();
     289             :   }
     290             : 
     291             :   private static void checkHistogramDescription(String name, Description desc) {
     292          15 :     checkMetricName(name);
     293          15 :     checkArgument(!desc.isConstant(), "histogram must not be constant");
     294          15 :     checkArgument(!desc.isGauge(), "histogram must not be a gauge");
     295          15 :     checkArgument(!desc.isRate(), "histogram must not be a rate");
     296          15 :     checkArgument(desc.isCumulative(), "histogram must be cumulative");
     297          15 :   }
     298             : 
     299             :   HistogramImpl newHistogramImpl(String name) {
     300          15 :     return new HistogramImpl(
     301             :         name,
     302          15 :         registry.histogram(
     303          15 :             name, () -> new Histogram(DropWizardReservoirProvider.get(reservoirConfig))));
     304             :   }
     305             : 
     306             :   @Override
     307             :   public <V> CallbackMetric0<V> newCallbackMetric(
     308             :       String name, Class<V> valueClass, Description desc) {
     309          16 :     checkMetricName(name);
     310          16 :     define(name, desc);
     311          16 :     return new CallbackMetricImpl0<>(this, registry, name, valueClass);
     312             :   }
     313             : 
     314             :   @Override
     315             :   public <F1, V> CallbackMetric1<F1, V> newCallbackMetric(
     316             :       String name, Class<V> valueClass, Description desc, Field<F1> field1) {
     317          16 :     checkMetricName(name);
     318          16 :     CallbackMetricImpl1<F1, V> m =
     319             :         new CallbackMetricImpl1<>(this, registry, name, valueClass, desc, field1);
     320          16 :     define(name, desc);
     321          16 :     bucketed.put(name, m);
     322          16 :     return m.create();
     323             :   }
     324             : 
     325             :   @Override
     326             :   public synchronized RegistrationHandle newTrigger(
     327             :       Set<CallbackMetric<?>> metrics, Runnable trigger) {
     328          16 :     ImmutableSet<CallbackMetricGlue> all =
     329          16 :         FluentIterable.from(metrics).transform(m -> (CallbackMetricGlue) m).toSet();
     330             : 
     331          16 :     trigger = new CallbackGroup(trigger, all);
     332          16 :     for (CallbackMetricGlue m : all) {
     333          16 :       m.register(trigger);
     334          16 :     }
     335          16 :     trigger.run();
     336             : 
     337          16 :     return () -> all.forEach(CallbackMetricGlue::remove);
     338             :   }
     339             : 
     340             :   synchronized void remove(String name) {
     341           0 :     bucketed.remove(name);
     342           0 :     descriptions.remove(name);
     343           0 :   }
     344             : 
     345             :   private synchronized void define(String name, Description desc) {
     346          17 :     if (descriptions.containsKey(name)) {
     347          15 :       ImmutableMap<String, String> annotations = descriptions.get(name);
     348          15 :       if (!desc.getAnnotations()
     349          15 :           .get(Description.DESCRIPTION)
     350          15 :           .equals(annotations.get(Description.DESCRIPTION))) {
     351           0 :         throw new IllegalStateException(String.format("metric '%s' already defined", name));
     352             :       }
     353          15 :     } else {
     354          17 :       descriptions.put(name, desc.getAnnotations());
     355             :     }
     356          17 :   }
     357             : 
     358         140 :   private static final Pattern METRIC_NAME_PATTERN =
     359         140 :       Pattern.compile("[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*");
     360             : 
     361             :   private static void checkMetricName(String name) {
     362          17 :     checkArgument(
     363          17 :         METRIC_NAME_PATTERN.matcher(name).matches(),
     364             :         "invalid metric name '%s': must match pattern '%s'",
     365             :         name,
     366          17 :         METRIC_NAME_PATTERN.pattern());
     367          17 :   }
     368             : 
     369             :   @Override
     370             :   public String sanitizeMetricName(String name) {
     371          16 :     if (METRIC_NAME_PATTERN.matcher(name).matches()) {
     372          15 :       return name;
     373             :     }
     374             : 
     375           1 :     String first = name.substring(0, 1).replaceFirst("[^\\w-]", "_");
     376           1 :     if (name.length() == 1) {
     377           0 :       return first;
     378             :     }
     379             : 
     380           1 :     String result = first + name.substring(1).replaceAll("/[/]+", "/").replaceAll("[^\\w-/]", "_");
     381             : 
     382           1 :     if (result.endsWith("/")) {
     383           1 :       result = result.substring(0, result.length() - 1);
     384             :     }
     385             : 
     386           1 :     return result;
     387             :   }
     388             : 
     389             :   static String name(Description.FieldOrdering ordering, String codeName, String fieldValues) {
     390          16 :     if (ordering == FieldOrdering.PREFIX_FIELDS_BASENAME) {
     391           1 :       int s = codeName.lastIndexOf('/');
     392           1 :       if (s > 0) {
     393           1 :         String prefix = codeName.substring(0, s);
     394           1 :         String metric = codeName.substring(s + 1);
     395           1 :         return prefix + '/' + fieldValues + '/' + metric;
     396             :       }
     397             :     }
     398          16 :     return codeName + '/' + fieldValues;
     399             :   }
     400             : 
     401             :   abstract class CounterImpl extends Counter0 {
     402             :     private final String name;
     403             :     final Metric metric;
     404             : 
     405          16 :     CounterImpl(String name, Metric metric) {
     406          16 :       this.name = name;
     407          16 :       this.metric = metric;
     408          16 :     }
     409             : 
     410             :     @Override
     411             :     public void remove() {
     412           0 :       descriptions.remove(name);
     413           0 :       registry.remove(name);
     414           0 :     }
     415             :   }
     416             : 
     417             :   class TimerImpl extends Timer0 {
     418             :     final com.codahale.metrics.Timer metric;
     419             : 
     420          16 :     private TimerImpl(String name, com.codahale.metrics.Timer metric) {
     421          16 :       super(name);
     422          16 :       this.metric = metric;
     423          16 :     }
     424             : 
     425             :     @Override
     426             :     protected void doRecord(long value, TimeUnit unit) {
     427          15 :       checkArgument(value >= 0, "timer delta must be >= 0");
     428          15 :       metric.update(value, unit);
     429          15 :     }
     430             : 
     431             :     @Override
     432             :     public void remove() {
     433           0 :       descriptions.remove(name);
     434           0 :       registry.remove(name);
     435           0 :     }
     436             :   }
     437             : 
     438             :   class HistogramImpl extends Histogram0 {
     439             :     private final String name;
     440             :     final com.codahale.metrics.Histogram metric;
     441             : 
     442          15 :     private HistogramImpl(String name, com.codahale.metrics.Histogram metric) {
     443          15 :       this.name = name;
     444          15 :       this.metric = metric;
     445          15 :     }
     446             : 
     447             :     @Override
     448             :     public void record(long value) {
     449           7 :       metric.update(value);
     450           7 :     }
     451             : 
     452             :     @Override
     453             :     public void remove() {
     454           0 :       descriptions.remove(name);
     455           0 :       registry.remove(name);
     456           0 :     }
     457             :   }
     458             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750