Support parameterized metrics
Metrics like Counter1 and Timer1 can now be given a field to dynamically allocate submetrics for specific values. Rename the versions with no field Counter0 and Timer0 to indicate they do allocate submetrics. Support Counter1-3 and Timer1-3 for up to 3 dimensional submetrics of either kind. Description.setFieldOrdering() can be used to control how the submetrics are named in DropWizard. The default appends field values to create a new submetric. Another option is offered to insert field values before the last '/', allowing things to be broken down by a slightly different layout. Change-Id: Id2b4f59eb603eb8c8690753650a5720fc26d617b
This commit is contained in:
		@@ -20,24 +20,8 @@ import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
/**
 | 
			
		||||
 * Metric whose value is supplied when the trigger is invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 *   CallbackMetric<Long> hits = metricMaker.newCallbackMetric("hits", ...);
 | 
			
		||||
 *   CallbackMetric<Long> total = metricMaker.newCallbackMetric("total", ...);
 | 
			
		||||
 *   metricMaker.newTrigger(hits, total, new Runnable() {
 | 
			
		||||
 *     public void run() {
 | 
			
		||||
 *       hits.set(1);
 | 
			
		||||
 *       total.set(5);
 | 
			
		||||
 *     }
 | 
			
		||||
 *   });
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @see CallbackMetric0
 | 
			
		||||
 * @param <V> type of the metric value, typically Integer or Long.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class CallbackMetric<V> implements RegistrationHandle {
 | 
			
		||||
  /**
 | 
			
		||||
   * Supply the current value of the metric.
 | 
			
		||||
   *
 | 
			
		||||
   * @param value current value.
 | 
			
		||||
   */
 | 
			
		||||
  public abstract void set(V value);
 | 
			
		||||
public interface CallbackMetric<V> extends RegistrationHandle {
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metric whose value is supplied when the trigger is invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 *   CallbackMetric0<Long> hits = metricMaker.newCallbackMetric("hits", ...);
 | 
			
		||||
 *   CallbackMetric0<Long> total = metricMaker.newCallbackMetric("total", ...);
 | 
			
		||||
 *   metricMaker.newTrigger(hits, total, new Runnable() {
 | 
			
		||||
 *     public void run() {
 | 
			
		||||
 *       hits.set(1);
 | 
			
		||||
 *       total.set(5);
 | 
			
		||||
 *     }
 | 
			
		||||
 *   });
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @param <V> type of the metric value, typically Integer or Long.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class CallbackMetric0<V> implements CallbackMetric<V> {
 | 
			
		||||
  /**
 | 
			
		||||
   * Supply the current value of the metric.
 | 
			
		||||
   *
 | 
			
		||||
   * @param value current value.
 | 
			
		||||
   */
 | 
			
		||||
  public abstract void set(V value);
 | 
			
		||||
}
 | 
			
		||||
@@ -26,7 +26,7 @@ import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 * For an instantaneous read of a value that can change over time
 | 
			
		||||
 * (e.g. "memory in use") use a {@link CallbackMetric}.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Counter implements RegistrationHandle {
 | 
			
		||||
public abstract class Counter0 implements RegistrationHandle {
 | 
			
		||||
  /** Increment the counter by one event. */
 | 
			
		||||
  public void increment() {
 | 
			
		||||
    incrementBy(1);
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metric whose value increments during the life of the process.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Suitable uses are "total requests handled", "bytes sent", etc.
 | 
			
		||||
 * Use {@link Description#setRate()} to suggest the monitoring system
 | 
			
		||||
 * should also track the rate of increments if this is of interest.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * For an instantaneous read of a value that can change over time
 | 
			
		||||
 * (e.g. "memory in use") use a {@link CallbackMetric}.
 | 
			
		||||
 *
 | 
			
		||||
 * @param <F1> type of the field.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Counter1<F1> implements RegistrationHandle {
 | 
			
		||||
  /** Increment the counter by one event. */
 | 
			
		||||
  public void increment(F1 field1) {
 | 
			
		||||
    incrementBy(field1, 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Increment the counter by a specified amount.
 | 
			
		||||
   *
 | 
			
		||||
   * @param field1 bucket to increment.
 | 
			
		||||
   * @param value value to increment by, must be >= 0.
 | 
			
		||||
   */
 | 
			
		||||
  public abstract void incrementBy(F1 field1, long value);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,46 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metric whose value increments during the life of the process.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Suitable uses are "total requests handled", "bytes sent", etc.
 | 
			
		||||
 * Use {@link Description#setRate()} to suggest the monitoring system
 | 
			
		||||
 * should also track the rate of increments if this is of interest.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * For an instantaneous read of a value that can change over time
 | 
			
		||||
 * (e.g. "memory in use") use a {@link CallbackMetric}.
 | 
			
		||||
 *
 | 
			
		||||
 * @param <F1> type of the field.
 | 
			
		||||
 * @param <F2> type of the field.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Counter2<F1, F2> implements RegistrationHandle {
 | 
			
		||||
  /** Increment the counter by one event. */
 | 
			
		||||
  public void increment(F1 field1, F2 field2) {
 | 
			
		||||
    incrementBy(field1, field2, 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Increment the counter by a specified amount.
 | 
			
		||||
   *
 | 
			
		||||
   * @param field1 bucket to increment.
 | 
			
		||||
   * @param field2 bucket to increment.
 | 
			
		||||
   * @param value value to increment by, must be >= 0.
 | 
			
		||||
   */
 | 
			
		||||
  public abstract void incrementBy(F1 field1, F2 field2, long value);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metric whose value increments during the life of the process.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Suitable uses are "total requests handled", "bytes sent", etc.
 | 
			
		||||
 * Use {@link Description#setRate()} to suggest the monitoring system
 | 
			
		||||
 * should also track the rate of increments if this is of interest.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * For an instantaneous read of a value that can change over time
 | 
			
		||||
 * (e.g. "memory in use") use a {@link CallbackMetric}.
 | 
			
		||||
 *
 | 
			
		||||
 * @param <F1> type of the field.
 | 
			
		||||
 * @param <F2> type of the field.
 | 
			
		||||
 * @param <F3> type of the field.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Counter3<F1, F2, F3> implements RegistrationHandle {
 | 
			
		||||
  /** Increment the counter by one event. */
 | 
			
		||||
  public void increment(F1 field1, F2 field2, F3 field3) {
 | 
			
		||||
    incrementBy(field1, field2, field3, 1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Increment the counter by a specified amount.
 | 
			
		||||
   *
 | 
			
		||||
   * @param field1 bucket to increment.
 | 
			
		||||
   * @param field2 bucket to increment.
 | 
			
		||||
   * @param field3 bucket to increment.
 | 
			
		||||
   * @param value value to increment by, must be >= 0.
 | 
			
		||||
   */
 | 
			
		||||
  public abstract void incrementBy(F1 field1, F2 field2, F3 field3, long value);
 | 
			
		||||
}
 | 
			
		||||
@@ -28,6 +28,7 @@ public class Description {
 | 
			
		||||
  public static final String CUMULATIVE = "CUMULATIVE";
 | 
			
		||||
  public static final String RATE = "RATE";
 | 
			
		||||
  public static final String GAUGE = "GAUGE";
 | 
			
		||||
  public static final String FIELD_ORDERING = "FIELD_ORDERING";
 | 
			
		||||
  public static final String TRUE_VALUE = "1";
 | 
			
		||||
 | 
			
		||||
  public static class Units {
 | 
			
		||||
@@ -42,6 +43,19 @@ public class Description {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static enum FieldOrdering {
 | 
			
		||||
    /** Default ordering places fields at end of the parent metric name. */
 | 
			
		||||
    AT_END,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Splits the metric name by inserting field values before the last '/' in
 | 
			
		||||
     * the metric name. For example {@code "plugins/replication/push_latency"}
 | 
			
		||||
     * with a {@code Field.ofString("remote")} will create submetrics named
 | 
			
		||||
     * {@code "plugins/replication/some-server/push_latency"}.
 | 
			
		||||
     */
 | 
			
		||||
    PREFIX_FIELDS_BASENAME;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final Map<String, String> annotations;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -65,7 +79,7 @@ public class Description {
 | 
			
		||||
  /**
 | 
			
		||||
   * Indicates the metric may be usefully interpreted as a count over short
 | 
			
		||||
   * periods of time, such as request arrival rate. May only be applied to a
 | 
			
		||||
   * {@link Counter}.
 | 
			
		||||
   * {@link Counter0}.
 | 
			
		||||
   */
 | 
			
		||||
  public Description setRate() {
 | 
			
		||||
    annotations.put(RATE, TRUE_VALUE);
 | 
			
		||||
@@ -83,7 +97,7 @@ public class Description {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Indicates the metric accumulates over the lifespan of the process. A
 | 
			
		||||
   * {@link Counter} like total requests handled accumulates over the process
 | 
			
		||||
   * {@link Counter0} like total requests handled accumulates over the process
 | 
			
		||||
   * and should be {@code setCumulative()}.
 | 
			
		||||
   */
 | 
			
		||||
  public Description setCumulative() {
 | 
			
		||||
@@ -91,6 +105,12 @@ public class Description {
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Configure how fields are ordered into submetric names. */
 | 
			
		||||
  public Description setFieldOrdering(FieldOrdering ordering) {
 | 
			
		||||
    annotations.put(FIELD_ORDERING, ordering.name());
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** True if the metric may be interpreted as a rate over time. */
 | 
			
		||||
  public boolean isRate() {
 | 
			
		||||
    return TRUE_VALUE.equals(annotations.get(RATE));
 | 
			
		||||
@@ -106,6 +126,12 @@ public class Description {
 | 
			
		||||
    return TRUE_VALUE.equals(annotations.get(CUMULATIVE));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Get the suggested field ordering. */
 | 
			
		||||
  public FieldOrdering getFieldOrdering() {
 | 
			
		||||
    String o = annotations.get(FIELD_ORDERING);
 | 
			
		||||
    return o != null ? FieldOrdering.valueOf(o) : FieldOrdering.AT_END;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Decode the unit as a unit of time.
 | 
			
		||||
   *
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								gerrit-server/src/main/java/com/google/gerrit/metrics/Field.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								gerrit-server/src/main/java/com/google/gerrit/metrics/Field.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import static com.google.common.base.Preconditions.checkArgument;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Function;
 | 
			
		||||
import com.google.common.base.Functions;
 | 
			
		||||
 | 
			
		||||
/** Describes a bucketing field used by a metric. */
 | 
			
		||||
public class Field<T> {
 | 
			
		||||
  /** Break down metrics by boolean true/false. */
 | 
			
		||||
  public static Field<Boolean> ofBoolean(String name) {
 | 
			
		||||
    return ofBoolean(name, null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Break down metrics by boolean true/false. */
 | 
			
		||||
  public static Field<Boolean> ofBoolean(String name, String description) {
 | 
			
		||||
    return new Field<>(name, Boolean.class, description);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Break down metrics by cases of an enum. */
 | 
			
		||||
  public static <E extends Enum<E>> Field<E> ofEnum(Class<E> enumType,
 | 
			
		||||
      String name) {
 | 
			
		||||
    return ofEnum(enumType, name, null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Break down metrics by cases of an enum. */
 | 
			
		||||
  public static <E extends Enum<E>> Field<E> ofEnum(Class<E> enumType,
 | 
			
		||||
      String name, String description) {
 | 
			
		||||
    return new Field<>(name, enumType, description);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Break down metrics by string.
 | 
			
		||||
   * <p>
 | 
			
		||||
   * Each unique string will allocate a new submetric. <b>Do not use user
 | 
			
		||||
   * content as a field value</b> as field values are never reclaimed.
 | 
			
		||||
   */
 | 
			
		||||
  public static Field<String> ofString(String name) {
 | 
			
		||||
    return ofString(name, null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Break down metrics by string.
 | 
			
		||||
   * <p>
 | 
			
		||||
   * Each unique string will allocate a new submetric. <b>Do not use user
 | 
			
		||||
   * content as a field value</b> as field values are never reclaimed.
 | 
			
		||||
   */
 | 
			
		||||
  public static Field<String> ofString(String name, String description) {
 | 
			
		||||
    return new Field<>(name, String.class, description);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Break down metrics by integer.
 | 
			
		||||
   * <p>
 | 
			
		||||
   * Each unique integer will allocate a new submetric. <b>Do not use user
 | 
			
		||||
   * content as a field value</b> as field values are never reclaimed.
 | 
			
		||||
   */
 | 
			
		||||
  public static Field<Integer> ofInteger(String name) {
 | 
			
		||||
    return ofInteger(name, null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Break down metrics by integer.
 | 
			
		||||
   * <p>
 | 
			
		||||
   * Each unique integer will allocate a new submetric. <b>Do not use user
 | 
			
		||||
   * content as a field value</b> as field values are never reclaimed.
 | 
			
		||||
   */
 | 
			
		||||
  public static Field<Integer> ofInteger(String name, String description) {
 | 
			
		||||
    return new Field<>(name, Integer.class, description);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final String name;
 | 
			
		||||
  private final Class<T> keyType;
 | 
			
		||||
  private final Function<T, String> formatter;
 | 
			
		||||
  private final String description;
 | 
			
		||||
 | 
			
		||||
  private Field(String name, Class<T> keyType, String description) {
 | 
			
		||||
    checkArgument(name.matches("^[a-z_]+$"), "name must match [a-z_]");
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.keyType = keyType;
 | 
			
		||||
    this.formatter = initFormatter(keyType);
 | 
			
		||||
    this.description = description;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Name of this field within the metric. */
 | 
			
		||||
  public String getName() {
 | 
			
		||||
    return name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Type of value used within the field. */
 | 
			
		||||
  public Class<T> getType() {
 | 
			
		||||
    return keyType;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Description text for the field explaining its range of values. */
 | 
			
		||||
  public String getDescription() {
 | 
			
		||||
    return description;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public Function<T, String> formatter() {
 | 
			
		||||
    return formatter;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  private static <T> Function<T, String> initFormatter(Class<T> keyType) {
 | 
			
		||||
    if (keyType == String.class) {
 | 
			
		||||
      return (Function<T, String>) Functions.<String> identity();
 | 
			
		||||
 | 
			
		||||
    } else if (keyType == Integer.class || keyType == Boolean.class) {
 | 
			
		||||
      return (Function<T, String>) Functions.toStringFunction();
 | 
			
		||||
 | 
			
		||||
    } else if (Enum.class.isAssignableFrom(keyType)) {
 | 
			
		||||
      return new Function<T, String>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public String apply(T in) {
 | 
			
		||||
          return ((Enum<?>) in).name();
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    throw new IllegalStateException("unsupported type " + keyType.getName());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,10 +23,28 @@ import java.util.Set;
 | 
			
		||||
/** Factory to create metrics for monitoring. */
 | 
			
		||||
public abstract class MetricMaker {
 | 
			
		||||
  /** Metric whose value increments during the life of the process. */
 | 
			
		||||
  public abstract Counter newCounter(String name, Description desc);
 | 
			
		||||
  public abstract Counter0 newCounter(String name, Description desc);
 | 
			
		||||
  public abstract <F1> Counter1<F1> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1);
 | 
			
		||||
  public abstract <F1, F2> Counter2<F1, F2> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2);
 | 
			
		||||
  public abstract <F1, F2, F3> Counter3<F1, F2, F3> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2, Field<F3> field3);
 | 
			
		||||
 | 
			
		||||
  /** Metric recording time spent on an operation. */
 | 
			
		||||
  public abstract Timer newTimer(String name, Description desc);
 | 
			
		||||
  public abstract Timer0 newTimer(String name, Description desc);
 | 
			
		||||
  public abstract <F1> Timer1<F1> newTimer(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1);
 | 
			
		||||
  public abstract <F1, F2> Timer2<F1, F2> newTimer(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2);
 | 
			
		||||
  public abstract <F1, F2, F3> Timer3<F1, F2, F3> newTimer(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2, Field<F3> field3);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Instantaneous reading of a value.
 | 
			
		||||
@@ -50,7 +68,7 @@ public abstract class MetricMaker {
 | 
			
		||||
   */
 | 
			
		||||
  public <V> void newCallbackMetric(String name,
 | 
			
		||||
      Class<V> valueClass, Description desc, final Supplier<V> trigger) {
 | 
			
		||||
    final CallbackMetric<V> metric = newCallbackMetric(name, valueClass, desc);
 | 
			
		||||
    final CallbackMetric0<V> metric = newCallbackMetric(name, valueClass, desc);
 | 
			
		||||
    newTrigger(metric, new Runnable() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void run() {
 | 
			
		||||
@@ -59,9 +77,9 @@ public abstract class MetricMaker {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Instantaneous reading of a particular value. */
 | 
			
		||||
  public abstract <V> CallbackMetric<V> newCallbackMetric(String name,
 | 
			
		||||
      Class<V> valueClass, Description desc);
 | 
			
		||||
  /** Instantaneous reading of a single value. */
 | 
			
		||||
  public abstract <V> CallbackMetric0<V> newCallbackMetric(
 | 
			
		||||
      String name, Class<V> valueClass, Description desc);
 | 
			
		||||
 | 
			
		||||
  /** Connect logic to populate a previously created {@link CallbackMetric}. */
 | 
			
		||||
  public RegistrationHandle newTrigger(CallbackMetric<?> metric1, Runnable trigger) {
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit;
 | 
			
		||||
 * }
 | 
			
		||||
 * </pre>
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Timer implements RegistrationHandle {
 | 
			
		||||
public abstract class Timer0 implements RegistrationHandle {
 | 
			
		||||
  public class Context implements AutoCloseable {
 | 
			
		||||
    private final long startNanos;
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Records elapsed time for an operation or span.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Typical usage in a try-with-resources block:
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * try (Timer1.Context ctx = timer.start(field)) {
 | 
			
		||||
 * }
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @param <F1> type of the field.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Timer1<F1> implements RegistrationHandle {
 | 
			
		||||
  public static class Context implements AutoCloseable {
 | 
			
		||||
    private final Timer1<Object> timer;
 | 
			
		||||
    private final Object field1;
 | 
			
		||||
    private final long startNanos;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    <F1> Context(Timer1<F1> timer, F1 field1) {
 | 
			
		||||
      this.timer = (Timer1<Object>) timer;
 | 
			
		||||
      this.field1 = field1;
 | 
			
		||||
      this.startNanos = System.nanoTime();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
      timer.record(field1, System.nanoTime() - startNanos, NANOSECONDS);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Begin a timer for the current block, value will be recorded when closed. */
 | 
			
		||||
  public Context start(F1 field1) {
 | 
			
		||||
    return new Context(this, field1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Record a value in the distribution. */
 | 
			
		||||
  public abstract void record(F1 field1, long value, TimeUnit unit);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,64 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Records elapsed time for an operation or span.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Typical usage in a try-with-resources block:
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * try (Timer2.Context ctx = timer.start(field)) {
 | 
			
		||||
 * }
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @param <F1> type of the field.
 | 
			
		||||
 * @param <F2> type of the field.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Timer2<F1, F2> implements RegistrationHandle {
 | 
			
		||||
  public static class Context implements AutoCloseable {
 | 
			
		||||
    private final Timer2<Object, Object> timer;
 | 
			
		||||
    private final Object field1;
 | 
			
		||||
    private final Object field2;
 | 
			
		||||
    private final long startNanos;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    <F1, F2> Context(Timer2<F1, F2> timer, F1 field1, F2 field2) {
 | 
			
		||||
      this.timer = (Timer2<Object, Object>) timer;
 | 
			
		||||
      this.field1 = field1;
 | 
			
		||||
      this.field2 = field2;
 | 
			
		||||
      this.startNanos = System.nanoTime();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
      timer.record(field1, field2, System.nanoTime() - startNanos, NANOSECONDS);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Begin a timer for the current block, value will be recorded when closed. */
 | 
			
		||||
  public Context start(F1 field1, F2 field2) {
 | 
			
		||||
    return new Context(this, field1, field2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Record a value in the distribution. */
 | 
			
		||||
  public abstract void record(F1 field1, F2 field2, long value, TimeUnit unit);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,69 @@
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Records elapsed time for an operation or span.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Typical usage in a try-with-resources block:
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * try (Timer3.Context ctx = timer.start(field)) {
 | 
			
		||||
 * }
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @param <F1> type of the field.
 | 
			
		||||
 * @param <F2> type of the field.
 | 
			
		||||
 * @param <F3> type of the field.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Timer3<F1, F2, F3> implements RegistrationHandle {
 | 
			
		||||
  public static class Context implements AutoCloseable {
 | 
			
		||||
    private final Timer3<Object, Object, Object> timer;
 | 
			
		||||
    private final Object field1;
 | 
			
		||||
    private final Object field2;
 | 
			
		||||
    private final Object field3;
 | 
			
		||||
    private final long startNanos;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    <F1, F2, F3> Context(Timer3<F1, F2, F3> timer, F1 f1, F2 f2, F3 f3) {
 | 
			
		||||
      this.timer = (Timer3<Object, Object, Object>) timer;
 | 
			
		||||
      this.field1 = f1;
 | 
			
		||||
      this.field2 = f2;
 | 
			
		||||
      this.field3 = f3;
 | 
			
		||||
      this.startNanos = System.nanoTime();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
      timer.record(field1, field2, field3,
 | 
			
		||||
          System.nanoTime() - startNanos, NANOSECONDS);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Begin a timer for the current block, value will be recorded when closed. */
 | 
			
		||||
  public Context start(F1 field1, F2 field2, F3 field3) {
 | 
			
		||||
    return new Context(this, field1, field2, field3);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Record a value in the distribution. */
 | 
			
		||||
  public abstract void record(F1 field1, F2 field2, F3 field3,
 | 
			
		||||
      long value, TimeUnit unit);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,109 @@
 | 
			
		||||
// 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 com.google.common.base.Function;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.Maps;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker.CounterImpl;
 | 
			
		||||
 | 
			
		||||
import com.codahale.metrics.Metric;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
/** Abstract counter broken down into buckets by {@link Field} values. */
 | 
			
		||||
abstract class BucketedCounter implements BucketedMetric {
 | 
			
		||||
  private final DropWizardMetricMaker metrics;
 | 
			
		||||
  private final String name;
 | 
			
		||||
  private final boolean isRate;
 | 
			
		||||
  private final Description.FieldOrdering ordering;
 | 
			
		||||
  protected final Field<?>[] fields;
 | 
			
		||||
  protected final CounterImpl total;
 | 
			
		||||
  private final Map<Object, CounterImpl> cells;
 | 
			
		||||
 | 
			
		||||
  BucketedCounter(DropWizardMetricMaker metrics,
 | 
			
		||||
      String name, Description desc, Field<?>... fields) {
 | 
			
		||||
    this.metrics = metrics;
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.isRate = desc.isRate();
 | 
			
		||||
    this.ordering = desc.getFieldOrdering();
 | 
			
		||||
    this.fields = fields;
 | 
			
		||||
    this.total = metrics.newCounterImpl(name + "_total", isRate);
 | 
			
		||||
    this.cells = new ConcurrentHashMap<>();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void doRemove() {
 | 
			
		||||
    for (CounterImpl c : cells.values()) {
 | 
			
		||||
      c.remove();
 | 
			
		||||
    }
 | 
			
		||||
    total.remove();
 | 
			
		||||
    metrics.remove(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CounterImpl forceCreate(Object f1, Object f2) {
 | 
			
		||||
    return forceCreate(ImmutableList.of(f1, f2));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CounterImpl forceCreate(Object f1, Object f2, Object f3) {
 | 
			
		||||
    return forceCreate(ImmutableList.of(f1, f2, f3));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CounterImpl forceCreate(Object key) {
 | 
			
		||||
    CounterImpl c = cells.get(key);
 | 
			
		||||
    if (c != null) {
 | 
			
		||||
      return c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    synchronized (cells) {
 | 
			
		||||
      c = cells.get(key);
 | 
			
		||||
      if (c == null) {
 | 
			
		||||
        c = metrics.newCounterImpl(submetric(key), isRate);
 | 
			
		||||
        cells.put(key, c);
 | 
			
		||||
      }
 | 
			
		||||
      return c;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private String submetric(Object key) {
 | 
			
		||||
    return DropWizardMetricMaker.name(ordering, name, name(key));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  abstract String name(Object key);
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Metric getTotal() {
 | 
			
		||||
    return total.metric;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Field<?>[] getFields() {
 | 
			
		||||
    return fields;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Map<Object, Metric> getCells() {
 | 
			
		||||
    return Maps.transformValues(
 | 
			
		||||
        cells,
 | 
			
		||||
        new Function<CounterImpl, Metric> () {
 | 
			
		||||
          @Override
 | 
			
		||||
          public Metric apply(CounterImpl in) {
 | 
			
		||||
            return in.metric;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
// 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 com.google.gerrit.common.Nullable;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
 | 
			
		||||
import com.codahale.metrics.Metric;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/** Metric broken down into buckets by {@link Field} values. */
 | 
			
		||||
interface BucketedMetric extends Metric {
 | 
			
		||||
  @Nullable Metric getTotal();
 | 
			
		||||
  Field<?>[] getFields();
 | 
			
		||||
  Map<?, Metric> getCells();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,107 @@
 | 
			
		||||
// 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 com.google.common.base.Function;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.Maps;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker.TimerImpl;
 | 
			
		||||
 | 
			
		||||
import com.codahale.metrics.Metric;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
/** Abstract timer broken down into buckets by {@link Field} values. */
 | 
			
		||||
abstract class BucketedTimer implements BucketedMetric {
 | 
			
		||||
  private final DropWizardMetricMaker metrics;
 | 
			
		||||
  private final String name;
 | 
			
		||||
  private final Description.FieldOrdering ordering;
 | 
			
		||||
  protected final Field<?>[] fields;
 | 
			
		||||
  protected final TimerImpl total;
 | 
			
		||||
  private final Map<Object, TimerImpl> cells;
 | 
			
		||||
 | 
			
		||||
  BucketedTimer(DropWizardMetricMaker metrics, String name,
 | 
			
		||||
      Description desc, Field<?>... fields) {
 | 
			
		||||
    this.metrics = metrics;
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.ordering = desc.getFieldOrdering();
 | 
			
		||||
    this.fields = fields;
 | 
			
		||||
    this.total = metrics.newTimerImpl(name + "_total");
 | 
			
		||||
    this.cells = new ConcurrentHashMap<>();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void doRemove() {
 | 
			
		||||
    for (TimerImpl c : cells.values()) {
 | 
			
		||||
      c.remove();
 | 
			
		||||
    }
 | 
			
		||||
    total.remove();
 | 
			
		||||
    metrics.remove(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TimerImpl forceCreate(Object f1, Object f2) {
 | 
			
		||||
    return forceCreate(ImmutableList.of(f1, f2));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TimerImpl forceCreate(Object f1, Object f2, Object f3) {
 | 
			
		||||
    return forceCreate(ImmutableList.of(f1, f2, f3));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TimerImpl forceCreate(Object key) {
 | 
			
		||||
    TimerImpl c = cells.get(key);
 | 
			
		||||
    if (c != null) {
 | 
			
		||||
      return c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    synchronized (cells) {
 | 
			
		||||
      c = cells.get(key);
 | 
			
		||||
      if (c == null) {
 | 
			
		||||
        c = metrics.newTimerImpl(submetric(key));
 | 
			
		||||
        cells.put(key, c);
 | 
			
		||||
      }
 | 
			
		||||
      return c;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private String submetric(Object key) {
 | 
			
		||||
    return DropWizardMetricMaker.name(ordering, name, name(key));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  abstract String name(Object key);
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Metric getTotal() {
 | 
			
		||||
    return total.metric;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Field<?>[] getFields() {
 | 
			
		||||
    return fields;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Map<Object, Metric> getCells() {
 | 
			
		||||
    return Maps.transformValues(
 | 
			
		||||
        cells,
 | 
			
		||||
        new Function<TimerImpl, Metric> () {
 | 
			
		||||
          @Override
 | 
			
		||||
          public Metric apply(TimerImpl in) {
 | 
			
		||||
            return in.metric;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
// 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 java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicLong;
 | 
			
		||||
 | 
			
		||||
class CallbackGroup implements Runnable {
 | 
			
		||||
  private static final long PERIOD = TimeUnit.SECONDS.toNanos(2);
 | 
			
		||||
 | 
			
		||||
  private final AtomicLong reloadAt;
 | 
			
		||||
  private final Runnable trigger;
 | 
			
		||||
 | 
			
		||||
  CallbackGroup(Runnable trigger) {
 | 
			
		||||
    this.reloadAt = new AtomicLong(0);
 | 
			
		||||
    this.trigger = trigger;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void run() {
 | 
			
		||||
    if (reload()) {
 | 
			
		||||
      trigger.run();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private boolean reload() {
 | 
			
		||||
    for (;;) {
 | 
			
		||||
      long now = System.nanoTime();
 | 
			
		||||
      long next = reloadAt.get();
 | 
			
		||||
      if (next > now) {
 | 
			
		||||
        return false;
 | 
			
		||||
      } else if (reloadAt.compareAndSet(next, now + PERIOD)) {
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,67 @@
 | 
			
		||||
// 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 com.google.gerrit.metrics.CallbackMetric0;
 | 
			
		||||
 | 
			
		||||
class CallbackMetricImpl0<V> extends CallbackMetric0<V> {
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  static <V> V zeroFor(Class<V> valueClass) {
 | 
			
		||||
    if (valueClass == Integer.class) {
 | 
			
		||||
      return (V) Integer.valueOf(0);
 | 
			
		||||
    } else if (valueClass == Long.class) {
 | 
			
		||||
      return (V) Long.valueOf(0);
 | 
			
		||||
    } else if (valueClass == Double.class) {
 | 
			
		||||
      return (V) Double.valueOf(0);
 | 
			
		||||
    } else if (valueClass == Float.class) {
 | 
			
		||||
      return (V) Float.valueOf(0);
 | 
			
		||||
    } else if (valueClass == String.class) {
 | 
			
		||||
      return (V) "";
 | 
			
		||||
    } else if (valueClass == Boolean.class) {
 | 
			
		||||
      return (V) Boolean.FALSE;
 | 
			
		||||
    } else {
 | 
			
		||||
      throw new IllegalArgumentException("unsupported value type "
 | 
			
		||||
          + valueClass.getName());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final String name;
 | 
			
		||||
  private V value;
 | 
			
		||||
 | 
			
		||||
  CallbackMetricImpl0(String name, Class<V> valueType) {
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.value = zeroFor(valueType);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void set(V value) {
 | 
			
		||||
    this.value = value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void remove() {
 | 
			
		||||
    // Triggers register and remove the metric.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  com.codahale.metrics.Gauge<V> gauge(final Runnable trigger) {
 | 
			
		||||
    return new com.codahale.metrics.Gauge<V>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public V getValue() {
 | 
			
		||||
        trigger.run();
 | 
			
		||||
        return value;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
// 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 com.google.common.base.Function;
 | 
			
		||||
import com.google.gerrit.metrics.Counter1;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
 | 
			
		||||
/** Optimized version of {@link BucketedCounter} for single dimension. */
 | 
			
		||||
class CounterImpl1<F1> extends BucketedCounter {
 | 
			
		||||
  CounterImpl1(DropWizardMetricMaker metrics, String name, Description desc,
 | 
			
		||||
      Field<F1> field1) {
 | 
			
		||||
    super(metrics, name, desc, field1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Counter1<F1> counter() {
 | 
			
		||||
    return new Counter1<F1>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void incrementBy(F1 field1, long value) {
 | 
			
		||||
        total.incrementBy(value);
 | 
			
		||||
        forceCreate(field1).incrementBy(value);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
        doRemove();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  String name(Object field1) {
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    Function<Object, String> fmt =
 | 
			
		||||
        (Function<Object, String>) fields[0].formatter();
 | 
			
		||||
 | 
			
		||||
    return fmt.apply(field1).replace('/', '-');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,75 @@
 | 
			
		||||
// 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 com.google.common.base.Function;
 | 
			
		||||
import com.google.common.base.Joiner;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.gerrit.metrics.Counter2;
 | 
			
		||||
import com.google.gerrit.metrics.Counter3;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
 | 
			
		||||
/** Generalized implementation of N-dimensional counter metrics. */
 | 
			
		||||
class CounterImplN extends BucketedCounter implements BucketedMetric {
 | 
			
		||||
  CounterImplN(DropWizardMetricMaker metrics, String name, Description desc,
 | 
			
		||||
      Field<?>... fields) {
 | 
			
		||||
    super(metrics, name, desc, fields);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  <F1, F2> Counter2<F1, F2> counter2() {
 | 
			
		||||
    return new Counter2<F1, F2>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void incrementBy(F1 field1, F2 field2, long value) {
 | 
			
		||||
        total.incrementBy(value);
 | 
			
		||||
        forceCreate(field1, field2).incrementBy(value);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
        doRemove();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  <F1, F2, F3> Counter3<F1, F2, F3> counter3() {
 | 
			
		||||
    return new Counter3<F1, F2, F3>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void incrementBy(F1 field1, F2 field2, F3 field3, long value) {
 | 
			
		||||
        total.incrementBy(value);
 | 
			
		||||
        forceCreate(field1, field2, field3).incrementBy(value);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
        doRemove();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  @Override
 | 
			
		||||
  String name(Object key) {
 | 
			
		||||
    ImmutableList<Object> keyList = (ImmutableList<Object>) key;
 | 
			
		||||
    String[] parts = new String[fields.length];
 | 
			
		||||
    for (int i = 0; i < fields.length; i++) {
 | 
			
		||||
      Function<Object, String> fmt =
 | 
			
		||||
          (Function<Object, String>) fields[i].formatter();
 | 
			
		||||
 | 
			
		||||
      parts[i] = fmt.apply(keyList.get(i)).replace('/', '-');
 | 
			
		||||
    }
 | 
			
		||||
    return Joiner.on('/').join(parts);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,10 +23,19 @@ 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.Counter;
 | 
			
		||||
import com.google.gerrit.metrics.CallbackMetric0;
 | 
			
		||||
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.Field;
 | 
			
		||||
import com.google.gerrit.metrics.MetricMaker;
 | 
			
		||||
import com.google.gerrit.metrics.Timer;
 | 
			
		||||
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.Description.FieldOrdering;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
import com.google.inject.Scopes;
 | 
			
		||||
import com.google.inject.Singleton;
 | 
			
		||||
@@ -35,10 +44,10 @@ import com.codahale.metrics.Metric;
 | 
			
		||||
import com.codahale.metrics.MetricRegistry;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -62,102 +71,174 @@ public class DropWizardMetricMaker extends MetricMaker {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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.descriptions = new HashMap<>();
 | 
			
		||||
    this.bucketed = new ConcurrentHashMap<>();
 | 
			
		||||
    this.descriptions = new ConcurrentHashMap<>();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Map<String, Metric> getMetricMap() {
 | 
			
		||||
    return registry.getMetrics();
 | 
			
		||||
  Iterable<String> getMetricNames() {
 | 
			
		||||
    return descriptions.keySet();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Get the underlying metric implementation. */
 | 
			
		||||
  public Metric getMetric(String name) {
 | 
			
		||||
    return registry.getMetrics().get(name);
 | 
			
		||||
    Metric m = bucketed.get(name);
 | 
			
		||||
    return m != null ? m : registry.getMetrics().get(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Lookup annotations from a metric's {@link Description}.  */
 | 
			
		||||
  /** Lookup annotations from a metric's {@link Description}. */
 | 
			
		||||
  public ImmutableMap<String, String> getAnnotations(String name) {
 | 
			
		||||
    return descriptions.get(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public synchronized Counter newCounter(String name, Description desc) {
 | 
			
		||||
    checkArgument(!desc.isGauge(), "counters must not be gauge");
 | 
			
		||||
    checkNotDefined(name);
 | 
			
		||||
    descriptions.put(name, desc.getAnnotations());
 | 
			
		||||
  public synchronized Counter0 newCounter(String name, Description desc) {
 | 
			
		||||
    checkCounterDescription(desc);
 | 
			
		||||
    define(name, desc);
 | 
			
		||||
    return newCounterImpl(name, desc.isRate());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    if (desc.isRate()) {
 | 
			
		||||
      final com.codahale.metrics.Meter metric = registry.meter(name);
 | 
			
		||||
      return new CounterImpl(name) {
 | 
			
		||||
  @Override
 | 
			
		||||
  public synchronized <F1> Counter1<F1> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1) {
 | 
			
		||||
    checkCounterDescription(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(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(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(Description desc) {
 | 
			
		||||
    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");
 | 
			
		||||
          metric.mark(delta);
 | 
			
		||||
          m.mark(delta);
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    } else {
 | 
			
		||||
      final com.codahale.metrics.Counter metric = registry.counter(name);
 | 
			
		||||
      return new CounterImpl(name) {
 | 
			
		||||
      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");
 | 
			
		||||
          metric.inc(delta);
 | 
			
		||||
          m.inc(delta);
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public synchronized Timer newTimer(final String name, Description desc) {
 | 
			
		||||
  public synchronized Timer0 newTimer(String name, Description desc) {
 | 
			
		||||
    checkTimerDescription(desc);
 | 
			
		||||
    define(name, desc);
 | 
			
		||||
    return newTimerImpl(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public synchronized <F1> Timer1<F1> newTimer(String name, Description desc, Field<F1> field1) {
 | 
			
		||||
    checkTimerDescription(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(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(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(Description desc) {
 | 
			
		||||
    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");
 | 
			
		||||
    checkNotDefined(name);
 | 
			
		||||
    descriptions.put(name, desc.getAnnotations());
 | 
			
		||||
 | 
			
		||||
    final com.codahale.metrics.Timer metric = registry.timer(name);
 | 
			
		||||
    return new Timer() {
 | 
			
		||||
      @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);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unused")
 | 
			
		||||
  TimerImpl newTimerImpl(String name) {
 | 
			
		||||
    return new TimerImpl(name, registry.timer(name));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <V> CallbackMetric<V> newCallbackMetric(String name,
 | 
			
		||||
      Class<V> valueClass, Description desc) {
 | 
			
		||||
    checkNotDefined(name);
 | 
			
		||||
    descriptions.put(name, desc.getAnnotations());
 | 
			
		||||
    return new CallbackMetricImpl<V>(name, valueClass);
 | 
			
		||||
  public <V> CallbackMetric0<V> newCallbackMetric(
 | 
			
		||||
      String name, Class<V> valueClass, Description desc) {
 | 
			
		||||
    define(name, desc);
 | 
			
		||||
    return new CallbackMetricImpl0<>(name, valueClass);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public synchronized RegistrationHandle newTrigger(
 | 
			
		||||
      Set<CallbackMetric<?>> metrics, Runnable trigger) {
 | 
			
		||||
    if (metrics.size() > 1) {
 | 
			
		||||
      trigger = new CallbackGroup(trigger);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (CallbackMetric<?> m : metrics) {
 | 
			
		||||
      checkNotDefined(((CallbackMetricImpl<?>) m).name);
 | 
			
		||||
      CallbackMetricImpl0<?> metric = (CallbackMetricImpl0<?>) m;
 | 
			
		||||
      if (registry.getMetrics().containsKey(metric.name)) {
 | 
			
		||||
        throw new IllegalStateException(String.format(
 | 
			
		||||
            "metric %s already configured", metric.name));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final List<String> names = new ArrayList<>(metrics.size());
 | 
			
		||||
    for (CallbackMetric<?> m : metrics) {
 | 
			
		||||
      CallbackMetricImpl<?> metric = (CallbackMetricImpl<?>) m;
 | 
			
		||||
      CallbackMetricImpl0<?> metric = (CallbackMetricImpl0<?>) m;
 | 
			
		||||
      registry.register(metric.name, metric.gauge(trigger));
 | 
			
		||||
      names.add(metric.name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return new RegistrationHandle() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
@@ -169,18 +250,40 @@ public class DropWizardMetricMaker extends MetricMaker {
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void checkNotDefined(String name) {
 | 
			
		||||
    if (registry.getNames().contains(name)) {
 | 
			
		||||
  synchronized void remove(String name) {
 | 
			
		||||
    bucketed.remove(name);
 | 
			
		||||
    descriptions.remove(name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private synchronized void define(String name, Description desc) {
 | 
			
		||||
    if (descriptions.containsKey(name)) {
 | 
			
		||||
      throw new IllegalStateException(String.format(
 | 
			
		||||
          "metric %s already defined", name));
 | 
			
		||||
    }
 | 
			
		||||
    descriptions.put(name, desc.getAnnotations());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private abstract class CounterImpl extends Counter {
 | 
			
		||||
    private final String name;
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    CounterImpl(String name) {
 | 
			
		||||
  abstract class CounterImpl extends Counter0 {
 | 
			
		||||
    private final String name;
 | 
			
		||||
    final Metric metric;
 | 
			
		||||
 | 
			
		||||
    CounterImpl(String name, Metric metric) {
 | 
			
		||||
      this.name = name;
 | 
			
		||||
      this.metric = metric;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -190,50 +293,25 @@ public class DropWizardMetricMaker extends MetricMaker {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static class CallbackMetricImpl<V> extends CallbackMetric<V> {
 | 
			
		||||
  class TimerImpl extends Timer0 {
 | 
			
		||||
    private final String name;
 | 
			
		||||
    private V value;
 | 
			
		||||
    final com.codahale.metrics.Timer metric;
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    CallbackMetricImpl(String name, Class<V> valueClass) {
 | 
			
		||||
    private TimerImpl(String name, com.codahale.metrics.Timer metric) {
 | 
			
		||||
      this.name = name;
 | 
			
		||||
 | 
			
		||||
      if (valueClass == Integer.class) {
 | 
			
		||||
        value = (V) Integer.valueOf(0);
 | 
			
		||||
      } else if (valueClass == Long.class) {
 | 
			
		||||
        value = (V) Long.valueOf(0);
 | 
			
		||||
      } else if (valueClass == Double.class) {
 | 
			
		||||
        value = (V) Double.valueOf(0);
 | 
			
		||||
      } else if (valueClass == Float.class) {
 | 
			
		||||
        value = (V) Float.valueOf(0);
 | 
			
		||||
      } else if (valueClass == String.class) {
 | 
			
		||||
        value = (V) "";
 | 
			
		||||
      } else if (valueClass == Boolean.class) {
 | 
			
		||||
        value = (V) Boolean.FALSE;
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new IllegalArgumentException("unsupported value type "
 | 
			
		||||
            + valueClass.getName());
 | 
			
		||||
      }
 | 
			
		||||
      this.metric = metric;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void set(V value) {
 | 
			
		||||
      this.value = value;
 | 
			
		||||
    public void record(long value, TimeUnit unit) {
 | 
			
		||||
      checkArgument(value >= 0, "timer delta must be >= 0");
 | 
			
		||||
      metric.update(value, unit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void remove() {
 | 
			
		||||
      // Triggers register and remove the metric.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    com.codahale.metrics.Gauge<V> gauge(final Runnable trigger) {
 | 
			
		||||
      return new com.codahale.metrics.Gauge<V>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public V getValue() {
 | 
			
		||||
          trigger.run();
 | 
			
		||||
          return value;
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
      descriptions.remove(name);
 | 
			
		||||
      registry.remove(name);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,14 +55,12 @@ class ListMetrics implements RestReadView<ConfigResource> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SortedMap<String, MetricJson> out = new TreeMap<>();
 | 
			
		||||
    Map<String, Metric> all = metrics.getMetricMap();
 | 
			
		||||
 | 
			
		||||
    List<String> prefixes = new ArrayList<>(query.size());
 | 
			
		||||
    for (String q : query) {
 | 
			
		||||
      if (q.endsWith("/")) {
 | 
			
		||||
        prefixes.add(q);
 | 
			
		||||
      } else {
 | 
			
		||||
        Metric m = all.get(q);
 | 
			
		||||
        Metric m = metrics.getMetric(q);
 | 
			
		||||
        if (m != null) {
 | 
			
		||||
          out.put(q, toJson(q, m));
 | 
			
		||||
        }
 | 
			
		||||
@@ -70,10 +68,9 @@ class ListMetrics implements RestReadView<ConfigResource> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (query.isEmpty() || !prefixes.isEmpty()) {
 | 
			
		||||
      for (Map.Entry<String, Metric> e : all.entrySet()) {
 | 
			
		||||
        String name = e.getKey();
 | 
			
		||||
      for (String name : metrics.getMetricNames()) {
 | 
			
		||||
        if (include(prefixes, name)) {
 | 
			
		||||
          out.put(name, toJson(name, e.getValue()));
 | 
			
		||||
          out.put(name, toJson(name, metrics.getMetric(name)));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,11 @@
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.metrics.dropwizard;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Function;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.ImmutableMap;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
 | 
			
		||||
import com.codahale.metrics.Counter;
 | 
			
		||||
import com.codahale.metrics.Gauge;
 | 
			
		||||
@@ -24,6 +27,11 @@ import com.codahale.metrics.Metric;
 | 
			
		||||
import com.codahale.metrics.Snapshot;
 | 
			
		||||
import com.codahale.metrics.Timer;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.TreeMap;
 | 
			
		||||
 | 
			
		||||
class MetricJson {
 | 
			
		||||
  String description;
 | 
			
		||||
  String unit;
 | 
			
		||||
@@ -50,6 +58,9 @@ class MetricJson {
 | 
			
		||||
  Double max;
 | 
			
		||||
  Double std_dev;
 | 
			
		||||
 | 
			
		||||
  List<FieldJson> fields;
 | 
			
		||||
  Map<String, Object> buckets;
 | 
			
		||||
 | 
			
		||||
  MetricJson(Metric metric, ImmutableMap<String, String> atts, boolean dataOnly) {
 | 
			
		||||
    if (!dataOnly) {
 | 
			
		||||
      description = atts.get(Description.DESCRIPTION);
 | 
			
		||||
@@ -62,7 +73,20 @@ class MetricJson {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void init(Metric metric, ImmutableMap<String, String> atts) {
 | 
			
		||||
    if (metric instanceof Counter) {
 | 
			
		||||
    if (metric instanceof BucketedMetric) {
 | 
			
		||||
      BucketedMetric m = (BucketedMetric) metric;
 | 
			
		||||
      if (m.getTotal() != null) {
 | 
			
		||||
        init(m.getTotal(), atts);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Field<?>[] fieldList = m.getFields();
 | 
			
		||||
      fields = new ArrayList<>(fieldList.length);
 | 
			
		||||
      for (Field<?> f : fieldList) {
 | 
			
		||||
        fields.add(new FieldJson(f));
 | 
			
		||||
      }
 | 
			
		||||
      buckets = makeBuckets(fieldList, m.getCells(), atts);
 | 
			
		||||
 | 
			
		||||
    } else if (metric instanceof Counter) {
 | 
			
		||||
      Counter c = (Counter) metric;
 | 
			
		||||
      count = c.getCount();
 | 
			
		||||
 | 
			
		||||
@@ -103,4 +127,61 @@ class MetricJson {
 | 
			
		||||
  private static Boolean toBool(ImmutableMap<String, String> atts, String key) {
 | 
			
		||||
    return Description.TRUE_VALUE.equals(atts.get(key)) ? true : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  private static Map<String, Object> makeBuckets(
 | 
			
		||||
      Field<?>[] fields,
 | 
			
		||||
      Map<?, Metric> metrics,
 | 
			
		||||
      ImmutableMap<String, String> atts) {
 | 
			
		||||
    if (fields.length == 1) {
 | 
			
		||||
      Function<Object, String> fmt =
 | 
			
		||||
          (Function<Object, String>) fields[0].formatter();
 | 
			
		||||
      Map<String, Object> out = new TreeMap<>();
 | 
			
		||||
      for (Map.Entry<?, Metric> e : metrics.entrySet()) {
 | 
			
		||||
        out.put(
 | 
			
		||||
            fmt.apply(e.getKey()),
 | 
			
		||||
            new MetricJson(e.getValue(), atts, true));
 | 
			
		||||
      }
 | 
			
		||||
      return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Map<String, Object> out = new TreeMap<>();
 | 
			
		||||
    for (Map.Entry<?, Metric> e : metrics.entrySet()) {
 | 
			
		||||
      ImmutableList<Object> keys = (ImmutableList<Object>) e.getKey();
 | 
			
		||||
      Map<String, Object> dst = out;
 | 
			
		||||
 | 
			
		||||
      for (int i = 0; i < fields.length - 1; i++) {
 | 
			
		||||
        Function<Object, String> fmt =
 | 
			
		||||
            (Function<Object, String>) fields[i].formatter();
 | 
			
		||||
        String key = fmt.apply(keys.get(i));
 | 
			
		||||
        Map<String, Object> t = (Map<String, Object>) dst.get(key);
 | 
			
		||||
        if (t == null) {
 | 
			
		||||
          t = new TreeMap<>();
 | 
			
		||||
          dst.put(key, t);
 | 
			
		||||
        }
 | 
			
		||||
        dst = t;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Function<Object, String> fmt =
 | 
			
		||||
          (Function<Object, String>) fields[fields.length - 1].formatter();
 | 
			
		||||
      dst.put(
 | 
			
		||||
          fmt.apply(keys.get(fields.length - 1)),
 | 
			
		||||
          new MetricJson(e.getValue(), atts, true));
 | 
			
		||||
    }
 | 
			
		||||
    return out;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static class FieldJson {
 | 
			
		||||
    String name;
 | 
			
		||||
    String type;
 | 
			
		||||
    String description;
 | 
			
		||||
 | 
			
		||||
    FieldJson(Field<?> field) {
 | 
			
		||||
      this.name = field.getName();
 | 
			
		||||
      this.description = field.getDescription();
 | 
			
		||||
      this.type = Enum.class.isAssignableFrom(field.getType())
 | 
			
		||||
          ? field.getType().getSimpleName()
 | 
			
		||||
          : null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,54 @@
 | 
			
		||||
// 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 com.google.common.base.Function;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
import com.google.gerrit.metrics.Timer1;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/** Optimized version of {@link BucketedTimer} for single dimension. */
 | 
			
		||||
class TimerImpl1<F1> extends BucketedTimer implements BucketedMetric {
 | 
			
		||||
  TimerImpl1(DropWizardMetricMaker metrics, String name,
 | 
			
		||||
      Description desc, Field<F1> field1) {
 | 
			
		||||
    super(metrics, name, desc, field1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Timer1<F1> timer() {
 | 
			
		||||
    return new Timer1<F1>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void record(F1 field1, long value, TimeUnit unit) {
 | 
			
		||||
        total.record(value, unit);
 | 
			
		||||
        forceCreate(field1).record(value, unit);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
        doRemove();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  String name(Object field1) {
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    Function<Object, String> fmt =
 | 
			
		||||
        (Function<Object, String>) fields[0].formatter();
 | 
			
		||||
 | 
			
		||||
    return fmt.apply(field1).replace('/', '-');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,78 @@
 | 
			
		||||
// 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 com.google.common.base.Function;
 | 
			
		||||
import com.google.common.base.Joiner;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
import com.google.gerrit.metrics.Timer2;
 | 
			
		||||
import com.google.gerrit.metrics.Timer3;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/** Generalized implementation of N-dimensional timer metrics. */
 | 
			
		||||
class TimerImplN extends BucketedTimer implements BucketedMetric {
 | 
			
		||||
  TimerImplN(DropWizardMetricMaker metrics, String name,
 | 
			
		||||
      Description desc, Field<?>... fields) {
 | 
			
		||||
    super(metrics, name, desc, fields);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  <F1, F2> Timer2<F1, F2> timer2() {
 | 
			
		||||
    return new Timer2<F1, F2>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void record(F1 field1, F2 field2, long value, TimeUnit unit) {
 | 
			
		||||
        total.record(value, unit);
 | 
			
		||||
        forceCreate(field1, field2).record(value, unit);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
        doRemove();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  <F1, F2, F3> Timer3<F1, F2, F3> timer3() {
 | 
			
		||||
    return new Timer3<F1, F2, F3>() {
 | 
			
		||||
      @Override
 | 
			
		||||
      public void record(F1 field1, F2 field2, F3 field3,
 | 
			
		||||
          long value, TimeUnit unit) {
 | 
			
		||||
        total.record(value, unit);
 | 
			
		||||
        forceCreate(field1, field2, field3).record(value, unit);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @Override
 | 
			
		||||
      public void remove() {
 | 
			
		||||
        doRemove();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @SuppressWarnings("unchecked")
 | 
			
		||||
  @Override
 | 
			
		||||
  String name(Object key) {
 | 
			
		||||
    ImmutableList<Object> keyList = (ImmutableList<Object>) key;
 | 
			
		||||
    String[] parts = new String[fields.length];
 | 
			
		||||
    for (int i = 0; i < fields.length; i++) {
 | 
			
		||||
      Function<Object, String> fmt =
 | 
			
		||||
          (Function<Object, String>) fields[i].formatter();
 | 
			
		||||
 | 
			
		||||
      parts[i] = fmt.apply(keyList.get(i)).replace('/', '-');
 | 
			
		||||
    }
 | 
			
		||||
    return Joiner.on('/').join(parts);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -14,8 +14,9 @@
 | 
			
		||||
 | 
			
		||||
package com.google.gerrit.server.git;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.metrics.Counter;
 | 
			
		||||
import com.google.gerrit.metrics.Counter1;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.Field;
 | 
			
		||||
import com.google.gerrit.metrics.MetricMaker;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
import com.google.inject.Singleton;
 | 
			
		||||
@@ -25,7 +26,12 @@ import org.eclipse.jgit.transport.PostUploadHook;
 | 
			
		||||
 | 
			
		||||
@Singleton
 | 
			
		||||
public class UploadPackMetricsHook implements PostUploadHook {
 | 
			
		||||
  private final Counter upload;
 | 
			
		||||
  enum Operation {
 | 
			
		||||
    CLONE,
 | 
			
		||||
    FETCH;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final Counter1<Operation> upload;
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  UploadPackMetricsHook(MetricMaker metricMaker) {
 | 
			
		||||
@@ -33,11 +39,17 @@ public class UploadPackMetricsHook implements PostUploadHook {
 | 
			
		||||
        "git/upload-pack",
 | 
			
		||||
        new Description("Total number of git-upload-pack requests")
 | 
			
		||||
          .setRate()
 | 
			
		||||
          .setUnit("requests"));
 | 
			
		||||
          .setUnit("requests"),
 | 
			
		||||
        Field.ofEnum(Operation.class, "operation"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void onPostUpload(PackStatistics stats) {
 | 
			
		||||
    upload.increment();
 | 
			
		||||
    Operation op = Operation.FETCH;
 | 
			
		||||
    if (stats.getUninterestingObjects() == null
 | 
			
		||||
        || stats.getUninterestingObjects().isEmpty()) {
 | 
			
		||||
      op = Operation.CLONE;
 | 
			
		||||
    }
 | 
			
		||||
    upload.increment(op);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,18 @@ package com.google.gerrit.server.plugins;
 | 
			
		||||
import com.google.gerrit.extensions.events.LifecycleListener;
 | 
			
		||||
import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
			
		||||
import com.google.gerrit.metrics.CallbackMetric;
 | 
			
		||||
import com.google.gerrit.metrics.Counter;
 | 
			
		||||
import com.google.gerrit.metrics.CallbackMetric0;
 | 
			
		||||
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.Field;
 | 
			
		||||
import com.google.gerrit.metrics.MetricMaker;
 | 
			
		||||
import com.google.gerrit.metrics.Timer;
 | 
			
		||||
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 java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
@@ -39,23 +47,79 @@ class PluginMetricMaker extends MetricMaker implements LifecycleListener {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Counter newCounter(String name, Description desc) {
 | 
			
		||||
    Counter m = root.newCounter(prefix + name, desc);
 | 
			
		||||
  public Counter0 newCounter(String name, Description desc) {
 | 
			
		||||
    Counter0 m = root.newCounter(prefix + name, desc);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Timer newTimer(String name, Description desc) {
 | 
			
		||||
    Timer m = root.newTimer(prefix + name, desc);
 | 
			
		||||
  public <F1> Counter1<F1> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1) {
 | 
			
		||||
    Counter1<F1> m = root.newCounter(prefix + name, desc, field1);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <V> CallbackMetric<V> newCallbackMetric(String name,
 | 
			
		||||
      Class<V> valueClass, Description desc) {
 | 
			
		||||
    CallbackMetric<V> m = root.newCallbackMetric(prefix + name, valueClass, desc);
 | 
			
		||||
  public <F1, F2> Counter2<F1, F2> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2) {
 | 
			
		||||
    Counter2<F1, F2> m = root.newCounter(prefix + name, desc, field1, field2);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <F1, F2, F3> Counter3<F1, F2, F3> newCounter(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2, Field<F3> field3) {
 | 
			
		||||
    Counter3<F1, F2, F3> m =
 | 
			
		||||
        root.newCounter(prefix + name, desc, field1, field2, field3);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Timer0 newTimer(String name, Description desc) {
 | 
			
		||||
    Timer0 m = root.newTimer(prefix + name, desc);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <F1> Timer1<F1> newTimer(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1) {
 | 
			
		||||
    Timer1<F1> m = root.newTimer(prefix + name, desc, field1);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <F1, F2> Timer2<F1, F2> newTimer(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2) {
 | 
			
		||||
    Timer2<F1, F2> m = root.newTimer(prefix + name, desc, field1, field2);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <F1, F2, F3> Timer3<F1, F2, F3> newTimer(
 | 
			
		||||
      String name, Description desc,
 | 
			
		||||
      Field<F1> field1, Field<F2> field2, Field<F3> field3) {
 | 
			
		||||
    Timer3<F1, F2, F3> m =
 | 
			
		||||
        root.newTimer(prefix + name, desc, field1, field2, field3);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public <V> CallbackMetric0<V> newCallbackMetric(
 | 
			
		||||
      String name, Class<V> valueClass, Description desc) {
 | 
			
		||||
    CallbackMetric0<V> m = root.newCallbackMetric(prefix + name, valueClass, desc);
 | 
			
		||||
    cleanup.add(m);
 | 
			
		||||
    return m;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import com.google.common.collect.Ordering;
 | 
			
		||||
import com.google.gerrit.common.data.GlobalCapability;
 | 
			
		||||
import com.google.gerrit.metrics.Description;
 | 
			
		||||
import com.google.gerrit.metrics.MetricMaker;
 | 
			
		||||
import com.google.gerrit.metrics.Timer;
 | 
			
		||||
import com.google.gerrit.metrics.Timer0;
 | 
			
		||||
import com.google.gerrit.reviewdb.server.ReviewDb;
 | 
			
		||||
import com.google.gerrit.server.CurrentUser;
 | 
			
		||||
import com.google.gerrit.server.index.IndexConfig;
 | 
			
		||||
@@ -122,7 +122,7 @@ public class QueryProcessor {
 | 
			
		||||
      List<Predicate<ChangeData>> queries)
 | 
			
		||||
      throws OrmException, QueryParseException {
 | 
			
		||||
    @SuppressWarnings("resource")
 | 
			
		||||
    Timer.Context context = metrics.executionTime.start();
 | 
			
		||||
    Timer0.Context context = metrics.executionTime.start();
 | 
			
		||||
 | 
			
		||||
    Predicate<ChangeData> visibleToMe = enforceVisibility
 | 
			
		||||
        ? new IsVisibleToPredicate(db, changeControlFactory, userProvider.get())
 | 
			
		||||
@@ -217,7 +217,7 @@ public class QueryProcessor {
 | 
			
		||||
 | 
			
		||||
  @Singleton
 | 
			
		||||
  static class Metrics {
 | 
			
		||||
    final Timer executionTime;
 | 
			
		||||
    final Timer0 executionTime;
 | 
			
		||||
 | 
			
		||||
    @Inject
 | 
			
		||||
    Metrics(MetricMaker metricMaker) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user