Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4fd760d
Move Dynatrace metrics exporter for API v1 to a separate package (#2553)
pirgeo Apr 20, 2021
4f53492
Adding validation logic into validate in DynatraceConfig
jonatan-ivanov Apr 20, 2021
5213b53
Polishing Dynatrace V1 refactor
jonatan-ivanov Apr 21, 2021
1115208
Breaking circular dependency between DynatraceMeterRegistry and Dynat…
jonatan-ivanov Apr 21, 2021
2db046a
Merge branch 'master' into move-dynatrace-registry-v1
jonatan-ivanov Apr 26, 2021
8965981
Add an exporter for the Dynatrace metrics API v2
pirgeo Apr 27, 2021
1845333
remove a comma that might lead to invalid ingestion result parsing
pirgeo Apr 30, 2021
54fd167
Address code review comments
pirgeo May 12, 2021
bcd543d
Use new library features to make the exporter code more concise
pirgeo May 20, 2021
e9b0c5d
Merge branch 'add-dynatrace-registry-v2' of git://github.com/dynatrac…
jonatan-ivanov May 25, 2021
42b90c0
Merge branch 'main' into add-dynatrace-registry-v2
jonatan-ivanov May 25, 2021
90efd94
Making dependency configuration calls consistent
jonatan-ivanov May 24, 2021
ed896c6
Clean-up comments in DynatraceConfig (no validation changes)
jonatan-ivanov May 25, 2021
d68f179
Fixing license header in DynatraceExporterV2
jonatan-ivanov May 25, 2021
2781781
Converting config field to local variable in ctor of DynatraceMeterRe…
jonatan-ivanov May 25, 2021
bc2f34a
DynatraceExporterV2 refactor: renamings, method order/visibility
jonatan-ivanov May 26, 2021
4414474
Simplify DynatraceExporterV2
jonatan-ivanov May 26, 2021
439381f
Adding back partitioning for Dynatrace V1
jonatan-ivanov May 26, 2021
be72004
More formatting for DynatraceExporterV2
jonatan-ivanov May 26, 2021
c1f1464
Simplifying toMeterLine and createMeterLine methods
jonatan-ivanov May 26, 2021
23f7f73
Improving naming and error messages
jonatan-ivanov May 26, 2021
3469809
Fixing tests after refactor
jonatan-ivanov May 26, 2021
72a93b3
Merge branch 'main' into add-dynatrace-registry-v2
jonatan-ivanov May 27, 2021
387a54f
Implement fallback for config uri.
jonatan-ivanov May 25, 2021
168c25f
add a warning on unlikely configurations and check if the token needs…
pirgeo May 25, 2021
800519b
Fix/improve DynatraceExporterV2Test, round one:
jonatan-ivanov Jun 2, 2021
5bfdd25
Fix/improve DynatraceExporterV2Test, round two:
jonatan-ivanov Jun 2, 2021
395e087
DynatraceExporterV2Test: static imports, nullable problems
jonatan-ivanov Jun 2, 2021
06fd508
Initialize DynatraceExporterV2Test the JUnit way
jonatan-ivanov Jun 2, 2021
81cf135
Improving DynatraceExporterV2Test and test the interaction with the h…
jonatan-ivanov Jun 3, 2021
499a4d8
Fixing overwriting user-defined percentiles.
jonatan-ivanov Jun 3, 2021
d69d75e
re-enabling empty tag tests
jonatan-ivanov Jun 3, 2021
a1d76fa
Adding DynatraceMeterRegistryTest
jonatan-ivanov Jun 3, 2021
f4a488d
+javadoc
jonatan-ivanov Jun 3, 2021
a2571c3
Rename blank tag tests, part one
jonatan-ivanov Jun 7, 2021
fe48423
Rename blank tag tests, part two
jonatan-ivanov Jun 7, 2021
21da805
Removing artificially added 0th percentile meters
jonatan-ivanov Jun 8, 2021
688fa6c
Merge branch 'main' into add-dynatrace-registry-v2
jonatan-ivanov Jun 8, 2021
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
1 change: 1 addition & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ def VERSIONS = [
'ch.qos.logback:logback-classic:1.2.+',
'colt:colt:1.2.0',
'com.amazonaws:aws-java-sdk-cloudwatch:latest.release',
'com.dynatrace.metric.util:dynatrace-metric-utils-java:0.3.+',
'com.fasterxml.jackson.core:jackson-databind:latest.release',
'com.github.ben-manes.caffeine:caffeine:2.+',
'com.github.charithe:kafka-junit:latest.release',
Expand Down
2 changes: 2 additions & 0 deletions implementations/micrometer-registry-dynatrace/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ dependencies {
api project(':micrometer-core')

implementation 'org.slf4j:slf4j-api'

implementation 'com.dynatrace.metric.util:dynatrace-metric-utils-java'

testImplementation project(':micrometer-test')
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@
* @since 1.8.0
*/
public abstract class AbstractDynatraceExporter {

protected DynatraceConfig config;
protected Clock clock;
protected HttpSender httpClient;
protected final DynatraceConfig config;
protected final Clock clock;
protected final HttpSender httpClient;

public AbstractDynatraceExporter(DynatraceConfig config, Clock clock, HttpSender httpClient) {
this.config = config;
Expand All @@ -46,5 +45,5 @@ public TimeUnit getBaseTimeUnit() {
return TimeUnit.MILLISECONDS;
}

public abstract void export(@Nonnull List<List<Meter>> partitions);
public abstract void export(@Nonnull List<Meter> meters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

/**
* An enum containing valid Dynatrace API versions.
*
* @author Georg Pirklbauer
* @since 1.8.0
*/
public enum DynatraceApiVersion {
V1,
V2
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
*/
package io.micrometer.dynatrace;

import com.dynatrace.metric.util.DynatraceMetricApiConstants;
import io.micrometer.core.instrument.config.validate.Validated;
import io.micrometer.core.instrument.step.StepRegistryConfig;
import io.micrometer.core.instrument.util.StringUtils;
import io.micrometer.core.lang.Nullable;

import java.util.Collections;
import java.util.Map;

import static io.micrometer.core.instrument.config.MeterRegistryConfigValidator.*;
import static io.micrometer.core.instrument.config.validate.PropertyValidator.*;

Expand All @@ -38,21 +41,25 @@ default String prefix() {
}

default String apiToken() {
return getSecret(this, "apiToken").required().get();
if (apiVersion() == DynatraceApiVersion.V1) {
return getSecret(this, "apiToken").required().get();
}
return getSecret(this, "apiToken").orElse("");
}

default String uri() {
return getUrlString(this, "uri").required().get();
if (apiVersion() == DynatraceApiVersion.V1) {
return getUrlString(this, "uri").required().get();
}
return getUrlString(this, "uri").orElse(DynatraceMetricApiConstants.getDefaultOneAgentEndpoint());
}

default String deviceId() {
return getString(this, "deviceId").required().get();
}

default String technologyType() {
return getSecret(this, "technologyType")
.map(val -> StringUtils.isEmpty(val) ? "java" : val)
.get();
return getSecret(this, "technologyType").orElse("java");
}

/**
Expand All @@ -67,14 +74,25 @@ default String group() {
}

/**
* Return the version of the target Dynatrace API.
* Return the version of the target Dynatrace API. Defaults to v1 if not provided.
*
* @return a {@link DynatraceApiVersion} containing the version of the targeted Dynatrace API.
*/
default DynatraceApiVersion apiVersion() {
// if not specified, defaults to v1 for backwards compatibility.
return getEnum(this, DynatraceApiVersion.class, "apiVersion")
.orElse(DynatraceApiVersion.V1);
return getEnum(this, DynatraceApiVersion.class, "apiVersion").orElse(DynatraceApiVersion.V1);
}

default String metricKeyPrefix() {
return getString(this, "metricKeyPrefix").orElse("");
}

default Map<String, String> defaultDimensions() {
return Collections.emptyMap();
}

default Boolean enrichWithOneAgentMetadata() {
return getBoolean(this, "enrichWithOneAgentMetadata").orElse(true);
}

@Override
Expand All @@ -86,15 +104,17 @@ default Validated<?> validate() {
if (apiVersionValidation.isValid()) {
return checkAll(this,
config -> {
if (config.apiVersion() == DynatraceApiVersion.V1) {
if (config.apiVersion() == DynatraceApiVersion.V1) {
return checkAll(this,
checkRequired("apiToken", DynatraceConfig::apiToken),
checkRequired("uri", DynatraceConfig::uri),
checkRequired("deviceId", DynatraceConfig::deviceId),
check("technologyType", DynatraceConfig::technologyType).andThen(Validated::nonBlank)
);
} else {
return apiVersionValidation; // V2 validation comes here
return checkAll(this,
checkRequired("uri", DynatraceConfig::uri)
);
}
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
package io.micrometer.dynatrace;

import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.step.StepMeterRegistry;
import io.micrometer.core.instrument.util.MeterPartition;
import io.micrometer.core.instrument.util.NamedThreadFactory;
import io.micrometer.core.ipc.http.HttpSender;
import io.micrometer.core.ipc.http.HttpUrlConnectionSender;
import io.micrometer.dynatrace.v1.DynatraceExporterV1;
import io.micrometer.dynatrace.v2.DynatraceExporterV2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -42,7 +45,6 @@ public class DynatraceMeterRegistry extends StepMeterRegistry {
private static final ThreadFactory DEFAULT_THREAD_FACTORY = new NamedThreadFactory("dynatrace-metrics-publisher");
private static final Logger logger = LoggerFactory.getLogger(DynatraceMeterRegistry.class);

private final DynatraceConfig config;
private final AbstractDynatraceExporter exporter;

@SuppressWarnings("deprecation")
Expand All @@ -53,13 +55,15 @@ public DynatraceMeterRegistry(DynatraceConfig config, Clock clock) {
private DynatraceMeterRegistry(DynatraceConfig config, Clock clock, ThreadFactory threadFactory, HttpSender httpClient) {
super(config, clock);

this.config = config;
if (config.apiVersion() == DynatraceApiVersion.V1) {
logger.info("Using Dynatrace v1 exporter.");
this.exporter = new DynatraceExporterV1(config, clock, httpClient);
if (config.apiVersion() == DynatraceApiVersion.V2) {
logger.info("Exporting to Dynatrace metrics API v2");
this.exporter = new DynatraceExporterV2(config, clock, httpClient);
registerMinPercentile();
} else {
throw new IllegalArgumentException("Only v1 export is available at the moment.");
logger.info("Exporting to Dynatrace metrics API v1");
this.exporter = new DynatraceExporterV1(config, clock, httpClient);
}

start(threadFactory);
}

Expand All @@ -69,14 +73,31 @@ public static Builder builder(DynatraceConfig config) {

@Override
protected void publish() {
exporter.export(MeterPartition.partition(this, config.batchSize()));
exporter.export(this.getMeters());
}

@Override
protected TimeUnit getBaseTimeUnit() {
return this.exporter.getBaseTimeUnit();
}

/**
* As the micrometer summary statistics (DistributionSummary, and a number of timer meter types)
* do not provide the minimum values that are required by Dynatrace to ingest summary metrics,
* we add the 0% percentile to each summary statistic and use that as the minimum value.
*/
private void registerMinPercentile() {
config().meterFilter(new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
return DistributionStatisticConfig.builder()
.percentiles(0)
.build()
.merge(config);
}
});
}

public static class Builder {
private final DynatraceConfig config;

Expand Down Expand Up @@ -110,4 +131,3 @@ public DynatraceMeterRegistry build() {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.config.NamingConvention;
import io.micrometer.core.instrument.distribution.HistogramSnapshot;
import io.micrometer.core.instrument.util.MeterPartition;
import io.micrometer.core.instrument.util.StringUtils;
import io.micrometer.core.ipc.http.HttpSender;
import io.micrometer.core.lang.Nullable;
import io.micrometer.dynatrace.AbstractDynatraceExporter;
import io.micrometer.dynatrace.DynatraceApiVersion;
import io.micrometer.dynatrace.DynatraceConfig;
import io.micrometer.dynatrace.DynatraceNamingConvention;
import org.slf4j.Logger;
Expand Down Expand Up @@ -67,15 +69,15 @@ public DynatraceExporterV1(DynatraceConfig config, Clock clock, HttpSender httpC
super(config, clock, httpClient);

this.customMetricEndpointTemplate = config.uri() + "/api/v1/timeseries/";
this.namingConvention = new DynatraceNamingConvention();
this.namingConvention = new DynatraceNamingConvention(NamingConvention.dot, DynatraceApiVersion.V1);
}

@Override
public void export(@Nonnull List<List<Meter>> partitions) {
public void export(@Nonnull List<Meter> meters) {
String customDeviceMetricEndpoint = config.uri() + "/api/v1/entity/infrastructure/custom/" +
config.deviceId() + "?api-token=" + config.apiToken();

for (List<Meter> batch : partitions) {
for (List<Meter> batch : new MeterPartition(meters, config.batchSize())) {
final List<DynatraceCustomMetric> series = batch.stream()
.flatMap(meter -> meter.match(
this::writeMeter,
Expand Down
Loading