org.apache.hudi.metrics.datadog.DatadogReporter Maven / Gradle / Ivy
/*
* 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 org.apache.hudi.metrics.datadog;
import org.apache.hudi.common.util.Option;
import org.apache.hudi.common.util.ValidationUtils;
import org.apache.hudi.common.util.collection.Pair;
import org.apache.hudi.metrics.MetricUtils;
import com.codahale.metrics.Clock;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.Timer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* A reporter which publishes metric values to Datadog API.
*
* Responsible for collecting and composing metrics payload.
*
* Internally use {@link DatadogHttpClient} to interact with Datadog APIs.
*/
public class DatadogReporter extends ScheduledReporter {
private static final Logger LOG = LoggerFactory.getLogger(DatadogReporter.class);
private final DatadogHttpClient client;
private final String prefix;
private final Option host;
private final Option> tags;
private final Clock clock;
protected DatadogReporter(
MetricRegistry registry,
DatadogHttpClient client,
String prefix,
Option host,
Option> tags,
MetricFilter filter,
TimeUnit rateUnit,
TimeUnit durationUnit) {
super(registry, "hudi-datadog-reporter", filter, rateUnit, durationUnit);
this.client = client;
this.prefix = prefix;
this.host = host;
this.tags = tags;
this.clock = Clock.defaultClock();
}
@Override
public void report(
SortedMap gauges,
SortedMap counters,
SortedMap histograms,
SortedMap meters,
SortedMap timers) {
Map, List>>> labelsPair = gauges.keySet().stream().map(MetricUtils::getLabelsAndMetricList)
.collect(Collectors.groupingBy(Pair::getValue));
labelsPair.entrySet().forEach(labelsKeyValue -> {
final long now = clock.getTime() / 1000;
final PayloadBuilder builder = new PayloadBuilder();
builder.withMetricType(MetricType.gauge);
gauges.forEach(
(metricName, metric) -> builder.addGauge(prefix(MetricUtils.getMetricAndLabels(metricName).getKey()),
now, (long) metric.getValue()));
host.ifPresent(builder::withHost);
List runTimeLables = labelsKeyValue.getKey();
tags.map(runTimeLables::addAll);
builder.withTags(runTimeLables);
client.send(builder.build());
});
}
protected String prefix(String... components) {
return MetricRegistry.name(prefix, components);
}
@Override
public void stop() {
try {
super.stop();
} finally {
try {
client.close();
} catch (IOException e) {
LOG.warn("Error disconnecting from Datadog.", e);
}
}
}
/**
* Build payload that contains metrics data.
*
* Refer to Datadog API reference https://docs.datadoghq.com/api/?lang=bash#post-timeseries-points
*/
static class PayloadBuilder {
private static final ObjectMapper MAPPER = new ObjectMapper();
private final ObjectNode payload;
private final ArrayNode series;
private MetricType type;
PayloadBuilder() {
payload = MAPPER.createObjectNode();
series = payload.putArray("series");
}
PayloadBuilder withMetricType(MetricType type) {
this.type = type;
return this;
}
PayloadBuilder addGauge(String metric, long timestamp, long gaugeValue) {
ValidationUtils.checkState(type == MetricType.gauge);
ObjectNode seriesItem = MAPPER.createObjectNode().put("metric", metric);
seriesItem.putArray("points").addArray().add(timestamp).add(gaugeValue);
series.add(seriesItem);
return this;
}
PayloadBuilder withHost(String host) {
series.forEach(seriesItem -> ((ObjectNode) seriesItem).put("host", host));
return this;
}
PayloadBuilder withTags(List tags) {
series.forEach(seriesItem -> {
((ObjectNode) seriesItem)
.putArray("tags")
.addAll(tags.stream().map(TextNode::new).collect(Collectors.toList()));
});
return this;
}
String build() {
return payload.toString();
}
}
enum MetricType {
gauge;
}
}