Export standard JVM and JGit metrics

Add support for constant metrics.  These are exported values that are
computed only once per process and cannot change without the JVM
shutting down and starting back up again.

Export proc/birth_timestamp as build/label to describe when the
process launched and what version it is.  These are constants.

Add support for CallbackMetric1, allowing for some JVM GC data to be
exported. These can be useful to track Java GC overheads over time.

Export JGit block cache metrics along with high level JVM memory usage.

Change-Id: I9bb24a466eab99cf93358b105ff0c7333bd78ea2
This commit is contained in:
Shawn Pearce 2015-11-11 22:47:21 -08:00
parent b65b83a5f6
commit 98d198c701
14 changed files with 674 additions and 29 deletions

View File

@ -0,0 +1,34 @@
// 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.
*
* @param <F1> type of the field.
* @param <V> type of the metric value, typically Integer or Long.
*/
public abstract class CallbackMetric1<F1, V> implements CallbackMetric<V> {
/**
* Supply the current value of the metric.
*
* @param field1 bucket to increment.
* @param value current value.
*/
public abstract void set(F1 field1, V value);
/** Ensure a zeroed metric is created for the field value. */
public abstract void forceCreate(F1 field1);
}

View File

@ -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 CONSTANT = "CONSTANT";
public static final String FIELD_ORDERING = "FIELD_ORDERING";
public static final String TRUE_VALUE = "1";
@ -76,6 +77,16 @@ public class Description {
return this;
}
/**
* Mark the value as constant for the life of this process. Typically used for
* software versions, command line arguments, etc. that cannot change without
* a process restart.
*/
public Description setConstant() {
annotations.put(CONSTANT, TRUE_VALUE);
return this;
}
/**
* 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
@ -111,6 +122,11 @@ public class Description {
return this;
}
/** True if the metric value never changes after startup. */
public boolean isConstant() {
return TRUE_VALUE.equals(annotations.get(CONSTANT));
}
/** True if the metric may be interpreted as a rate over time. */
public boolean isRate() {
return TRUE_VALUE.equals(annotations.get(RATE));

View File

@ -100,6 +100,16 @@ public class DisabledMetricMaker extends MetricMaker {
};
}
@Override
public <F1, V> CallbackMetric1<F1, V> newCallbackMetric(String name,
Class<V> valueClass, Description desc, Field<F1> field1) {
return new CallbackMetric1<F1, V>() {
@Override public void set(F1 field1, V value) {}
@Override public void forceCreate(F1 field1) {}
@Override public void remove() {}
};
}
@Override
public RegistrationHandle newTrigger(Set<CallbackMetric<?>> metrics,
Runnable trigger) {

View File

@ -46,6 +46,27 @@ public abstract class MetricMaker {
String name, Description desc,
Field<F1> field1, Field<F2> field2, Field<F3> field3);
/**
* Constant value that does not change.
*
* @param name unique name of the metric.
* @param value only value of the metric.
* @param desc description of the metric.
*/
public <V> void newConstantMetric(String name, final V value, Description desc) {
desc.setConstant();
@SuppressWarnings("unchecked")
Class<V> type = (Class<V>) value.getClass();
final CallbackMetric0<V> metric = newCallbackMetric(name, type, desc);
newTrigger(metric, new Runnable() {
@Override
public void run() {
metric.set(value);
}
});
}
/**
* Instantaneous reading of a value.
*
@ -80,6 +101,9 @@ public abstract class MetricMaker {
/** Instantaneous reading of a single value. */
public abstract <V> CallbackMetric0<V> newCallbackMetric(
String name, Class<V> valueClass, Description desc);
public abstract <F1, V> CallbackMetric1<F1, V> newCallbackMetric(
String name, Class<V> valueClass, Description desc,
Field<F1> field1);
/** Connect logic to populate a previously created {@link CallbackMetric}. */
public RegistrationHandle newTrigger(CallbackMetric<?> metric1, Runnable trigger) {

View File

@ -0,0 +1,124 @@
// 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.codahale.metrics.Gauge;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
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;
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);
}
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 (cells) {
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,
new Function<ValueGauge, Metric> () {
@Override
public Metric apply(ValueGauge in) {
return in;
}
});
}
final class ValueGauge implements Gauge<V> {
volatile V value = zero;
@Override
public V getValue() {
Runnable t = trigger;
if (t != null) {
t.run();
}
return value;
}
}
}

View File

@ -0,0 +1,19 @@
// 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 register(Runnable trigger);
}

View File

@ -16,7 +16,11 @@ package com.google.gerrit.metrics.dropwizard;
import com.google.gerrit.metrics.CallbackMetric0;
class CallbackMetricImpl0<V> extends CallbackMetric0<V> {
import com.codahale.metrics.MetricRegistry;
class CallbackMetricImpl0<V>
extends CallbackMetric0<V>
implements CallbackMetricGlue {
@SuppressWarnings("unchecked")
static <V> V zeroFor(Class<V> valueClass) {
if (valueClass == Integer.class) {
@ -37,10 +41,15 @@ class CallbackMetricImpl0<V> extends CallbackMetric0<V> {
}
}
final String name;
private V value;
private final DropWizardMetricMaker metrics;
private final MetricRegistry registry;
private final String name;
private volatile V value;
CallbackMetricImpl0(String name, Class<V> valueType) {
CallbackMetricImpl0(DropWizardMetricMaker metrics, MetricRegistry registry,
String name, Class<V> valueType) {
this.metrics = metrics;
this.registry = registry;
this.name = name;
this.value = zeroFor(valueType);
}
@ -52,16 +61,18 @@ class CallbackMetricImpl0<V> extends CallbackMetric0<V> {
@Override
public void remove() {
// Triggers register and remove the metric.
metrics.remove(name);
registry.remove(name);
}
com.codahale.metrics.Gauge<V> gauge(final Runnable trigger) {
return new com.codahale.metrics.Gauge<V>() {
@Override
public void register(final Runnable trigger) {
registry.register(name, new com.codahale.metrics.Gauge<V>() {
@Override
public V getValue() {
trigger.run();
return value;
}
};
});
}
}

View File

@ -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.common.base.Function;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Field;
import com.codahale.metrics.MetricRegistry;
/** 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 set(F1 field1, V value) {
getOrCreate(field1).value = value;
}
@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

@ -24,18 +24,21 @@ 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.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.Description.FieldOrdering;
import com.google.gerrit.metrics.proc.JGitMetricModule;
import com.google.gerrit.metrics.proc.ProcMetricModule;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
@ -64,6 +67,9 @@ public class DropWizardMetricMaker extends MetricMaker {
bind(DropWizardMetricMaker.class).in(Scopes.SINGLETON);
bind(MetricMaker.class).to(DropWizardMetricMaker.class);
install(new ProcMetricModule());
install(new JGitMetricModule());
DynamicMap.mapOf(binder(), METRIC_KIND);
child(CONFIG_KIND, "metrics").to(MetricsCollection.class);
get(METRIC_KIND).to(GetMetric.class);
@ -137,6 +143,7 @@ public class DropWizardMetricMaker extends MetricMaker {
}
private static void checkCounterDescription(Description desc) {
checkArgument(!desc.isConstant(), "counters must not be constant");
checkArgument(!desc.isGauge(), "counters must not be gauge");
}
@ -200,6 +207,7 @@ public class DropWizardMetricMaker extends MetricMaker {
}
private static void checkTimerDescription(Description desc) {
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");
@ -214,37 +222,35 @@ public class DropWizardMetricMaker extends MetricMaker {
public <V> CallbackMetric0<V> newCallbackMetric(
String name, Class<V> valueClass, Description desc) {
define(name, desc);
return new CallbackMetricImpl0<>(name, valueClass);
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) {
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) {
if (metrics.size() > 1) {
trigger = new CallbackGroup(trigger);
}
trigger = new CallbackGroup(trigger);
for (CallbackMetric<?> m : metrics) {
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) {
CallbackMetricImpl0<?> metric = (CallbackMetricImpl0<?>) m;
registry.register(metric.name, metric.gauge(trigger));
names.add(metric.name);
((CallbackMetricGlue) m).register(trigger);
}
trigger.run();
final List<CallbackMetric<?>> all = new ArrayList<>(metrics);
return new RegistrationHandle() {
@Override
public void remove() {
for (String name : names) {
descriptions.remove(name);
registry.remove(name);
for (CallbackMetric<?> m : all) {
m.remove();
}
}
};

View File

@ -35,6 +35,7 @@ import java.util.TreeMap;
class MetricJson {
String description;
String unit;
Boolean constant;
Boolean rate;
Boolean gauge;
Boolean cumulative;
@ -65,6 +66,7 @@ class MetricJson {
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);

View File

@ -0,0 +1,53 @@
// 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.proc;
import com.google.common.base.Supplier;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.Description.Units;
import org.eclipse.jgit.internal.storage.file.WindowCacheStatAccessor;
public class JGitMetricModule extends MetricModule {
@Override
protected void configure(MetricMaker metrics) {
metrics.newCallbackMetric(
"jgit/block_cache/cache_used",
Long.class,
new Description("Bytes of memory retained in JGit block cache.")
.setGauge()
.setUnit(Units.BYTES),
new Supplier<Long>() {
@Override
public Long get() {
return WindowCacheStatAccessor.getOpenBytes();
}
});
metrics.newCallbackMetric(
"jgit/block_cache/open_files",
Integer.class,
new Description("File handles held open by JGit block cache.")
.setGauge()
.setUnit("fds"),
new Supplier<Integer>() {
@Override
public Integer get() {
return WindowCacheStatAccessor.getOpenFiles();
}
});
}
}

View File

@ -0,0 +1,43 @@
// 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.proc;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.metrics.MetricMaker;
import com.google.inject.Inject;
/** Guice module to configure metrics on server startup. */
public abstract class MetricModule extends LifecycleModule {
/** Configure metrics during server startup. */
protected abstract void configure(MetricMaker metrics);
@Override
protected void configure() {
listener().toInstance(new LifecycleListener() {
@Inject
MetricMaker metrics;
@Override
public void start() {
configure(metrics);
}
@Override
public void stop() {
}
});
}
}

View File

@ -0,0 +1,226 @@
// 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.proc;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Version;
import com.google.gerrit.metrics.CallbackMetric;
import com.google.gerrit.metrics.CallbackMetric0;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
import com.google.gerrit.metrics.Field;
import com.google.gerrit.metrics.MetricMaker;
import com.sun.management.OperatingSystemMXBean;
import com.sun.management.UnixOperatingSystemMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("restriction")
public class ProcMetricModule extends MetricModule {
@Override
protected void configure(MetricMaker metrics) {
buildLabel(metrics);
procUptime(metrics);
procCpuUsage(metrics);
procJvmGc(metrics);
procJvmMemory(metrics);
procJvmThread(metrics);
}
private void buildLabel(MetricMaker metrics) {
metrics.newConstantMetric(
"build/label",
Strings.nullToEmpty(Version.getVersion()),
new Description("Version of Gerrit server software"));
}
private void procUptime(MetricMaker metrics) {
metrics.newConstantMetric(
"proc/birth_timestamp",
Long.valueOf(TimeUnit.MILLISECONDS.toMicros(
System.currentTimeMillis())),
new Description("Time at which the process started")
.setUnit(Units.MICROSECONDS));
metrics.newCallbackMetric(
"proc/uptime",
Long.class,
new Description("Uptime of this process")
.setUnit(Units.MILLISECONDS),
new Supplier<Long>() {
@Override
public Long get() {
return ManagementFactory.getRuntimeMXBean().getUptime();
}
});
}
private void procCpuUsage(MetricMaker metrics) {
final OperatingSystemMXBean sys =
(OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
if (sys.getProcessCpuTime() != -1) {
metrics.newCallbackMetric(
"proc/cpu/usage",
Double.class,
new Description("CPU time used by the process")
.setCumulative()
.setUnit(Units.SECONDS),
new Supplier<Double>() {
@Override
public Double get() {
return sys.getProcessCpuTime() / 1e9;
}
});
}
if (sys instanceof UnixOperatingSystemMXBean) {
final UnixOperatingSystemMXBean unix = (UnixOperatingSystemMXBean) sys;
if (unix.getOpenFileDescriptorCount() != -1) {
metrics.newCallbackMetric(
"proc/num_open_fds",
Long.class,
new Description("Number of open file descriptors")
.setGauge()
.setUnit("fds"),
new Supplier<Long>() {
@Override
public Long get() {
return unix.getOpenFileDescriptorCount();
}
});
}
}
}
private void procJvmMemory(MetricMaker metrics) {
final CallbackMetric0<Long> heapCommitted = metrics.newCallbackMetric(
"proc/jvm/memory/heap_committed",
Long.class,
new Description("Amount of memory guaranteed for user objects.")
.setGauge()
.setUnit(Units.BYTES));
final CallbackMetric0<Long> heapUsed = metrics.newCallbackMetric(
"proc/jvm/memory/heap_used",
Long.class,
new Description("Amount of memory holding user objects.")
.setGauge()
.setUnit(Units.BYTES));
final CallbackMetric0<Long> nonHeapCommitted = metrics.newCallbackMetric(
"proc/jvm/memory/non_heap_committed",
Long.class,
new Description("Amount of memory guaranteed for classes, etc.")
.setGauge()
.setUnit(Units.BYTES));
final CallbackMetric0<Long> nonHeapUsed = metrics.newCallbackMetric(
"proc/jvm/memory/non_heap_used",
Long.class,
new Description("Amount of memory holding classes, etc.")
.setGauge()
.setUnit(Units.BYTES));
final CallbackMetric0<Integer> objectPendingFinalizationCount =
metrics.newCallbackMetric(
"proc/jvm/memory/object_pending_finalization_count",
Integer.class,
new Description("Approximate number of objects needing finalization.")
.setGauge()
.setUnit("objects"));
final MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
metrics.newTrigger(
ImmutableSet.<CallbackMetric<?>> of(
heapCommitted, heapUsed, nonHeapCommitted,
nonHeapUsed, objectPendingFinalizationCount),
new Runnable() {
@Override
public void run() {
try {
MemoryUsage stats = memory.getHeapMemoryUsage();
heapCommitted.set(stats.getCommitted());
heapUsed.set(stats.getUsed());
} catch (IllegalArgumentException e) {
// MXBean may throw due to a bug in Java 7; ignore.
}
MemoryUsage stats = memory.getNonHeapMemoryUsage();
nonHeapCommitted.set(stats.getCommitted());
nonHeapUsed.set(stats.getUsed());
objectPendingFinalizationCount.set(
memory.getObjectPendingFinalizationCount());
}
});
}
private void procJvmGc(MetricMaker metrics) {
final CallbackMetric1<String, Long> gcCount = metrics.newCallbackMetric(
"proc/jvm/gc/count",
Long.class,
new Description("Number of GCs").setCumulative(),
Field.ofString("gc_name", "The name of the garbage collector"));
final CallbackMetric1<String, Long> gcTime = metrics.newCallbackMetric(
"proc/jvm/gc/time",
Long.class,
new Description("Approximate accumulated GC elapsed time")
.setCumulative()
.setUnit(Units.MILLISECONDS),
Field.ofString("gc_name", "The name of the garbage collector"));
metrics.newTrigger(gcCount, gcTime, new Runnable() {
@Override
public void run() {
for (GarbageCollectorMXBean gc : ManagementFactory
.getGarbageCollectorMXBeans()) {
long count = gc.getCollectionCount();
if (count != -1) {
gcCount.set(gc.getName(), count);
}
long time = gc.getCollectionTime();
if (time != -1) {
gcTime.set(gc.getName(), time);
}
}
}
});
}
private void procJvmThread(MetricMaker metrics) {
final ThreadMXBean thread = ManagementFactory.getThreadMXBean();
metrics.newCallbackMetric(
"proc/jvm/thread/num_live",
Integer.class,
new Description("Current live thread count")
.setGauge()
.setUnit("threads"),
new Supplier<Integer>() {
@Override
public Integer get() {
return thread.getThreadCount();
}
});
}
}

View File

@ -18,6 +18,7 @@ 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.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;
@ -124,6 +125,15 @@ class PluginMetricMaker extends MetricMaker implements LifecycleListener {
return m;
}
@Override
public <F1, V> CallbackMetric1<F1, V> newCallbackMetric(String name,
Class<V> valueClass, Description desc, Field<F1> field1) {
CallbackMetric1<F1, V> m =
root.newCallbackMetric(prefix + name, valueClass, desc, field1);
cleanup.add(m);
return m;
}
@Override
public RegistrationHandle newTrigger(Set<CallbackMetric<?>> metrics,
Runnable trigger) {