| /* |
| * Copyright 2017 HugeGraph Authors |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with this |
| * work for additional information regarding copyright ownership. The ASF |
| * licenses this file to You 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.baidu.hugegraph.stats; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.time.Duration; |
| import java.util.List; |
| import java.util.UUID; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.management.MBeanServer; |
| import javax.management.MBeanServerFactory; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.baidu.hugegraph.util.Log; |
| import com.codahale.metrics.ConsoleReporter; |
| import com.codahale.metrics.Counter; |
| import com.codahale.metrics.CsvReporter; |
| import com.codahale.metrics.Histogram; |
| import com.codahale.metrics.JmxReporter; |
| import com.codahale.metrics.MetricFilter; |
| import com.codahale.metrics.MetricRegistry; |
| import com.codahale.metrics.Slf4jReporter; |
| import com.codahale.metrics.Timer; |
| import com.codahale.metrics.ganglia.GangliaReporter; |
| import com.codahale.metrics.graphite.Graphite; |
| import com.codahale.metrics.graphite.GraphiteReporter; |
| import com.google.common.base.Preconditions; |
| |
| import info.ganglia.gmetric4j.gmetric.GMetric; |
| import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode; |
| |
| /** |
| * Singleton that contains and configures HugeGraph's {@code MetricRegistry}. |
| */ |
| public class MetricsManager { |
| |
| private static final Logger LOG = Log.logger(MetricsManager.class); |
| |
| private static final MetricsManager INSTANCE = new MetricsManager(); |
| |
| private final MetricRegistry registry; |
| private ConsoleReporter consoleReporter = null; |
| private CsvReporter csvReporter = null; |
| private JmxReporter jmxReporter = null; |
| private Slf4jReporter slf4jReporter = null; |
| private GangliaReporter gangliaReporter = null; |
| private GraphiteReporter graphiteReporter = null; |
| |
| /** |
| * Private constructor for singleton. |
| */ |
| private MetricsManager() { |
| this.registry = new MetricRegistry(); |
| } |
| |
| /** |
| * Return the Singleton Metrics Manager. |
| * |
| * @return the single {@code MetricManager} |
| */ |
| public static MetricsManager instance() { |
| return INSTANCE; |
| } |
| |
| /** |
| * Return the HugeGraph Metrics registry. |
| * |
| * @return the single {@code MetricRegistry} used for all of HugeGraph's |
| * Metrics monitoring |
| */ |
| public MetricRegistry getRegistry() { |
| return this.registry; |
| } |
| |
| /** |
| * Create a {@link ConsoleReporter} attached to the HugeGraph Metrics |
| * registry. |
| * |
| * @param reportInterval |
| * time to wait between dumping metrics to the console |
| */ |
| public synchronized void addConsoleReporter(Duration reportInterval) { |
| if (this.consoleReporter != null) { |
| LOG.debug("Metrics ConsoleReporter already active"); |
| return; |
| } |
| |
| this.consoleReporter = ConsoleReporter.forRegistry(getRegistry()).build(); |
| this.consoleReporter.start(reportInterval.toMillis(), TimeUnit.MILLISECONDS); |
| } |
| |
| /** |
| * Stop a {@link ConsoleReporter} previously created by a call to |
| * {@link #addConsoleReporter(Duration)} and release it for GC. Idempotent |
| * between calls to the associated add method. Does nothing before the first |
| * call to the associated add method. |
| */ |
| public synchronized void removeConsoleReporter() { |
| if (this.consoleReporter != null) { |
| this.consoleReporter.stop(); |
| } |
| this.consoleReporter = null; |
| } |
| |
| /** |
| * Create a {@link CsvReporter} attached to the HugeGraph Metrics registry. |
| * <p> |
| * The {@code output} argument must be non-null but need not exist. If it |
| * doesn't already exist, this method attempts to create it by calling |
| * {@link File#mkdirs()}. |
| * |
| * @param reportInterval |
| * time to wait between dumping metrics to CSV files in |
| * the configured directory |
| * @param output |
| * the path to a directory into which Metrics will periodically |
| * write CSV data |
| */ |
| public synchronized void addCsvReporter(Duration reportInterval, |
| String output) { |
| |
| File outputDir = new File(output); |
| |
| if (this.csvReporter != null) { |
| LOG.debug("Metrics CsvReporter already active"); |
| return; |
| } |
| |
| if (!outputDir.exists()) { |
| if (!outputDir.mkdirs()) { |
| LOG.warn("Failed to create CSV metrics dir {}", outputDir); |
| } |
| } |
| |
| this.csvReporter = CsvReporter.forRegistry(getRegistry()).build(outputDir); |
| this.csvReporter.start(reportInterval.toMillis(), TimeUnit.MILLISECONDS); |
| } |
| |
| /** |
| * Stop a {@link CsvReporter} previously created by a call to |
| * {@link #addCsvReporter(Duration, String)} and release it for GC. |
| * Idempotent between calls to the associated add method. Does nothing |
| * before the first call to the associated add method. |
| */ |
| public synchronized void removeCsvReporter() { |
| if (this.csvReporter != null) { |
| this.csvReporter.stop(); |
| } |
| this.csvReporter = null; |
| } |
| |
| /** |
| * Create a {@link JmxReporter} attached to the HugeGraph Metrics registry. |
| * <p> |
| * If {@code domain} or {@code agentId} is null, then Metrics's uses its own |
| * internal default value(s). |
| * <p> |
| * If {@code agentId} is non-null, then |
| * {@link MBeanServerFactory#findMBeanServer(String)} must return exactly |
| * one {@code MBeanServer}. The reporter will register with that server. If |
| * the {@code findMBeanServer(agentId)} call returns no or multiple servers, |
| * then this method logs an error and falls back on the Metrics default for |
| * {@code agentId}. |
| * |
| * @param domain |
| * the JMX domain in which to continuously expose metrics |
| * @param agentId |
| * the JMX agent ID |
| */ |
| public synchronized void addJmxReporter(String domain, String agentId) { |
| if (this.jmxReporter != null) { |
| LOG.debug("Metrics JmxReporter already active"); |
| return; |
| } |
| |
| JmxReporter.Builder builder = JmxReporter.forRegistry(getRegistry()); |
| |
| if (domain != null) { |
| builder.inDomain(domain); |
| } |
| |
| if (agentId != null) { |
| List<MBeanServer> servs = |
| MBeanServerFactory.findMBeanServer(agentId); |
| if (servs != null && servs.size() == 1) { |
| builder.registerWith(servs.get(0)); |
| } else { |
| LOG.error("Metrics Slf4jReporter agentId {} does not " + |
| "resolve to a single MBeanServer", agentId); |
| } |
| } |
| |
| this.jmxReporter = builder.build(); |
| this.jmxReporter.start(); |
| } |
| |
| /** |
| * Stop a {@link JmxReporter} previously created by a call to |
| * {@link #addJmxReporter(String, String)} and release it for GC. Idempotent |
| * between calls to the associated add method. Does nothing before the first |
| * call to the associated add method. |
| */ |
| public synchronized void removeJmxReporter() { |
| if (this.jmxReporter != null) { |
| this.jmxReporter.stop(); |
| } |
| this.jmxReporter = null; |
| } |
| |
| /** |
| * Create a {@link Slf4jReporter} attached to the HugeGraph Metrics registry |
| * <p> |
| * If {@code loggerName} is null, or if it is non-null but |
| * {@link LoggerFactory#getLogger(String)} returns null, then Metrics's |
| * default Slf4j logger name is used instead. |
| * |
| * @param reportInterval |
| * time to wait between writing metrics to the Slf4j logger |
| * @param loggerName |
| * the name of the Slf4j logger that receives metrics |
| */ |
| public synchronized void addSlf4jReporter(Duration reportInterval, |
| String loggerName) { |
| if (this.slf4jReporter != null) { |
| LOG.debug("Metrics Slf4jReporter already active"); |
| return; |
| } |
| |
| Slf4jReporter.Builder builder = |
| Slf4jReporter.forRegistry(getRegistry()); |
| |
| if (loggerName != null) { |
| Logger slfLogger = LoggerFactory.getLogger(loggerName); |
| if (slfLogger != null) { |
| builder.outputTo(slfLogger); |
| } else { |
| LOG.error("Logger with name {} could not be obtained", |
| loggerName); |
| } |
| } |
| |
| this.slf4jReporter = builder.build(); |
| this.slf4jReporter.start(reportInterval.toMillis(), TimeUnit.MILLISECONDS); |
| } |
| |
| /** |
| * Stop a {@link Slf4jReporter} previously created by a call to |
| * {@link #addSlf4jReporter(Duration, String)} and release it for GC. |
| * Idempotent between calls to the associated add method. Does nothing |
| * before the first call to the associated add method. |
| */ |
| public synchronized void removeSlf4jReporter() { |
| if (this.slf4jReporter != null) { |
| this.slf4jReporter.stop(); |
| } |
| this.slf4jReporter = null; |
| } |
| |
| /** |
| * Create a {@link GangliaReporter} attached to the HugeGraph Metrics |
| * registry. |
| * <p> |
| * {@code groupOrHost} and {@code addressingMode} must be non-null. The |
| * remaining non-primitive arguments may be null. If {@code protocol31} is |
| * null, then true is assumed. Null values of {@code hostUUID} or |
| * {@code spoof} are passed into the {@link GMetric} constructor, which |
| * causes Ganglia to use its internal logic for generating a default UUID |
| * and default reporting hostname (respectively). |
| * |
| * @param groupOrHost |
| * the multicast group or unicast hostname to which Ganglia |
| * events are sent |
| * @param port |
| * the port to which events are sent |
| * @param addressingMode |
| * whether to send events with multicast or unicast |
| * @param ttl |
| * multicast ttl (ignored for unicast) |
| * @param protocol31 |
| * true to use Ganglia protocol version 3.1, false to use 3.0 |
| * @param hostUUID |
| * uuid for the host |
| * @param spoof |
| * override this machine's IP/hostname as it appears on the |
| * Ganglia server |
| * @param reportInterval |
| * titme to wait before sending data to the ganglia |
| * unicast host or multicast group |
| * @throws IOException |
| * when a {@link GMetric} can't be instantiated using the |
| * provided arguments |
| */ |
| public synchronized void addGangliaReporter( |
| String groupOrHost, int port, UDPAddressingMode addressingMode, |
| int ttl, Boolean protocol31, UUID hostUUID, String spoof, |
| Duration reportInterval) throws IOException { |
| |
| Preconditions.checkNotNull(groupOrHost); |
| Preconditions.checkNotNull(addressingMode); |
| |
| if (this.gangliaReporter != null) { |
| LOG.debug("Metrics GangliaReporter already active"); |
| return; |
| } |
| |
| if (protocol31 == null) { |
| protocol31 = true; |
| } |
| GMetric ganglia = new GMetric(groupOrHost, port, addressingMode, ttl, |
| protocol31, hostUUID, spoof); |
| |
| GangliaReporter.Builder builder = |
| GangliaReporter.forRegistry(getRegistry()); |
| |
| this.gangliaReporter = builder.build(ganglia); |
| this.gangliaReporter.start(reportInterval.toMillis(), |
| TimeUnit.MILLISECONDS); |
| |
| LOG.info("Configured Ganglia Metrics reporter host={} interval={} " + |
| "port={} addrmode={} ttl={} proto31={} uuid={} spoof={}", |
| groupOrHost, reportInterval, port, addressingMode, ttl, |
| protocol31, hostUUID, spoof); |
| } |
| |
| /** |
| * Stop a {@link GangliaReporter} previously created by a call to |
| * {@link #addGangliaReporter} |
| * and release it for GC. Idempotent between calls to the associated add |
| * method. Does nothing before the first call to the associated add method. |
| */ |
| public synchronized void removeGangliaReporter() { |
| if (this.gangliaReporter != null) { |
| this.gangliaReporter.stop(); |
| } |
| this.gangliaReporter = null; |
| } |
| |
| /** |
| * Create a {@link GraphiteReporter} attached to the HugeGraph Metrics |
| * registry. |
| * <p> |
| * If {@code prefix} is null, then Metrics's internal default prefix is used |
| * (empty string at the time this comment was written). |
| * |
| * @param host |
| * the host to which Graphite reports are sent |
| * @param port |
| * the port to which Graphite reports are sent |
| * @param prefix |
| * the optional metrics prefix |
| * @param reportInterval |
| * time to wait between sending metrics to the configured |
| * Graphite host and port |
| */ |
| public synchronized void addGraphiteReporter(String host, int port, |
| String prefix, |
| Duration reportInterval) { |
| |
| Preconditions.checkNotNull(host); |
| |
| Graphite graphite = new Graphite(new InetSocketAddress(host, port)); |
| |
| GraphiteReporter.Builder builder = |
| GraphiteReporter.forRegistry(getRegistry()); |
| |
| if (prefix != null) { |
| builder.prefixedWith(prefix); |
| } |
| builder.filter(MetricFilter.ALL); |
| |
| this.graphiteReporter = builder.build(graphite); |
| this.graphiteReporter.start(reportInterval.toMillis(), |
| TimeUnit.MILLISECONDS); |
| LOG.info("Configured Graphite reporter host={} interval={} " + |
| "port={} prefix={}", host, reportInterval, port, prefix); |
| } |
| |
| /** |
| * Stop a {@link GraphiteReporter} previously created by a call to |
| * {@link #addGraphiteReporter(String, int, String, Duration)} and release |
| * it for GC. Idempotent between calls to the associated add method. Does |
| * nothing before the first call to the associated add method. |
| */ |
| public synchronized void removeGraphiteReporter() { |
| if (this.graphiteReporter != null) { |
| this.graphiteReporter.stop(); |
| } |
| this.graphiteReporter = null; |
| } |
| |
| /** |
| * Remove all HugeGraph Metrics reporters previously configured through the |
| * {@code add*} methods on this class. |
| */ |
| public synchronized void removeAllReporters() { |
| removeConsoleReporter(); |
| removeCsvReporter(); |
| removeJmxReporter(); |
| removeSlf4jReporter(); |
| removeGangliaReporter(); |
| removeGraphiteReporter(); |
| } |
| |
| public Counter getCounter(String name) { |
| return getRegistry().counter(name); |
| } |
| |
| public Counter getCounter(String prefix, String... names) { |
| return getRegistry().counter(MetricRegistry.name(prefix, names)); |
| } |
| |
| public Timer getTimer(String name) { |
| return getRegistry().timer(name); |
| } |
| |
| public Timer getTimer(String prefix, String... names) { |
| return getRegistry().timer(MetricRegistry.name(prefix, names)); |
| } |
| |
| public Histogram getHistogram(String name) { |
| return getRegistry().histogram(name); |
| } |
| |
| public Histogram getHistogram(String prefix, String... names) { |
| return getRegistry().histogram(MetricRegistry.name(prefix, names)); |
| } |
| |
| public boolean remove(String name) { |
| return getRegistry().remove(name); |
| } |
| } |