Extract metrics package from gerrit-server top-level directory

Change-Id: I7c21b82de289ebc24b530d6b37abf6c33b4dec2f
This commit is contained in:
David Ostrovsky
2017-08-22 23:02:19 +02:00
committed by Dave Borowitz
parent bd3f50e61b
commit 1f78f3efa1
58 changed files with 62 additions and 30 deletions

View File

@@ -0,0 +1,15 @@
java_library(
name = "dropwizard",
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//gerrit-server:server",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/metrics",
"//lib:args4j",
"//lib:guava",
"//lib/dropwizard:dropwizard-core",
"//lib/guice",
],
)

View File

@@ -0,0 +1,145 @@
// 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.codahale.metrics.Gauge;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
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 java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** Abstract callback metric broken down into buckets. */
abstract class BucketedCallback<V> implements BucketedMetric {
private final DropWizardMetricMaker metrics;
private final MetricRegistry registry;
private final String name;
private final Description.FieldOrdering ordering;
protected final Field<?>[] fields;
private final V zero;
private final Map<Object, ValueGauge> cells;
protected volatile Runnable trigger;
private final Object lock = new Object();
BucketedCallback(
DropWizardMetricMaker metrics,
MetricRegistry registry,
String name,
Class<V> valueType,
Description desc,
Field<?>... fields) {
this.metrics = metrics;
this.registry = registry;
this.name = name;
this.ordering = desc.getFieldOrdering();
this.fields = fields;
this.zero = CallbackMetricImpl0.zeroFor(valueType);
this.cells = new ConcurrentHashMap<>();
}
void doRemove() {
for (Object key : cells.keySet()) {
registry.remove(submetric(key));
}
metrics.remove(name);
}
void doBeginSet() {
for (ValueGauge g : cells.values()) {
g.set = false;
}
}
void doPrune() {
Iterator<Map.Entry<Object, ValueGauge>> i = cells.entrySet().iterator();
while (i.hasNext()) {
if (!i.next().getValue().set) {
i.remove();
}
}
}
void doEndSet() {
for (ValueGauge g : cells.values()) {
if (!g.set) {
g.value = zero;
}
}
}
ValueGauge getOrCreate(Object f1, Object f2) {
return getOrCreate(ImmutableList.of(f1, f2));
}
ValueGauge getOrCreate(Object f1, Object f2, Object f3) {
return getOrCreate(ImmutableList.of(f1, f2, f3));
}
ValueGauge getOrCreate(Object key) {
ValueGauge c = cells.get(key);
if (c != null) {
return c;
}
synchronized (lock) {
c = cells.get(key);
if (c == null) {
c = new ValueGauge();
registry.register(submetric(key), c);
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 null;
}
@Override
public Field<?>[] getFields() {
return fields;
}
@Override
public Map<Object, Metric> getCells() {
return Maps.transformValues(cells, in -> (Metric) in);
}
final class ValueGauge implements Gauge<V> {
volatile V value = zero;
boolean set;
@Override
public V getValue() {
Runnable t = trigger;
if (t != null) {
t.run();
}
return value;
}
}
}

View File

@@ -0,0 +1,100 @@
// 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.codahale.metrics.Metric;
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 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;
private final Object lock = new Object();
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 (lock) {
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, c -> c.metric);
}
}

View File

@@ -0,0 +1,98 @@
// 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.codahale.metrics.Metric;
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.HistogramImpl;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** Abstract histogram broken down into buckets by {@link Field} values. */
abstract class BucketedHistogram implements BucketedMetric {
private final DropWizardMetricMaker metrics;
private final String name;
private final Description.FieldOrdering ordering;
protected final Field<?>[] fields;
protected final HistogramImpl total;
private final Map<Object, HistogramImpl> cells;
private final Object lock = new Object();
BucketedHistogram(
DropWizardMetricMaker metrics, String name, Description desc, Field<?>... fields) {
this.metrics = metrics;
this.name = name;
this.ordering = desc.getFieldOrdering();
this.fields = fields;
this.total = metrics.newHistogramImpl(name + "_total");
this.cells = new ConcurrentHashMap<>();
}
void doRemove() {
for (HistogramImpl c : cells.values()) {
c.remove();
}
total.remove();
metrics.remove(name);
}
HistogramImpl forceCreate(Object f1, Object f2) {
return forceCreate(ImmutableList.of(f1, f2));
}
HistogramImpl forceCreate(Object f1, Object f2, Object f3) {
return forceCreate(ImmutableList.of(f1, f2, f3));
}
HistogramImpl forceCreate(Object key) {
HistogramImpl c = cells.get(key);
if (c != null) {
return c;
}
synchronized (lock) {
c = cells.get(key);
if (c == null) {
c = metrics.newHistogramImpl(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, h -> h.metric);
}
}

View File

@@ -0,0 +1,30 @@
// 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.codahale.metrics.Metric;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.metrics.Field;
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();
}

View File

@@ -0,0 +1,97 @@
// 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.codahale.metrics.Metric;
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 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;
private final Object lock = new Object();
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 (lock) {
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, t -> t.metric);
}
}

View File

@@ -0,0 +1,68 @@
// 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.collect.ImmutableSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* Run a user specified trigger only once every 2 seconds.
*
* <p>This allows the same Runnable trigger to be applied to several metrics. When a recorder is
* sampling the related metrics only the first access will perform recomputation. Reading other
* related metrics will rely on the already set values for the next several seconds.
*/
class CallbackGroup implements Runnable {
private static final long PERIOD = TimeUnit.SECONDS.toNanos(2);
private final AtomicLong reloadAt;
private final Runnable trigger;
private final ImmutableSet<CallbackMetricGlue> metrics;
private final Object reloadLock = new Object();
CallbackGroup(Runnable trigger, ImmutableSet<CallbackMetricGlue> metrics) {
this.reloadAt = new AtomicLong(0);
this.trigger = trigger;
this.metrics = metrics;
}
@Override
public void run() {
if (reload()) {
synchronized (reloadLock) {
for (CallbackMetricGlue m : metrics) {
m.beginSet();
}
trigger.run();
for (CallbackMetricGlue m : metrics) {
m.endSet();
}
}
}
}
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;
}
}
}
}

View File

@@ -0,0 +1,25 @@
// 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;
interface CallbackMetricGlue {
void beginSet();
void endSet();
void register(Runnable trigger);
void remove();
}

View File

@@ -0,0 +1,82 @@
// 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.codahale.metrics.MetricRegistry;
import com.google.gerrit.metrics.CallbackMetric0;
class CallbackMetricImpl0<V> extends CallbackMetric0<V> implements CallbackMetricGlue {
@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());
}
}
private final DropWizardMetricMaker metrics;
private final MetricRegistry registry;
private final String name;
private volatile V value;
CallbackMetricImpl0(
DropWizardMetricMaker metrics, MetricRegistry registry, String name, Class<V> valueType) {
this.metrics = metrics;
this.registry = registry;
this.name = name;
this.value = zeroFor(valueType);
}
@Override
public void beginSet() {}
@Override
public void endSet() {}
@Override
public void set(V value) {
this.value = value;
}
@Override
public void remove() {
metrics.remove(name);
registry.remove(name);
}
@Override
public void register(Runnable trigger) {
registry.register(
name,
new com.codahale.metrics.Gauge<V>() {
@Override
public V getValue() {
trigger.run();
return value;
}
});
}
}

View File

@@ -0,0 +1,85 @@
// 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.codahale.metrics.MetricRegistry;
import com.google.common.base.Function;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
/** Optimized version of {@link BucketedCallback} for single dimension. */
class CallbackMetricImpl1<F1, V> extends BucketedCallback<V> {
CallbackMetricImpl1(
DropWizardMetricMaker metrics,
MetricRegistry registry,
String name,
Class<V> valueClass,
Description desc,
Field<F1> field1) {
super(metrics, registry, name, valueClass, desc, field1);
}
CallbackMetric1<F1, V> create() {
return new Impl1();
}
private final class Impl1 extends CallbackMetric1<F1, V> implements CallbackMetricGlue {
@Override
public void beginSet() {
doBeginSet();
}
@Override
public void set(F1 field1, V value) {
BucketedCallback<V>.ValueGauge cell = getOrCreate(field1);
cell.value = value;
cell.set = true;
}
@Override
public void prune() {
doPrune();
}
@Override
public void endSet() {
doEndSet();
}
@Override
public void forceCreate(F1 field1) {
getOrCreate(field1);
}
@Override
public void register(Runnable t) {
trigger = t;
}
@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('/', '-');
}
}

View File

@@ -0,0 +1,50 @@
// 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('/', '-');
}
}

View File

@@ -0,0 +1,73 @@
// 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);
}
}

View File

@@ -0,0 +1,416 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.metrics.dropwizard;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.metrics.dropwizard.MetricResource.METRIC_KIND;
import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.metrics.CallbackMetric;
import com.google.gerrit.metrics.CallbackMetric0;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Counter0;
import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Counter2;
import com.google.gerrit.metrics.Counter3;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.FieldOrdering;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.Histogram0;
import com.google.gerrit.metrics.Histogram1;
import com.google.gerrit.metrics.Histogram2;
import com.google.gerrit.metrics.Histogram3;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.metrics.Timer1;
import com.google.gerrit.metrics.Timer2;
import com.google.gerrit.metrics.Timer3;
import com.google.gerrit.metrics.proc.JGitMetricModule;
import com.google.gerrit.metrics.proc.ProcMetricModule;
import com.google.gerrit.server.cache.CacheMetrics;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* Connects Gerrit metric package onto DropWizard.
*
* @see <a href="http://www.dropwizard.io/">DropWizard</a>
*/
@Singleton
public class DropWizardMetricMaker extends MetricMaker {
public static class ApiModule extends RestApiModule {
@Override
protected void configure() {
bind(MetricRegistry.class).in(Scopes.SINGLETON);
bind(DropWizardMetricMaker.class).in(Scopes.SINGLETON);
bind(MetricMaker.class).to(DropWizardMetricMaker.class);
install(new ProcMetricModule());
install(new JGitMetricModule());
}
}
public static class RestModule extends RestApiModule {
@Override
protected void configure() {
DynamicMap.mapOf(binder(), METRIC_KIND);
child(CONFIG_KIND, "metrics").to(MetricsCollection.class);
get(METRIC_KIND).to(GetMetric.class);
bind(CacheMetrics.class);
}
}
private final MetricRegistry registry;
private final Map<String, BucketedMetric> bucketed;
private final Map<String, ImmutableMap<String, String>> descriptions;
@Inject
DropWizardMetricMaker(MetricRegistry registry) {
this.registry = registry;
this.bucketed = new ConcurrentHashMap<>();
this.descriptions = new ConcurrentHashMap<>();
}
Iterable<String> getMetricNames() {
return descriptions.keySet();
}
/** Get the underlying metric implementation. */
public Metric getMetric(String name) {
Metric m = bucketed.get(name);
return m != null ? m : registry.getMetrics().get(name);
}
/** Lookup annotations from a metric's {@link Description}. */
public ImmutableMap<String, String> getAnnotations(String name) {
return descriptions.get(name);
}
@Override
public synchronized Counter0 newCounter(String name, Description desc) {
checkCounterDescription(name, desc);
define(name, desc);
return newCounterImpl(name, desc.isRate());
}
@Override
public synchronized <F1> Counter1<F1> newCounter(
String name, Description desc, Field<F1> field1) {
checkCounterDescription(name, desc);
CounterImpl1<F1> m = new CounterImpl1<>(this, name, desc, field1);
define(name, desc);
bucketed.put(name, m);
return m.counter();
}
@Override
public synchronized <F1, F2> Counter2<F1, F2> newCounter(
String name, Description desc, Field<F1> field1, Field<F2> field2) {
checkCounterDescription(name, desc);
CounterImplN m = new CounterImplN(this, name, desc, field1, field2);
define(name, desc);
bucketed.put(name, m);
return m.counter2();
}
@Override
public synchronized <F1, F2, F3> Counter3<F1, F2, F3> newCounter(
String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
checkCounterDescription(name, desc);
CounterImplN m = new CounterImplN(this, name, desc, field1, field2, field3);
define(name, desc);
bucketed.put(name, m);
return m.counter3();
}
private static void checkCounterDescription(String name, Description desc) {
checkMetricName(name);
checkArgument(!desc.isConstant(), "counters must not be constant");
checkArgument(!desc.isGauge(), "counters must not be gauge");
}
CounterImpl newCounterImpl(String name, boolean isRate) {
if (isRate) {
final com.codahale.metrics.Meter m = registry.meter(name);
return new CounterImpl(name, m) {
@Override
public void incrementBy(long delta) {
checkArgument(delta >= 0, "counter delta must be >= 0");
m.mark(delta);
}
};
}
final com.codahale.metrics.Counter m = registry.counter(name);
return new CounterImpl(name, m) {
@Override
public void incrementBy(long delta) {
checkArgument(delta >= 0, "counter delta must be >= 0");
m.inc(delta);
}
};
}
@Override
public synchronized Timer0 newTimer(String name, Description desc) {
checkTimerDescription(name, desc);
define(name, desc);
return newTimerImpl(name);
}
@Override
public synchronized <F1> Timer1<F1> newTimer(String name, Description desc, Field<F1> field1) {
checkTimerDescription(name, desc);
TimerImpl1<F1> m = new TimerImpl1<>(this, name, desc, field1);
define(name, desc);
bucketed.put(name, m);
return m.timer();
}
@Override
public synchronized <F1, F2> Timer2<F1, F2> newTimer(
String name, Description desc, Field<F1> field1, Field<F2> field2) {
checkTimerDescription(name, desc);
TimerImplN m = new TimerImplN(this, name, desc, field1, field2);
define(name, desc);
bucketed.put(name, m);
return m.timer2();
}
@Override
public synchronized <F1, F2, F3> Timer3<F1, F2, F3> newTimer(
String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
checkTimerDescription(name, desc);
TimerImplN m = new TimerImplN(this, name, desc, field1, field2, field3);
define(name, desc);
bucketed.put(name, m);
return m.timer3();
}
private static void checkTimerDescription(String name, Description desc) {
checkMetricName(name);
checkArgument(!desc.isConstant(), "timer must not be constant");
checkArgument(!desc.isGauge(), "timer must not be a gauge");
checkArgument(!desc.isRate(), "timer must not be a rate");
checkArgument(desc.isCumulative(), "timer must be cumulative");
checkArgument(desc.getTimeUnit() != null, "timer must have a unit");
}
TimerImpl newTimerImpl(String name) {
return new TimerImpl(name, registry.timer(name));
}
@Override
public synchronized Histogram0 newHistogram(String name, Description desc) {
checkHistogramDescription(name, desc);
define(name, desc);
return newHistogramImpl(name);
}
@Override
public synchronized <F1> Histogram1<F1> newHistogram(
String name, Description desc, Field<F1> field1) {
checkHistogramDescription(name, desc);
HistogramImpl1<F1> m = new HistogramImpl1<>(this, name, desc, field1);
define(name, desc);
bucketed.put(name, m);
return m.histogram1();
}
@Override
public synchronized <F1, F2> Histogram2<F1, F2> newHistogram(
String name, Description desc, Field<F1> field1, Field<F2> field2) {
checkHistogramDescription(name, desc);
HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2);
define(name, desc);
bucketed.put(name, m);
return m.histogram2();
}
@Override
public synchronized <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(
String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
checkHistogramDescription(name, desc);
HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2, field3);
define(name, desc);
bucketed.put(name, m);
return m.histogram3();
}
private static void checkHistogramDescription(String name, Description desc) {
checkMetricName(name);
checkArgument(!desc.isConstant(), "histogram must not be constant");
checkArgument(!desc.isGauge(), "histogram must not be a gauge");
checkArgument(!desc.isRate(), "histogram must not be a rate");
checkArgument(desc.isCumulative(), "histogram must be cumulative");
}
HistogramImpl newHistogramImpl(String name) {
return new HistogramImpl(name, registry.histogram(name));
}
@Override
public <V> CallbackMetric0<V> newCallbackMetric(
String name, Class<V> valueClass, Description desc) {
checkMetricName(name);
define(name, desc);
return new CallbackMetricImpl0<>(this, registry, name, valueClass);
}
@Override
public <F1, V> CallbackMetric1<F1, V> newCallbackMetric(
String name, Class<V> valueClass, Description desc, Field<F1> field1) {
checkMetricName(name);
CallbackMetricImpl1<F1, V> m =
new CallbackMetricImpl1<>(this, registry, name, valueClass, desc, field1);
define(name, desc);
bucketed.put(name, m);
return m.create();
}
@Override
public synchronized RegistrationHandle newTrigger(
Set<CallbackMetric<?>> metrics, Runnable trigger) {
ImmutableSet<CallbackMetricGlue> all =
FluentIterable.from(metrics).transform(m -> (CallbackMetricGlue) m).toSet();
trigger = new CallbackGroup(trigger, all);
for (CallbackMetricGlue m : all) {
m.register(trigger);
}
trigger.run();
return new RegistrationHandle() {
@Override
public void remove() {
for (CallbackMetricGlue m : all) {
m.remove();
}
}
};
}
synchronized void remove(String name) {
bucketed.remove(name);
descriptions.remove(name);
}
private synchronized void define(String name, Description desc) {
if (descriptions.containsKey(name)) {
ImmutableMap<String, String> annotations = descriptions.get(name);
if (!desc.getAnnotations()
.get(Description.DESCRIPTION)
.equals(annotations.get(Description.DESCRIPTION))) {
throw new IllegalStateException(String.format("metric %s already defined", name));
}
} else {
descriptions.put(name, desc.getAnnotations());
}
}
private static final Pattern METRIC_NAME_PATTERN =
Pattern.compile("[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*");
private static void checkMetricName(String name) {
checkArgument(
METRIC_NAME_PATTERN.matcher(name).matches(),
"metric name must match %s",
METRIC_NAME_PATTERN.pattern());
}
static String name(Description.FieldOrdering ordering, String codeName, String fieldValues) {
if (ordering == FieldOrdering.PREFIX_FIELDS_BASENAME) {
int s = codeName.lastIndexOf('/');
if (s > 0) {
String prefix = codeName.substring(0, s);
String metric = codeName.substring(s + 1);
return prefix + '/' + fieldValues + '/' + metric;
}
}
return codeName + '/' + fieldValues;
}
abstract class CounterImpl extends Counter0 {
private final String name;
final Metric metric;
CounterImpl(String name, Metric metric) {
this.name = name;
this.metric = metric;
}
@Override
public void remove() {
descriptions.remove(name);
registry.remove(name);
}
}
class TimerImpl extends Timer0 {
private final String name;
final com.codahale.metrics.Timer metric;
private TimerImpl(String name, com.codahale.metrics.Timer metric) {
this.name = name;
this.metric = metric;
}
@Override
public void record(long value, TimeUnit unit) {
checkArgument(value >= 0, "timer delta must be >= 0");
metric.update(value, unit);
}
@Override
public void remove() {
descriptions.remove(name);
registry.remove(name);
}
}
class HistogramImpl extends Histogram0 {
private final String name;
final com.codahale.metrics.Histogram metric;
private HistogramImpl(String name, com.codahale.metrics.Histogram metric) {
this.name = name;
this.metric = metric;
}
@Override
public void record(long value) {
metric.update(value);
}
@Override
public void remove() {
descriptions.remove(name);
registry.remove(name);
}
}
}

View File

@@ -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.dropwizard;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import org.kohsuke.args4j.Option;
class GetMetric implements RestReadView<MetricResource> {
private final PermissionBackend permissionBackend;
private final CurrentUser user;
private final DropWizardMetricMaker metrics;
@Option(name = "--data-only", usage = "return only values")
boolean dataOnly;
@Inject
GetMetric(PermissionBackend permissionBackend, CurrentUser user, DropWizardMetricMaker metrics) {
this.permissionBackend = permissionBackend;
this.user = user;
this.metrics = metrics;
}
@Override
public MetricJson apply(MetricResource resource)
throws AuthException, PermissionBackendException {
permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
return new MetricJson(
resource.getMetric(), metrics.getAnnotations(resource.getName()), dataOnly);
}
}

View File

@@ -0,0 +1,50 @@
// 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.Histogram1;
/** Optimized version of {@link BucketedHistogram} for single dimension. */
class HistogramImpl1<F1> extends BucketedHistogram implements BucketedMetric {
HistogramImpl1(DropWizardMetricMaker metrics, String name, Description desc, Field<F1> field1) {
super(metrics, name, desc, field1);
}
Histogram1<F1> histogram1() {
return new Histogram1<F1>() {
@Override
public void record(F1 field1, long value) {
total.record(value);
forceCreate(field1).record(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('/', '-');
}
}

View File

@@ -0,0 +1,73 @@
// 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.Histogram2;
import com.google.gerrit.metrics.Histogram3;
/** Generalized implementation of N-dimensional Histogram metrics. */
class HistogramImplN extends BucketedHistogram implements BucketedMetric {
HistogramImplN(DropWizardMetricMaker metrics, String name, Description desc, Field<?>... fields) {
super(metrics, name, desc, fields);
}
<F1, F2> Histogram2<F1, F2> histogram2() {
return new Histogram2<F1, F2>() {
@Override
public void record(F1 field1, F2 field2, long value) {
total.record(value);
forceCreate(field1, field2).record(value);
}
@Override
public void remove() {
doRemove();
}
};
}
<F1, F2, F3> Histogram3<F1, F2, F3> histogram3() {
return new Histogram3<F1, F2, F3>() {
@Override
public void record(F1 field1, F2 field2, F3 field3, long value) {
total.record(value);
forceCreate(field1, field2, field3).record(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);
}
}

View File

@@ -0,0 +1,101 @@
// 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.codahale.metrics.Metric;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.kohsuke.args4j.Option;
class ListMetrics implements RestReadView<ConfigResource> {
private final PermissionBackend permissionBackend;
private final CurrentUser user;
private final DropWizardMetricMaker metrics;
@Option(name = "--data-only", usage = "return only values")
boolean dataOnly;
@Option(
name = "--prefix",
aliases = {"-p"},
metaVar = "PREFIX",
usage = "match metric by exact match or prefix"
)
List<String> query = new ArrayList<>();
@Inject
ListMetrics(
PermissionBackend permissionBackend, CurrentUser user, DropWizardMetricMaker metrics) {
this.permissionBackend = permissionBackend;
this.user = user;
this.metrics = metrics;
}
@Override
public Map<String, MetricJson> apply(ConfigResource resource)
throws AuthException, PermissionBackendException {
permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
SortedMap<String, MetricJson> out = new TreeMap<>();
List<String> prefixes = new ArrayList<>(query.size());
for (String q : query) {
if (q.endsWith("/")) {
prefixes.add(q);
} else {
Metric m = metrics.getMetric(q);
if (m != null) {
out.put(q, toJson(q, m));
}
}
}
if (query.isEmpty() || !prefixes.isEmpty()) {
for (String name : metrics.getMetricNames()) {
if (include(prefixes, name)) {
out.put(name, toJson(name, metrics.getMetric(name)));
}
}
}
return out;
}
private MetricJson toJson(String q, Metric m) {
return new MetricJson(m, metrics.getAnnotations(q), dataOnly);
}
private static boolean include(List<String> prefixes, String name) {
if (prefixes.isEmpty()) {
return true;
}
for (String p : prefixes) {
if (name.startsWith(p)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,198 @@
// 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.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
class MetricJson {
String description;
String unit;
Boolean constant;
Boolean rate;
Boolean gauge;
Boolean cumulative;
Long count;
Object value;
Double rate_1m;
Double rate_5m;
Double rate_15m;
Double rate_mean;
Double p50;
Double p75;
Double p95;
Double p98;
Double p99;
Double p99_9;
Double min;
Double avg;
Double max;
Double sum;
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);
unit = atts.get(Description.UNIT);
constant = toBool(atts, Description.CONSTANT);
rate = toBool(atts, Description.RATE);
gauge = toBool(atts, Description.GAUGE);
cumulative = toBool(atts, Description.CUMULATIVE);
}
init(metric, atts);
}
private void init(Metric metric, ImmutableMap<String, String> atts) {
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();
} else if (metric instanceof Gauge) {
Gauge<?> g = (Gauge<?>) metric;
value = g.getValue();
} else if (metric instanceof Meter) {
Meter m = (Meter) metric;
count = m.getCount();
rate_1m = m.getOneMinuteRate();
rate_5m = m.getFiveMinuteRate();
rate_15m = m.getFifteenMinuteRate();
} else if (metric instanceof Timer) {
Timer m = (Timer) metric;
Snapshot s = m.getSnapshot();
count = m.getCount();
rate_1m = m.getOneMinuteRate();
rate_5m = m.getFiveMinuteRate();
rate_15m = m.getFifteenMinuteRate();
double div = Description.getTimeUnit(atts.get(Description.UNIT)).toNanos(1);
p50 = s.getMedian() / div;
p75 = s.get75thPercentile() / div;
p95 = s.get95thPercentile() / div;
p98 = s.get98thPercentile() / div;
p99 = s.get99thPercentile() / div;
p99_9 = s.get999thPercentile() / div;
min = s.getMin() / div;
max = s.getMax() / div;
std_dev = s.getStdDev() / div;
} else if (metric instanceof Histogram) {
Histogram m = (Histogram) metric;
Snapshot s = m.getSnapshot();
count = m.getCount();
p50 = s.getMedian();
p75 = s.get75thPercentile();
p95 = s.get95thPercentile();
p98 = s.get98thPercentile();
p99 = s.get99thPercentile();
p99_9 = s.get999thPercentile();
min = (double) s.getMin();
avg = (double) s.getMean();
max = (double) s.getMax();
sum = s.getMean() * m.getCount();
std_dev = s.getStdDev();
}
}
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;
}
}
}

View File

@@ -0,0 +1,41 @@
// 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.codahale.metrics.Metric;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.config.ConfigResource;
import com.google.inject.TypeLiteral;
class MetricResource extends ConfigResource {
static final TypeLiteral<RestView<MetricResource>> METRIC_KIND =
new TypeLiteral<RestView<MetricResource>>() {};
private final String name;
private final Metric metric;
MetricResource(String name, Metric metric) {
this.name = name;
this.metric = metric;
}
String getName() {
return name;
}
Metric getMetric() {
return metric;
}
}

View File

@@ -0,0 +1,76 @@
// 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.codahale.metrics.Metric;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
class MetricsCollection implements ChildCollection<ConfigResource, MetricResource> {
private final DynamicMap<RestView<MetricResource>> views;
private final Provider<ListMetrics> list;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
private final DropWizardMetricMaker metrics;
@Inject
MetricsCollection(
DynamicMap<RestView<MetricResource>> views,
Provider<ListMetrics> list,
PermissionBackend permissionBackend,
Provider<CurrentUser> user,
DropWizardMetricMaker metrics) {
this.views = views;
this.list = list;
this.permissionBackend = permissionBackend;
this.user = user;
this.metrics = metrics;
}
@Override
public DynamicMap<RestView<MetricResource>> views() {
return views;
}
@Override
public RestView<ConfigResource> list() {
return list.get();
}
@Override
public MetricResource parse(ConfigResource parent, IdString id)
throws ResourceNotFoundException, AuthException, PermissionBackendException {
permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
Metric metric = metrics.getMetric(id.get());
if (metric == null) {
throw new ResourceNotFoundException(id.get());
}
return new MetricResource(id.get(), metric);
}
}

View File

@@ -0,0 +1,51 @@
// 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('/', '-');
}
}

View File

@@ -0,0 +1,74 @@
// 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);
}
}