Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable Transformers #92

Merged
merged 4 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,53 @@ NewRelicReporter reporter = NewRelicReporter.build(metricRegistry, metricBatchSe
.commonAttributes(commonAttributes)
.build();

reporter.start(15, TimeUnit.SECONDS);
```
## Customizing Reported Metrics
If you would like to customize the way metric names or attributes are reported to New Relic, you will want to supply
customizers to the `NewRelicReporterBuilder`.

Consider metrics with tags encoded in their names, formatted like so: `"metricName[tag:value,othertag:othervalue]"`,
you might set up a reporter to report the metric name as `metricName` and add merge of the key/value pairs in `name`
with the `commonAttributes`.

```
MetricRegistry metricRegistry = new MetricRegistry(); // If you're already using dropwizard-metrics you may already have one of these.
...
String apiKey = "<YOUR_SECRET_API_KEY>";
MetricBatchSender metricBatchSender = MetricBatchSenderFactory
.fromHttpImplementation(OkHttpPoster::new)
.createBatchSender(apiKey);

Attributes commonAttributes = new Attributes()
.put("host", InetAddress.getLocalHost().getHostName())
.put("appName", "Your Application Name Here")
.put("other", "any other common attributes you wish");

MetricAttributeCustomizer mergeAttributesFromTaggedMetricName =
(name, metric, attributes) -> {
Attributes tagsAsAttributes = new Attributes();

// get a stream of each tag:value pair within the square brackets of name and add
// each pair to the tagsAsAttributes Attributes object
Stream.of(name.substring(name.indexOf('['), name.indexOf(']')).split(","))
.forEach(
str -> {
String[] keyValuePair = str.split(":");

tagsAsAttributes.put(keyValuePair[0], keyValuePair[1]);
});

return tagsAsAttributes.putAll(attributes);
};

NewRelicReporter reporter = NewRelicReporter.build(metricRegistry, metricBatchSender)
.commonAttributes(commonAttributes)
// customizer to strip encoded tags from metric name
.metricNameCustomizer(name -> name.substring(0, name.indexOf('[')))
.metricAttributeCustomizer(mergeAttributesFromTaggedMetricName)
.build();

reporter.start(15, TimeUnit.SECONDS);
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import com.codahale.metrics.newrelic.transformer.HistogramTransformer;
import com.codahale.metrics.newrelic.transformer.MeterTransformer;
import com.codahale.metrics.newrelic.transformer.TimerTransformer;
import com.codahale.metrics.newrelic.transformer.customizer.MetricAttributesCustomizer;
import com.codahale.metrics.newrelic.transformer.customizer.MetricNameCustomizer;
import com.codahale.metrics.newrelic.util.TimeTracker;
import com.newrelic.telemetry.Attributes;
import com.newrelic.telemetry.TelemetryClient;
Expand All @@ -35,6 +37,8 @@ public class NewRelicReporterBuilder {
private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
private Attributes commonAttributes = new Attributes();
private Set<MetricAttribute> disabledMetricAttributes = Collections.emptySet();
private MetricNameCustomizer nameCustomizer = MetricNameCustomizer.DEFAULT;
private MetricAttributesCustomizer attributeCustomizer = MetricAttributesCustomizer.DEFAULT;

public static NewRelicReporterBuilder forRegistry(
MetricRegistry registry, MetricBatchSender metricBatchSender) {
Expand Down Expand Up @@ -77,6 +81,17 @@ public NewRelicReporterBuilder disabledMetricAttributes(
return this;
}

public NewRelicReporterBuilder metricNameCustomizer(MetricNameCustomizer nameCustomizer) {
this.nameCustomizer = nameCustomizer;
return this;
}

public NewRelicReporterBuilder metricAttributeCustomizer(
MetricAttributesCustomizer attributeCustomizer) {
this.attributeCustomizer = attributeCustomizer;
return this;
}

public NewRelicReporter build() {
long rateFactor = rateUnit.toSeconds(1);
double durationFactor = durationUnit.toNanos(1);
Expand All @@ -85,12 +100,21 @@ public NewRelicReporter build() {

TimeTracker timeTracker = new TimeTracker(Clock.defaultClock());
MeterTransformer meterTransformer =
MeterTransformer.build(timeTracker, rateFactor, metricAttributePredicate);
MeterTransformer.build(
timeTracker, rateFactor, metricAttributePredicate, nameCustomizer, attributeCustomizer);
TimerTransformer timerTransformer =
TimerTransformer.build(timeTracker, rateFactor, durationFactor, metricAttributePredicate);
GaugeTransformer gaugeTransformer = new GaugeTransformer();
CounterTransformer counterTransformer = new CounterTransformer();
HistogramTransformer histogramTransformer = HistogramTransformer.build(timeTracker);
TimerTransformer.build(
timeTracker,
rateFactor,
durationFactor,
metricAttributePredicate,
nameCustomizer,
attributeCustomizer);
GaugeTransformer gaugeTransformer = new GaugeTransformer(nameCustomizer, attributeCustomizer);
CounterTransformer counterTransformer =
new CounterTransformer(nameCustomizer, attributeCustomizer);
HistogramTransformer histogramTransformer =
HistogramTransformer.build(timeTracker, nameCustomizer, attributeCustomizer);

return new NewRelicReporter(
timeTracker,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,49 @@

import com.codahale.metrics.Clock;
import com.codahale.metrics.Counter;
import com.codahale.metrics.newrelic.transformer.customizer.MetricAttributesCustomizer;
import com.codahale.metrics.newrelic.transformer.customizer.MetricNameCustomizer;
import com.newrelic.telemetry.Attributes;
import com.newrelic.telemetry.metrics.Gauge;
import com.newrelic.telemetry.metrics.Metric;
import java.util.Collection;
import java.util.Objects;

public class CounterTransformer implements DropWizardMetricTransformer<Counter> {

private final Clock clock;
private final MetricNameCustomizer nameCustomizer;
private final MetricAttributesCustomizer attributeCustomizer;

public CounterTransformer() {
this(Clock.defaultClock());
}

public CounterTransformer(
MetricNameCustomizer nameCustomizer, MetricAttributesCustomizer attributeCustomizer) {
this(Clock.defaultClock(), nameCustomizer, attributeCustomizer);
}

// exists for testing
CounterTransformer(Clock clock) {
this(clock, MetricNameCustomizer.DEFAULT, MetricAttributesCustomizer.DEFAULT);
}

CounterTransformer(
Clock clock,
MetricNameCustomizer nameCustomizer,
MetricAttributesCustomizer attributeCustomizer) {
this.clock = clock;
this.nameCustomizer = Objects.requireNonNull(nameCustomizer);
this.attributeCustomizer = Objects.requireNonNull(attributeCustomizer);
}

@Override
public Collection<Metric> transform(String name, Counter counter) {
return singleton(new Gauge(name, counter.getCount(), clock.getTime(), new Attributes()));
String customizedName = nameCustomizer.customizeMetricName(name);
Attributes customizedAttributes =
attributeCustomizer.customizeMetricAttributes(name, counter, new Attributes());
return singleton(
new Gauge(customizedName, counter.getCount(), clock.getTime(), customizedAttributes));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,69 @@

import com.codahale.metrics.Clock;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.newrelic.transformer.customizer.MetricAttributesCustomizer;
import com.codahale.metrics.newrelic.transformer.customizer.MetricNameCustomizer;
import com.newrelic.telemetry.Attributes;
import com.newrelic.telemetry.metrics.Metric;
import java.util.Collection;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GaugeTransformer implements DropWizardMetricTransformer<Gauge> {

private static final Logger LOG = LoggerFactory.getLogger(GaugeTransformer.class);
private final Clock clock;
private final MetricNameCustomizer nameCustomizer;
private final MetricAttributesCustomizer attributeCustomizer;

public GaugeTransformer() {
this(Clock.defaultClock());
this(MetricNameCustomizer.DEFAULT, MetricAttributesCustomizer.DEFAULT);
}

public GaugeTransformer(
MetricNameCustomizer nameCustomizer, MetricAttributesCustomizer attributeCustomizer) {
this(Clock.defaultClock(), nameCustomizer, attributeCustomizer);
}

// exists for testing
public GaugeTransformer(Clock clock) {
this(clock, MetricNameCustomizer.DEFAULT, MetricAttributesCustomizer.DEFAULT);
}

public GaugeTransformer(
Clock clock,
MetricNameCustomizer nameCustomizer,
MetricAttributesCustomizer attributeCustomizer) {
this.clock = clock;
this.nameCustomizer = Objects.requireNonNull(nameCustomizer);
this.attributeCustomizer = Objects.requireNonNull(attributeCustomizer);
}

@Override
public Collection<Metric> transform(String name, Gauge gauge) {
String customizedName = nameCustomizer.customizeMetricName(name);
Attributes customizedAttributes =
attributeCustomizer.customizeMetricAttributes(name, gauge, new Attributes());
long timestamp = clock.getTime();
Object gaugeValue = gauge.getValue();
if (gaugeValue == null) {
LOG.debug("Ignoring gauge with null value. Gauge name: {}", name);
LOG.debug(
"Ignoring gauge with null value. Gauge name: {}, Gauge attributes: {}",
customizedName,
customizedAttributes);
return emptySet();
}
if (gaugeValue instanceof Number) {
Metric metric =
new com.newrelic.telemetry.metrics.Gauge(
name, ((Number) gaugeValue).doubleValue(), timestamp, new Attributes());
customizedName, ((Number) gaugeValue).doubleValue(), timestamp, customizedAttributes);
return singleton(metric);
}
LOG.debug(
"Ignoring gauge [name: {}] with value of type {} (non-numeric gauges are unsupported)",
name,
"Ignoring gauge [name: {}, Attributes: {}] with value of type {} (non-numeric gauges are unsupported)",
customizedName,
customizedAttributes,
gaugeValue.getClass().getName());
return emptySet();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
import static java.util.stream.Stream.concat;

import com.codahale.metrics.Histogram;
import com.codahale.metrics.newrelic.transformer.customizer.MetricAttributesCustomizer;
import com.codahale.metrics.newrelic.transformer.customizer.MetricNameCustomizer;
import com.codahale.metrics.newrelic.transformer.interfaces.CountingTransformer;
import com.codahale.metrics.newrelic.transformer.interfaces.SamplingTransformer;
import com.codahale.metrics.newrelic.util.TimeTracker;
import com.newrelic.telemetry.Attributes;
import com.newrelic.telemetry.metrics.Metric;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Supplier;

public class HistogramTransformer implements DropWizardMetricTransformer<Histogram> {
Expand All @@ -25,30 +28,65 @@ public class HistogramTransformer implements DropWizardMetricTransformer<Histogr

private final CountingTransformer countingTransformer;
private final SamplingTransformer samplingTransformer;
private final MetricNameCustomizer nameCustomizer;
private final MetricAttributesCustomizer attributeCustomizer;

public static HistogramTransformer build(TimeTracker timeTracker) {
return build(timeTracker, MetricNameCustomizer.DEFAULT, MetricAttributesCustomizer.DEFAULT);
}

public static HistogramTransformer build(
TimeTracker timeTracker,
MetricNameCustomizer nameCustomizer,
MetricAttributesCustomizer attributeCustomizer) {
return new HistogramTransformer(
new CountingTransformer(timeTracker), new SamplingTransformer(timeTracker, 1L));
new CountingTransformer(timeTracker),
new SamplingTransformer(timeTracker, 1L),
nameCustomizer,
attributeCustomizer);
}

HistogramTransformer(
CountingTransformer countingTransformer, SamplingTransformer samplingTransformer) {
CountingTransformer countingTransformer,
SamplingTransformer samplingTransformer,
MetricNameCustomizer nameCustomizer,
MetricAttributesCustomizer attributeCustomizer) {
this.countingTransformer = countingTransformer;
this.samplingTransformer = samplingTransformer;
this.nameCustomizer = Objects.requireNonNull(nameCustomizer);
this.attributeCustomizer = Objects.requireNonNull(attributeCustomizer);
}

HistogramTransformer(
CountingTransformer countingTransformer, SamplingTransformer samplingTransformer) {
this(
countingTransformer,
samplingTransformer,
MetricNameCustomizer.DEFAULT,
MetricAttributesCustomizer.DEFAULT);
}

@Override
public Collection<Metric> transform(String name, Histogram histogram) {
Collection<Metric> counts = countingTransformer.transform(name, histogram, ATTRIBUTES_SUPPLIER);
String customizedName = nameCustomizer.customizeMetricName(name);
Supplier<Attributes> customizedAttributeSupplier =
() ->
attributeCustomizer.customizeMetricAttributes(
name, histogram, ATTRIBUTES_SUPPLIER.get());

Collection<Metric> counts =
countingTransformer.transform(customizedName, histogram, customizedAttributeSupplier);

return concat(
counts.stream(),
samplingTransformer.transform(name, histogram, ATTRIBUTES_SUPPLIER).stream())
samplingTransformer
.transform(customizedName, histogram, customizedAttributeSupplier)
.stream())
.collect(toSet());
}

@Override
public void onHistogramRemoved(String name) {
countingTransformer.remove(name);
countingTransformer.remove(nameCustomizer.customizeMetricName(name));
}
}
Loading