| // 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.collect.ImmutableSet; |
| import com.google.gerrit.common.Version; |
| 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.google.gerrit.server.logging.Metadata; |
| import java.lang.management.GarbageCollectorMXBean; |
| import java.lang.management.ManagementFactory; |
| import java.lang.management.MemoryMXBean; |
| import java.lang.management.MemoryUsage; |
| import java.lang.management.OperatingSystemMXBean; |
| import java.lang.management.ThreadMXBean; |
| import java.util.concurrent.TimeUnit; |
| |
| public class ProcMetricModule extends MetricModule { |
| @Override |
| protected void configure(MetricMaker metrics) { |
| buildLabel(metrics); |
| procUptime(metrics); |
| procCpuUsage(metrics); |
| procCpuLoad(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), |
| ManagementFactory.getRuntimeMXBean()::getUptime); |
| } |
| |
| private void procCpuUsage(MetricMaker metrics) { |
| OperatingSystemMXBeanInterface provider = OperatingSystemMXBeanFactory.create(); |
| |
| if (provider == null) { |
| return; |
| } |
| |
| if (provider.getProcessCpuTime() != -1) { |
| metrics.newCallbackMetric( |
| "proc/cpu/usage", |
| Double.class, |
| new Description("CPU time used by the process").setCumulative().setUnit(Units.SECONDS), |
| () -> provider.getProcessCpuTime() / 1e9); |
| } |
| |
| if (provider.getOpenFileDescriptorCount() != -1) { |
| metrics.newCallbackMetric( |
| "proc/num_open_fds", |
| Long.class, |
| new Description("Number of open file descriptors").setGauge().setUnit("fds"), |
| provider::getOpenFileDescriptorCount); |
| } |
| metrics.newCallbackMetric( |
| "proc/cpu/num_cores", |
| Integer.class, |
| new Description("Number of processors available to the Java virtual machine").setGauge(), |
| Runtime.getRuntime()::availableProcessors); |
| } |
| |
| private void procCpuLoad(MetricMaker metrics) { |
| OperatingSystemMXBean provider = |
| ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); |
| if (provider.getSystemLoadAverage() != -1) { |
| metrics.newCallbackMetric( |
| "proc/cpu/system_load", |
| Double.class, |
| new Description("System load average for the last minute").setGauge(), |
| provider::getSystemLoadAverage); |
| } |
| } |
| |
| private void procJvmMemory(MetricMaker metrics) { |
| 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)); |
| |
| CallbackMetric0<Long> heapUsed = |
| metrics.newCallbackMetric( |
| "proc/jvm/memory/heap_used", |
| Long.class, |
| new Description("Amount of memory holding user objects.") |
| .setGauge() |
| .setUnit(Units.BYTES)); |
| |
| 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)); |
| |
| 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)); |
| |
| 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")); |
| |
| MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); |
| metrics.newTrigger( |
| ImmutableSet.of( |
| heapCommitted, heapUsed, nonHeapCommitted, nonHeapUsed, objectPendingFinalizationCount), |
| () -> { |
| 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) { |
| Field<String> gcNameField = |
| Field.ofString("gc_name", Metadata.Builder::garbageCollectorName) |
| .description("The name of the garbage collector") |
| .build(); |
| |
| CallbackMetric1<String, Long> gcCount = |
| metrics.newCallbackMetric( |
| "proc/jvm/gc/count", |
| Long.class, |
| new Description("Number of GCs").setCumulative(), |
| gcNameField); |
| |
| CallbackMetric1<String, Long> gcTime = |
| metrics.newCallbackMetric( |
| "proc/jvm/gc/time", |
| Long.class, |
| new Description("Approximate accumulated GC elapsed time") |
| .setCumulative() |
| .setUnit(Units.MILLISECONDS), |
| gcNameField); |
| |
| metrics.newTrigger( |
| gcCount, |
| gcTime, |
| () -> { |
| 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) { |
| ThreadMXBean thread = ManagementFactory.getThreadMXBean(); |
| metrics.newCallbackMetric( |
| "proc/jvm/thread/num_live", |
| Integer.class, |
| new Description("Current live thread count").setGauge().setUnit("threads"), |
| thread::getThreadCount); |
| metrics.newCallbackMetric( |
| "proc/jvm/thread/num_daemon_live", |
| Integer.class, |
| new Description("Current live daemon threads count").setGauge().setUnit("threads"), |
| thread::getDaemonThreadCount); |
| metrics.newCallbackMetric( |
| "proc/jvm/thread/num_peak_live", |
| Integer.class, |
| new Description( |
| "Peak live thread count since the Java virtual machine started or peak was reset") |
| .setGauge() |
| .setUnit("threads"), |
| thread::getPeakThreadCount); |
| metrics.newCallbackMetric( |
| "proc/jvm/thread/num_total_started", |
| Long.class, |
| new Description( |
| "Total number of threads created and also started since the Java virtual machine started") |
| .setGauge() |
| .setUnit("threads"), |
| thread::getTotalStartedThreadCount); |
| if (thread.isSynchronizerUsageSupported()) { |
| metrics.newCallbackMetric( |
| "proc/jvm/thread/num_deadlocked_threads", |
| Integer.class, |
| new Description( |
| "number of threads that are deadlocked waiting for object monitors or ownable synchronizers") |
| .setGauge() |
| .setUnit("threads"), |
| () -> { |
| long[] deadlocked = thread.findDeadlockedThreads(); |
| if (deadlocked == null) { |
| return 0; |
| } |
| return deadlocked.length; |
| }); |
| } else { |
| metrics.newCallbackMetric( |
| "proc/jvm/thread/num_deadlocked_threads", |
| Integer.class, |
| new Description( |
| "number of threads that are deadlocked waiting to acquire object monitors") |
| .setGauge() |
| .setUnit("threads"), |
| () -> { |
| long[] deadlocked = thread.findMonitorDeadlockedThreads(); |
| if (deadlocked == null) { |
| return 0; |
| } |
| return deadlocked.length; |
| }); |
| } |
| } |
| } |