Skip to content

Commit

Permalink
Fix Dynatrace Exported to Generate DELTA Temporality (#174)
Browse files Browse the repository at this point in the history
The Dynatrace OTel metrics API requires DELTA temporality,
since CUMULATIVE is currently unsupported. This change will
allow configuration of the temporality via

`otel.exporter.dynatrace.metrics.temporality.preference`

This config now defaults to "delta" instead to the hard-coded
"cumulative" before. Usually, no configuration is required.

Signed-off-by: Karsten Schnitter <k.schnitter@sap.com>
Co-authored-by: Anika Solanka <anika.solanka@sap.com>
  • Loading branch information
KarstenSchnitter and Anika-Sol authored Feb 28, 2024
1 parent 9c22c17 commit e12520d
Showing 1 changed file with 39 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import io.pivotal.cfenv.core.CfService;

import java.time.Duration;
import java.util.Locale;
import java.util.function.Function;
import java.util.logging.Logger;

Expand Down Expand Up @@ -46,20 +47,39 @@ private static Duration getTimeOut(ConfigProperties config) {
return timeout != null ? timeout : config.getDuration("otel.exporter.dynatrace.timeout");
}

private static AggregationTemporalitySelector getAggregationTemporalitySelector(ConfigProperties config) {
String temporalityStr = config.getString("otel.exporter.dynatrace.metrics.temporality.preference");
if (temporalityStr == null) {
return AggregationTemporalitySelector.deltaPreferred();
}
AggregationTemporalitySelector temporalitySelector;
switch (temporalityStr.toLowerCase(Locale.ROOT)) {
case "cumulative":
return AggregationTemporalitySelector.alwaysCumulative();
case "delta":
return AggregationTemporalitySelector.deltaPreferred();
case "lowmemory":
return AggregationTemporalitySelector.lowMemory();
default:
throw new ConfigurationException("Unrecognized aggregation temporality: " + temporalityStr);
}
}

private static DefaultAggregationSelector getDefaultAggregationSelector(ConfigProperties config) {
String defaultHistogramAggregation =
config.getString("otel.exporter.dynatrace.metrics.default.histogram.aggregation");
if (defaultHistogramAggregation == null) {
return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation());
return DefaultAggregationSelector.getDefault()
.with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation());
}
if (AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram())
.equalsIgnoreCase(defaultHistogramAggregation)) {
return
DefaultAggregationSelector.getDefault()
.with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram());
.equalsIgnoreCase(defaultHistogramAggregation)) {
return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM,
Aggregation.base2ExponentialBucketHistogram());
} else if (AggregationUtil.aggregationName(explicitBucketHistogram())
.equalsIgnoreCase(defaultHistogramAggregation)) {
return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, Aggregation.explicitBucketHistogram());
.equalsIgnoreCase(defaultHistogramAggregation)) {
return DefaultAggregationSelector.getDefault()
.with(InstrumentType.HISTOGRAM, Aggregation.explicitBucketHistogram());
} else {
throw new ConfigurationException(
"Unrecognized default histogram aggregation: " + defaultHistogramAggregation);
Expand All @@ -83,16 +103,19 @@ public MetricExporter createExporter(ConfigProperties config) {
return NoopMetricExporter.getInstance();
}

LOG.info("Creating metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")");
LOG.info(
"Creating metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")");

String apiUrl = cfService.getCredentials().getString(CRED_DYNATRACE_APIURL);
if (isBlank(apiUrl)) {
LOG.warning("Credential \"" + CRED_DYNATRACE_APIURL + "\" not found. Skipping dynatrace exporter configuration");
LOG.warning(
"Credential \"" + CRED_DYNATRACE_APIURL + "\" not found. Skipping dynatrace exporter configuration");
return NoopMetricExporter.getInstance();
}
String tokenName = config.getString("otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name");
if (isBlank(tokenName)) {
LOG.warning("Configuration \"otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name\" not found. Skipping dynatrace exporter configuration");
LOG.warning(
"Configuration \"otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name\" not found. Skipping dynatrace exporter configuration");
return NoopMetricExporter.getInstance();
}
String apiToken = cfService.getCredentials().getString(tokenName);
Expand All @@ -102,19 +125,18 @@ public MetricExporter createExporter(ConfigProperties config) {
}

OtlpHttpMetricExporterBuilder builder = OtlpHttpMetricExporter.builder();
builder.setEndpoint(apiUrl + DT_APIURL_METRICS_SUFFIX)
.setCompression(getCompression(config))
.addHeader("Authorization", "Api-Token " + apiToken)
.setRetryPolicy(RetryPolicy.getDefault())
.setAggregationTemporalitySelector(AggregationTemporalitySelector.alwaysCumulative())
.setDefaultAggregationSelector(getDefaultAggregationSelector(config));
builder.setEndpoint(apiUrl + DT_APIURL_METRICS_SUFFIX).setCompression(getCompression(config))
.addHeader("Authorization", "Api-Token " + apiToken).setRetryPolicy(RetryPolicy.getDefault())
.setAggregationTemporalitySelector(getAggregationTemporalitySelector(config))
.setDefaultAggregationSelector(getDefaultAggregationSelector(config));

Duration timeOut = getTimeOut(config);
if (timeOut != null) {
builder.setTimeout(timeOut);
}

LOG.info("Created metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")");
LOG.info(
"Created metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")");
return builder.build();
}

Expand Down

0 comments on commit e12520d

Please sign in to comment.