From e12520dfeb3881fda701ae3870be1352455572de Mon Sep 17 00:00:00 2001 From: Karsten Schnitter Date: Wed, 28 Feb 2024 16:05:57 +0100 Subject: [PATCH] Fix Dynatrace Exported to Generate DELTA Temporality (#174) 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 Co-authored-by: Anika Solanka --- .../DynatraceMetricsExporterProvider.java | 56 +++++++++++++------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java index bb858a1..8f908d9 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java @@ -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; @@ -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); @@ -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); @@ -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(); }