/*
* 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 gobblin.metrics;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import com.google.common.io.Closer;
import com.typesafe.config.Config;
import javax.annotation.Nullable;
import gobblin.configuration.ConfigurationKeys;
import gobblin.configuration.State;
import gobblin.metrics.graphite.GraphiteConnectionType;
import gobblin.metrics.graphite.GraphiteEventReporter;
import gobblin.metrics.graphite.GraphiteReporter;
import gobblin.metrics.influxdb.InfluxDBConnectionType;
import gobblin.metrics.influxdb.InfluxDBEventReporter;
import gobblin.metrics.influxdb.InfluxDBReporter;
import gobblin.metrics.reporter.OutputStreamEventReporter;
import gobblin.metrics.reporter.OutputStreamReporter;
import gobblin.metrics.reporter.ScheduledReporter;
import gobblin.password.PasswordManager;
import gobblin.util.PropertiesUtils;
/**
* A class that represents a set of metrics associated with a given name.
*
* @author Yinan Li
*/
public class GobblinMetrics {
public static final String METRICS_STATE_CUSTOM_TAGS = "metrics.state.custom.tags";
protected static final GobblinMetricsRegistry GOBBLIN_METRICS_REGISTRY = GobblinMetricsRegistry.getInstance();
/**
* Enumeration of metric types.
*/
public enum MetricType {
COUNTER, METER, GAUGE
}
private static final Logger LOGGER = LoggerFactory.getLogger(GobblinMetrics.class);
/**
* Check whether metrics collection and reporting are enabled or not.
*
* @param properties Configuration properties
* @return whether metrics collection and reporting are enabled
*/
public static boolean isEnabled(Properties properties) {
return PropertiesUtils
.getPropAsBoolean(properties, ConfigurationKeys.METRICS_ENABLED_KEY, ConfigurationKeys.DEFAULT_METRICS_ENABLED);
}
/**
* Check whether metrics collection and reporting are enabled or not.
*
* @param state a {@link State} object containing configuration properties
* @return whether metrics collection and reporting are enabled
*/
public static boolean isEnabled(State state) {
return Boolean
.valueOf(state.getProp(ConfigurationKeys.METRICS_ENABLED_KEY, ConfigurationKeys.DEFAULT_METRICS_ENABLED));
}
/**
* Check whether metrics collection and reporting are enabled or not.
*
* @param cfg a {@link State} object containing configuration properties
* @return whether metrics collection and reporting are enabled
*/
public static boolean isEnabled(Config cfg) {
return cfg.hasPath(ConfigurationKeys.METRICS_ENABLED_KEY) ? cfg.getBoolean(ConfigurationKeys.METRICS_ENABLED_KEY)
: Boolean.parseBoolean(ConfigurationKeys.DEFAULT_METRICS_ENABLED);
}
/**
* Get a {@link GobblinMetrics} instance with the given ID.
*
* @param id the given {@link GobblinMetrics} ID
* @return a {@link GobblinMetrics} instance
*/
public static GobblinMetrics get(String id) {
return get(id, null);
}
/**
* Get a {@link GobblinMetrics} instance with the given ID and parent {@link MetricContext}.
*
* @param id the given {@link GobblinMetrics} ID
* @param parentContext the given parent {@link MetricContext}
* @return a {@link GobblinMetrics} instance
*/
public static GobblinMetrics get(String id, MetricContext parentContext) {
return get(id, parentContext, Lists.<Tag<?>>newArrayList());
}
/**
* Get a {@link GobblinMetrics} instance with the given ID, parent {@link MetricContext},
* and list of {@link Tag}s.
*
* @param id the given {@link GobblinMetrics} ID
* @param parentContext the given parent {@link MetricContext}
* @param tags the given list of {@link Tag}s
* @return a {@link GobblinMetrics} instance
*/
public static GobblinMetrics get(final String id, final MetricContext parentContext, final List<Tag<?>> tags) {
return GOBBLIN_METRICS_REGISTRY.getOrDefault(id, new Callable<GobblinMetrics>() {
@Override
public GobblinMetrics call()
throws Exception {
return new GobblinMetrics(id, parentContext, tags);
}
});
}
/**
* Remove the {@link GobblinMetrics} instance associated with the given ID.
*
* @param id the given {@link GobblinMetrics} ID
*/
public static void remove(String id) {
GOBBLIN_METRICS_REGISTRY.remove(id);
}
/**
* Add a {@link List} of {@link Tag}s to a {@link gobblin.configuration.State} with key {@link #METRICS_STATE_CUSTOM_TAGS}.
*
* <p>
* {@link gobblin.metrics.Tag}s under this key can later be parsed using the method {@link #getCustomTagsFromState}.
* </p>
*
* @param state {@link gobblin.configuration.State} state to add the tag to.
* @param tags list of {@link Tag}s to add.
*/
public static void addCustomTagToState(State state, List<? extends Tag<?>> tags) {
for (Tag<?> tag : tags) {
state.appendToListProp(METRICS_STATE_CUSTOM_TAGS, tag.toString());
}
}
/**
* Add a {@link Tag} to a {@link gobblin.configuration.State} with key {@link #METRICS_STATE_CUSTOM_TAGS}.
*
* <p>
* {@link gobblin.metrics.Tag}s under this key can later be parsed using the method {@link #getCustomTagsFromState}.
* </p>
*
* @param state {@link gobblin.configuration.State} state to add the tag to.
* @param tag {@link Tag} to add.
*/
public static void addCustomTagToState(State state, Tag<?> tag) {
state.appendToListProp(METRICS_STATE_CUSTOM_TAGS, tag.toString());
}
/**
* Add {@link List} of {@link Tag}s to a {@link Properties} with key {@link #METRICS_STATE_CUSTOM_TAGS}.
* <p>
* Also see {@link #addCustomTagToState(State, Tag)} , {@link #addCustomTagToProperties(Properties, Tag)}
* </p>
*
* <p>
* The {@link Properties} passed can be used to build a {@link State}.
* {@link gobblin.metrics.Tag}s under this key can later be parsed using the method {@link #getCustomTagsFromState}.
* </p>
*
* @param properties {@link Properties} to add the tag to.
* @param tags list of {@link Tag}s to add.
*/
public static void addCustomTagsToProperties(Properties properties, List<Tag<?>> tags) {
for (Tag<?> tag : tags) {
addCustomTagToProperties(properties, tag);
}
}
/**
* Add a {@link Tag} to a {@link Properties} with key {@link #METRICS_STATE_CUSTOM_TAGS}.
* Also see {@link #addCustomTagToState(State, Tag)}
*
* <p>
* The {@link Properties} passed can be used to build a {@link State}.
* {@link gobblin.metrics.Tag}s under this key can later be parsed using the method {@link #getCustomTagsFromState}.
* </p>
*
* @param properties {@link Properties} to add the tag to.
* @param tag {@link Tag} to add.
*/
public static void addCustomTagToProperties(Properties properties, Tag<?> tag) {
// Build a state wrapper to add custom tag to property
State state = new State(properties);
addCustomTagToState(state, tag);
}
/**
* Parse custom {@link gobblin.metrics.Tag}s from property {@link #METRICS_STATE_CUSTOM_TAGS}
* in the input {@link gobblin.configuration.State}.
* @param state {@link gobblin.configuration.State} possibly containing custom tags.
* @return List of {@link gobblin.metrics.Tag} parsed from input.
*/
public static List<Tag<?>> getCustomTagsFromState(State state) {
List<Tag<?>> tags = Lists.newArrayList();
for (String tagKeyValue : state.getPropAsList(METRICS_STATE_CUSTOM_TAGS, "")) {
Tag<?> tag = Tag.fromString(tagKeyValue);
if (tag != null) {
tags.add(tag);
}
}
return tags;
}
protected final String id;
protected final MetricContext metricContext;
// Closer for closing the metric output stream
protected final Closer codahaleReportersCloser = Closer.create();
// JMX metric reporter
private Optional<JmxReporter> jmxReporter = Optional.absent();
// Custom metric reporters instantiated through reflection
private final List<com.codahale.metrics.ScheduledReporter> codahaleScheduledReporters = Lists.newArrayList();
// A flag telling whether metric reporting has started or not
private volatile boolean metricsReportingStarted = false;
protected GobblinMetrics(String id, MetricContext parentContext, List<Tag<?>> tags) {
this.id = id;
this.metricContext = parentContext == null ? new MetricContext.Builder(id).addTags(tags).build()
: parentContext.childBuilder(id).addTags(tags).build();
}
/**
* Get the wrapped {@link com.codahale.metrics.MetricRegistry} instance.
*
* @return wrapped {@link com.codahale.metrics.MetricRegistry} instance
*/
public MetricContext getMetricContext() {
return this.metricContext;
}
/**
* Get the ID of this {@link GobblinMetrics}.
*
* @return ID of this {@link GobblinMetrics}
*/
public String getId() {
return this.id;
}
/**
* Get the name of this {@link GobblinMetrics}.
*
* <p>
* This method is currently equivalent to {@link #getId()}.
* </p>
*
* @return name of this {@link GobblinMetrics}
*/
public String getName() {
return this.id;
}
/**
* Get a {@link Meter} with the given name prefix and suffixes.
*
* @param prefix the given name prefix
* @param suffixes the given name suffixes
* @return a {@link Meter} with the given name prefix and suffixes
*/
public Meter getMeter(String prefix, String... suffixes) {
return this.metricContext.meter(MetricRegistry.name(prefix, suffixes));
}
/**
* Get a {@link Counter} with the given name prefix and suffixes.
*
* @param prefix the given name prefix
* @param suffixes the given name suffixes
* @return a {@link Counter} with the given name prefix and suffixes
*/
public Counter getCounter(String prefix, String... suffixes) {
return this.metricContext.counter(MetricRegistry.name(prefix, suffixes));
}
/**
* Get a {@link Histogram} with the given name prefix and suffixes.
*
* @param prefix the given name prefix
* @param suffixes the given name suffixes
* @return a {@link Histogram} with the given name prefix and suffixes
*/
public Histogram getHistogram(String prefix, String... suffixes) {
return this.metricContext.histogram(MetricRegistry.name(prefix, suffixes));
}
/**
* Get a {@link Timer} with the given name prefix and suffixes.
*
* @param prefix the given name prefix
* @param suffixes the given name suffixes
* @return a {@link Timer} with the given name prefix and suffixes
*/
public Timer getTimer(String prefix, String... suffixes) {
return this.metricContext.timer(MetricRegistry.name(prefix, suffixes));
}
/**
* Starts metric reporting and appends the given metrics file suffix to the current value of
* {@link ConfigurationKeys#METRICS_FILE_SUFFIX}.
*/
public void startMetricReportingWithFileSuffix(State state, String metricsFileSuffix) {
Properties metricsReportingProps = new Properties();
metricsReportingProps.putAll(state.getProperties());
String oldMetricsFileSuffix =
state.getProp(ConfigurationKeys.METRICS_FILE_SUFFIX, ConfigurationKeys.DEFAULT_METRICS_FILE_SUFFIX);
if (Strings.isNullOrEmpty(oldMetricsFileSuffix)) {
oldMetricsFileSuffix = metricsFileSuffix;
} else {
oldMetricsFileSuffix += "." + metricsFileSuffix;
}
metricsReportingProps.setProperty(ConfigurationKeys.METRICS_FILE_SUFFIX, oldMetricsFileSuffix);
startMetricReporting(metricsReportingProps);
}
/**
* Start metric reporting.
*
* @param configuration configuration properties
*/
public void startMetricReporting(Configuration configuration) {
Properties props = new Properties();
for (Map.Entry<String, String> entry : configuration) {
props.put(entry.getKey(), entry.getValue());
}
startMetricReporting(props);
}
/**
* Start metric reporting.
*
* @param properties configuration properties
*/
public void startMetricReporting(Properties properties) {
if (this.metricsReportingStarted) {
LOGGER.warn("Metric reporting has already started");
return;
}
TimeUnit reportTimeUnit = TimeUnit.MILLISECONDS;
long reportInterval = Long.parseLong(properties
.getProperty(ConfigurationKeys.METRICS_REPORT_INTERVAL_KEY, ConfigurationKeys.DEFAULT_METRICS_REPORT_INTERVAL));
ScheduledReporter.setReportingInterval(properties, reportInterval, reportTimeUnit);
// Build and start the JMX reporter
buildJmxMetricReporter(properties);
if (this.jmxReporter.isPresent()) {
LOGGER.info("Will start reporting metrics to JMX");
this.jmxReporter.get().start();
}
// Build all other reporters
buildFileMetricReporter(properties);
buildKafkaMetricReporter(properties);
buildGraphiteMetricReporter(properties);
buildInfluxDBMetricReporter(properties);
buildCustomMetricReporters(properties);
// Start reporters that implement gobblin.metrics.report.ScheduledReporter
RootMetricContext.get().startReporting();
// Start reporters that implement com.codahale.metrics.ScheduledReporter
for (com.codahale.metrics.ScheduledReporter scheduledReporter : this.codahaleScheduledReporters) {
scheduledReporter.start(reportInterval, reportTimeUnit);
}
this.metricsReportingStarted = true;
}
/**
* Stop metric reporting.
*/
public void stopMetricsReporting() {
if (!this.metricsReportingStarted) {
LOGGER.warn("Metric reporting has not started yet");
return;
}
// Stop the JMX reporter
if (this.jmxReporter.isPresent()) {
this.jmxReporter.get().stop();
}
// Trigger and stop reporters that implement gobblin.metrics.report.ScheduledReporter
RootMetricContext.get().stopReporting();
// Trigger and stop reporters that implement com.codahale.metrics.ScheduledReporter
for (com.codahale.metrics.ScheduledReporter scheduledReporter : this.codahaleScheduledReporters) {
scheduledReporter.report();
}
try {
this.codahaleReportersCloser.close();
} catch (IOException ioe) {
LOGGER.error("Failed to close metric output stream for job " + this.id, ioe);
}
this.metricsReportingStarted = false;
}
private void buildFileMetricReporter(Properties properties) {
if (!Boolean.valueOf(properties.getProperty(ConfigurationKeys.METRICS_REPORTING_FILE_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_FILE_ENABLED))) {
return;
}
LOGGER.info("Reporting metrics to log files");
if (!properties.containsKey(ConfigurationKeys.METRICS_LOG_DIR_KEY)) {
LOGGER.error(
"Not reporting metrics to log files because " + ConfigurationKeys.METRICS_LOG_DIR_KEY + " is undefined");
return;
}
try {
String fsUri = properties.getProperty(ConfigurationKeys.FS_URI_KEY, ConfigurationKeys.LOCAL_FS_URI);
FileSystem fs = FileSystem.get(URI.create(fsUri), new Configuration());
// Each job gets its own metric log subdirectory
Path metricsLogDir = new Path(properties.getProperty(ConfigurationKeys.METRICS_LOG_DIR_KEY), this.getName());
if (!fs.exists(metricsLogDir) && !fs.mkdirs(metricsLogDir)) {
LOGGER.error("Failed to create metric log directory for metrics " + this.getName());
return;
}
// Add a suffix to file name if specified in properties.
String metricsFileSuffix =
properties.getProperty(ConfigurationKeys.METRICS_FILE_SUFFIX, ConfigurationKeys.DEFAULT_METRICS_FILE_SUFFIX);
if (!Strings.isNullOrEmpty(metricsFileSuffix) && !metricsFileSuffix.startsWith(".")) {
metricsFileSuffix = "." + metricsFileSuffix;
}
// Each job run gets its own metric log file
Path metricLogFile =
new Path(metricsLogDir, this.id + metricsFileSuffix + ".metrics.log");
boolean append = false;
// Append to the metric file if it already exists
if (fs.exists(metricLogFile)) {
LOGGER.info(String.format("Metric log file %s already exists, appending to it", metricLogFile));
append = true;
}
OutputStream output = append ? fs.append(metricLogFile) : fs.create(metricLogFile, true);
OutputStreamReporter.Factory.newBuilder().outputTo(output).build(properties);
this.codahaleScheduledReporters.add(this.codahaleReportersCloser
.register(OutputStreamEventReporter.forContext(RootMetricContext.get()).outputTo(output).build()));
LOGGER.info("Will start reporting metrics to directory " + metricsLogDir);
} catch (IOException ioe) {
LOGGER.error("Failed to build file metric reporter for job " + this.id, ioe);
}
}
private void buildJmxMetricReporter(Properties properties) {
if (!Boolean.valueOf(properties.getProperty(ConfigurationKeys.METRICS_REPORTING_JMX_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_JMX_ENABLED))) {
return;
}
LOGGER.info("Reporting metrics to JMX");
this.jmxReporter = Optional.of(codahaleReportersCloser.register(JmxReporter.forRegistry(RootMetricContext.get()).
convertRatesTo(TimeUnit.SECONDS).convertDurationsTo(TimeUnit.MILLISECONDS).build()));
}
private void buildKafkaMetricReporter(Properties properties) {
if (!Boolean.valueOf(properties.getProperty(ConfigurationKeys.METRICS_REPORTING_KAFKA_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_KAFKA_ENABLED))) {
return;
}
buildScheduledReporter(properties, ConfigurationKeys.DEFAULT_METRICS_REPORTING_KAFKA_REPORTER_CLASS, Optional.of("Kafka"));
}
private void buildGraphiteMetricReporter(Properties properties) {
boolean metricsEnabled = PropertiesUtils
.getPropAsBoolean(properties, ConfigurationKeys.METRICS_REPORTING_GRAPHITE_METRICS_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_GRAPHITE_METRICS_ENABLED);
if (metricsEnabled) {
LOGGER.info("Reporting metrics to Graphite");
}
boolean eventsEnabled = PropertiesUtils
.getPropAsBoolean(properties, ConfigurationKeys.METRICS_REPORTING_GRAPHITE_EVENTS_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_GRAPHITE_EVENTS_ENABLED);
if (eventsEnabled) {
LOGGER.info("Reporting events to Graphite");
}
if (!metricsEnabled && !eventsEnabled) {
return;
}
try {
Preconditions.checkArgument(properties.containsKey(ConfigurationKeys.METRICS_REPORTING_GRAPHITE_HOSTNAME),
"Graphite hostname is missing.");
} catch (IllegalArgumentException exception) {
LOGGER.error("Not reporting to Graphite due to missing Graphite configuration(s).", exception);
return;
}
String hostname = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_GRAPHITE_HOSTNAME);
int port = Integer.parseInt(properties.getProperty(ConfigurationKeys.METRICS_REPORTING_GRAPHITE_PORT,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_GRAPHITE_PORT));
GraphiteConnectionType connectionType;
String type = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_GRAPHITE_SENDING_TYPE,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_GRAPHITE_SENDING_TYPE).toUpperCase();
try {
connectionType = GraphiteConnectionType.valueOf(type);
} catch (IllegalArgumentException exception) {
LOGGER
.warn("Graphite Reporter connection type " + type + " not recognized. Will use TCP for sending.", exception);
connectionType = GraphiteConnectionType.TCP;
}
if (metricsEnabled) {
try {
GraphiteReporter.Factory.newBuilder().withConnectionType(connectionType)
.withConnection(hostname, port).withMetricContextName(
this.metricContext.getName()) //contains the current job id
.build(properties);
} catch (IOException e) {
LOGGER.error("Failed to create Graphite metrics reporter. Will not report metrics to Graphite.", e);
}
}
if (eventsEnabled) {
boolean emitValueAsKey = PropertiesUtils
.getPropAsBoolean(properties, ConfigurationKeys.METRICS_REPORTING_GRAPHITE_EVENTS_VALUE_AS_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_GRAPHITE_EVENTS_VALUE_AS_KEY);
String eventsPortProp = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_GRAPHITE_EVENTS_PORT);
int eventsPort = (eventsPortProp == null) ? (metricsEnabled ? port
: Integer.parseInt(ConfigurationKeys.METRICS_REPORTING_GRAPHITE_PORT)) : Integer.parseInt(eventsPortProp);
try {
GraphiteEventReporter eventReporter =
GraphiteEventReporter.Factory.forContext(RootMetricContext.get())
.withConnectionType(connectionType)
.withConnection(hostname, eventsPort)
.withEmitValueAsKey(emitValueAsKey)
.build();
this.codahaleScheduledReporters.add(this.codahaleReportersCloser.register(eventReporter));
}
catch (IOException e) {
LOGGER.error("Failed to create Graphite event reporter. Will not report events to Graphite.", e);
}
}
}
private void buildInfluxDBMetricReporter(Properties properties) {
boolean metricsEnabled = PropertiesUtils
.getPropAsBoolean(properties, ConfigurationKeys.METRICS_REPORTING_INFLUXDB_METRICS_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_INFLUXDB_METRICS_ENABLED);
if (metricsEnabled) {
LOGGER.info("Reporting metrics to InfluxDB");
}
boolean eventsEnabled = PropertiesUtils
.getPropAsBoolean(properties, ConfigurationKeys.METRICS_REPORTING_INFLUXDB_EVENTS_ENABLED_KEY,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_INFLUXDB_EVENTS_ENABLED);
if (eventsEnabled) {
LOGGER.info("Reporting events to InfluxDB");
}
if (!metricsEnabled && !eventsEnabled) {
return;
}
try {
Preconditions.checkArgument(properties.containsKey(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_DATABASE),
"InfluxDB database name is missing.");
} catch (IllegalArgumentException exception) {
LOGGER.error("Not reporting to InfluxDB due to missing InfluxDB configuration(s).", exception);
return;
}
String url = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_URL);
String username = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_USER);
String password = PasswordManager.getInstance(properties)
.readPassword(properties.getProperty(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_PASSWORD));
String database = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_DATABASE);
InfluxDBConnectionType connectionType;
String type = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_SENDING_TYPE,
ConfigurationKeys.DEFAULT_METRICS_REPORTING_INFLUXDB_SENDING_TYPE).toUpperCase();
try {
connectionType = InfluxDBConnectionType.valueOf(type);
} catch (IllegalArgumentException exception) {
LOGGER
.warn("InfluxDB Reporter connection type " + type + " not recognized. Will use TCP for sending.", exception);
connectionType = InfluxDBConnectionType.TCP;
}
if (metricsEnabled) {
try {
InfluxDBReporter.Factory.newBuilder().withConnectionType(connectionType)
.withConnection(url, username, password, database).withMetricContextName(
this.metricContext.getName()) // contains the current job id
.build(properties);
} catch (IOException e) {
LOGGER.error("Failed to create InfluxDB metrics reporter. Will not report metrics to InfluxDB.", e);
}
}
if (eventsEnabled) {
String eventsDbProp = properties.getProperty(ConfigurationKeys.METRICS_REPORTING_INFLUXDB_EVENTS_DATABASE);
String eventsDatabase = (eventsDbProp == null) ? (metricsEnabled ? database : null) : eventsDbProp;
try {
InfluxDBEventReporter eventReporter =
InfluxDBEventReporter.Factory.forContext(RootMetricContext.get())
.withConnectionType(connectionType)
.withConnection(url, username, password, eventsDatabase)
.build();
this.codahaleScheduledReporters.add(this.codahaleReportersCloser.register(eventReporter));
}
catch (IOException e) {
LOGGER.error("Failed to create InfluxDB event reporter. Will not report events to InfluxDB.", e);
}
}
}
/**
* Build scheduled metrics reporters by reflection from the property
* {@link gobblin.configuration.ConfigurationKeys#METRICS_CUSTOM_BUILDERS}. This allows users to specify custom
* reporters for Gobblin runtime without having to modify the code.
*/
private void buildCustomMetricReporters(Properties properties) {
String reporterClasses = properties.getProperty(ConfigurationKeys.METRICS_CUSTOM_BUILDERS);
if (Strings.isNullOrEmpty(reporterClasses)) {
return;
}
for (String reporterClass : Splitter.on(",").split(reporterClasses)) {
buildScheduledReporter(properties, reporterClass, Optional.<String>absent());
}
}
private void buildScheduledReporter(Properties properties, String reporterClass, Optional<String> reporterSink) {
try {
Class<?> clazz = Class.forName(reporterClass);
if (CustomCodahaleReporterFactory.class.isAssignableFrom(clazz)) {
CustomCodahaleReporterFactory customCodahaleReporterFactory =
((CustomCodahaleReporterFactory) clazz.getConstructor().newInstance());
com.codahale.metrics.ScheduledReporter scheduledReporter = this.codahaleReportersCloser
.register(customCodahaleReporterFactory.newScheduledReporter(RootMetricContext.get(), properties));
String reporterSinkMsg = reporterSink.isPresent()?"to " + reporterSink.get():"";
LOGGER.info("Will start reporting metrics " + reporterSinkMsg + " using " + reporterClass);
this.codahaleScheduledReporters.add(scheduledReporter);
} else if (CustomReporterFactory.class.isAssignableFrom(clazz)) {
CustomReporterFactory customReporterFactory = ((CustomReporterFactory) clazz.getConstructor().newInstance());
customReporterFactory.newScheduledReporter(properties);
LOGGER.info("Will start reporting metrics using " + reporterClass);
} else {
throw new IllegalArgumentException("Class " + reporterClass +
" specified by key " + ConfigurationKeys.METRICS_CUSTOM_BUILDERS + " must implement: "
+ CustomCodahaleReporterFactory.class + " or " + CustomReporterFactory.class);
}
} catch (ClassNotFoundException exception) {
LOGGER.warn(String
.format("Failed to create metric reporter: requested CustomReporterFactory %s not found.", reporterClass),
exception);
} catch (NoSuchMethodException exception) {
LOGGER.warn(String.format("Failed to create metric reporter: requested CustomReporterFactory %s "
+ "does not have parameterless constructor.", reporterClass), exception);
} catch (Exception exception) {
LOGGER.warn("Could not create metric reporter from builder " + reporterClass + ".", exception);
}
}
}