/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under 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.streamsets.datacollector.metrics;
import com.codahale.metrics.Counter;
import com.codahale.metrics.ExponentiallyDecayingReservoir;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SlidingTimeWindowReservoir;
import com.codahale.metrics.Timer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Provides various helpful tools to work with metric objects. This util class expose several families of
* methods that can be used for various purposes:
*
* createX and deleteX
* Will always create a new metric object and throws an exception of metric of given name already exists.
*
* createStageX and deleteStageX
* Will create or create new or return existing stage specific metric object. Particularly useful for metrics
* that are covering all instances of given stage when pipeline is running in multi-threaded fashion.
*/
public class MetricsConfigurator {
public static final String JMX_PREFIX = "sdc.pipeline.";
public static final String METER_SUFFIX = ".meter";
public static final String COUNTER_SUFFIX = ".counter";
public static final String HISTOGRAM_M5_SUFFIX = ".histogramM5";
public static final String TIMER_SUFFIX = ".timer";
public static final String GAUGE_SUFFIX = ".gauge";
private static MetricRegistry sdcMetrics;
private static List<String> runningPipelines = new ArrayList<>();
private MetricsConfigurator() {}
private static String metricName(String name, String type) {
if (name.endsWith(type)) {
return name;
}
return name + type;
}
private static String jmxNamePrefix(String pipelineName, String pipelineRev) {
return JMX_PREFIX + pipelineName + "." + pipelineRev + ".";
}
private static <T extends Metric> T create(
MetricRegistry metrics,
final T metric,
final String name,
final String pipelineName,
final String pipelineRev
) {
final String jmxNamePrefix = jmxNamePrefix(pipelineName, pipelineRev);
final MetricRegistry metricRegistry = sdcMetrics;
if (metricRegistry != null && runningPipelines.contains(jmxNamePrefix)) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
String metricName = jmxNamePrefix + name;
if(!metricRegistry.getNames().contains(metricName)) {
metricRegistry.register(metricName, metric);
}
return null;
}
});
}
return metrics.register(name, metric);
}
public static Timer createStageTimer(MetricRegistry metrics, String nameSuffix, final String pipelineName, final String pipelineRev) {
String name = metricName(nameSuffix, TIMER_SUFFIX);
if(metrics.getTimers().containsKey(name)) {
return metrics.getTimers().get(name);
}
return createTimer(metrics, nameSuffix, pipelineName, pipelineRev);
}
public static Timer createTimer(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return create(
metrics,
new Timer(new SlidingTimeWindowReservoir(60, TimeUnit.SECONDS)),
metricName(name, TIMER_SUFFIX),
pipelineName,
pipelineRev
);
}
public static Meter createStageMeter(MetricRegistry metrics, String nameSuffix, final String pipelineName, final String pipelineRev) {
String name = metricName(nameSuffix, METER_SUFFIX);
if(metrics.getMeters().containsKey(name)) {
return metrics.getMeters().get(name);
}
return createMeter(metrics, nameSuffix, pipelineName, pipelineRev);
}
public static Meter createMeter(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return create(
metrics,
new ExtendedMeter(),
metricName(name, METER_SUFFIX),
pipelineName,
pipelineRev
);
}
public static Counter createStageCounter(MetricRegistry metrics, String nameSuffix, final String pipelineName, final String pipelineRev) {
String name = metricName(nameSuffix, COUNTER_SUFFIX);
if(metrics.getCounters().containsKey(name)) {
return metrics.getCounters().get(name);
}
return createCounter(metrics, nameSuffix, pipelineName, pipelineRev);
}
public static Counter createCounter(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return create(
metrics,
new Counter(),
metricName(name, COUNTER_SUFFIX),
pipelineName,
pipelineRev
);
}
public static Histogram createStageHistogram5Min(MetricRegistry metrics, String nameSuffix, final String pipelineName, final String pipelineRev) {
String name = metricName(nameSuffix, HISTOGRAM_M5_SUFFIX);
if(metrics.getHistograms().containsKey(name)) {
return metrics.getHistograms().get(name);
}
return createHistogram5Min(metrics, nameSuffix, pipelineName, pipelineRev);
}
public static Histogram createHistogram5Min(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return create(
metrics,
new Histogram(new ExponentiallyDecayingReservoir()),
metricName(name, HISTOGRAM_M5_SUFFIX),
pipelineName,
pipelineRev
);
}
public static Gauge<Map<String, Object>> createStageGauge(MetricRegistry metrics, String nameSuffix, Comparator<String> comparator, final String pipelineName, final String pipelineRev) {
String name = metricName(nameSuffix, GAUGE_SUFFIX);
if(metrics.getGauges().containsKey(name)) {
return metrics.getGauges().get(name);
}
return createGauge(metrics, nameSuffix, comparator, pipelineName, pipelineRev);
}
public static Gauge<Map<String, Object>> createGauge(MetricRegistry metrics, String name, Comparator<String> comparator, final String pipelineName, final String pipelineRev) {
return create(
metrics,
new MapGauge(comparator),
metricName(name, GAUGE_SUFFIX),
pipelineName,
pipelineRev
);
}
// Kept for backward compatibility with runtime stats, to be removed in future
public static Gauge<Map<String, Object>> createGauge(MetricRegistry metrics, String name, Gauge gauge, final String pipelineName, final String pipelineRev) {
return create(
metrics,
gauge,
metricName(name, GAUGE_SUFFIX),
pipelineName,
pipelineRev
);
}
public static Counter getCounter(MetricRegistry metrics, String name) {
return metrics.getCounters().get(metricName(name, COUNTER_SUFFIX));
}
public static ExtendedMeter getMeter(MetricRegistry metrics, String name) {
return (ExtendedMeter) metrics.getMeters().get(metricName(name, METER_SUFFIX));
}
public static Histogram getHistogram(MetricRegistry metrics, String name) {
return metrics.getHistograms().get(metricName(name, HISTOGRAM_M5_SUFFIX));
}
public static Timer getTimer(MetricRegistry metrics, String name) {
return metrics.getTimers().get(metricName(name, TIMER_SUFFIX));
}
public static Gauge getGauge(MetricRegistry metrics, String name) {
return metrics.getGauges().get(metricName(name, GAUGE_SUFFIX));
}
/**
* Remove metric object (regardless of it's type)
*/
private static boolean remove(final MetricRegistry metrics, final String name, String pipelineName, String pipelineRev) {
final String jmxNamePrefix = jmxNamePrefix(pipelineName, pipelineRev);
final MetricRegistry metricRegistry = sdcMetrics;
if (metricRegistry != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
metricRegistry.remove(jmxNamePrefix + name);
return null;
}
});
}
return metrics.remove(name);
}
public static boolean removeGauge(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return remove(metrics, metricName(name, GAUGE_SUFFIX), pipelineName, pipelineRev);
}
public static boolean removeMeter(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return remove(metrics, metricName(name, METER_SUFFIX), pipelineName, pipelineRev);
}
public static boolean removeCounter(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return remove(metrics, metricName(name, COUNTER_SUFFIX), pipelineName, pipelineRev);
}
public static boolean removeStageGauge(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return remove(metrics, metricName(name, GAUGE_SUFFIX), pipelineName, pipelineRev);
}
public static boolean removeStageMeter(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return remove(metrics, metricName(name, METER_SUFFIX), pipelineName, pipelineRev);
}
public static boolean removeStageCounter(MetricRegistry metrics, String name, final String pipelineName, final String pipelineRev) {
return remove(metrics, metricName(name, COUNTER_SUFFIX), pipelineName, pipelineRev);
}
public static synchronized void registerJmxMetrics(MetricRegistry metrics) {
sdcMetrics = metrics;
}
public static synchronized void registerPipeline(String pipelineName, String pipelineRev) {
runningPipelines.add(jmxNamePrefix(pipelineName, pipelineRev));
}
public static synchronized void cleanUpJmxMetrics(final String pipelineName, final String pipelineRev) {
final MetricRegistry metricRegistry = sdcMetrics;
final String jmxNamePrefix = jmxNamePrefix(pipelineName, pipelineRev);
runningPipelines.remove(jmxNamePrefix);
if (metricRegistry != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
for (String name : metricRegistry.getNames()) {
if (name.startsWith(jmxNamePrefix)) {
metricRegistry.remove(name);
}
}
return null;
}
});
}
}
public static boolean resetCounter(MetricRegistry metrics, String name) {
Counter counter = getCounter(metrics, name);
boolean result = false;
if(counter != null) {
//there could be race condition with observer thread trying to update the counter. This should be ok.
counter.dec(counter.getCount());
result = true;
}
return result;
}
}