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

Add support for Dynatrace metrics v2 API #26258

Original file line number Diff line number Diff line change
@@ -16,47 +16,38 @@

package org.springframework.boot.actuate.autoconfigure.metrics.export.dynatrace;

import java.util.Map;

import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;

/**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring Dynatrace
* metrics export.
*
* @author Andy Wilkinson
* @author Georg Pirklbauer
* @since 2.1.0
*/
@ConfigurationProperties(prefix = "management.metrics.export.dynatrace")
public class DynatraceProperties extends StepRegistryProperties {

/**
* Dynatrace authentication token.
*/
private String apiToken;
private final V1 v1 = new V1();

/**
* ID of the custom device that is exporting metrics to Dynatrace.
*/
private String deviceId;
private final V2 v2 = new V2();

/**
* Technology type for exported metrics. Used to group metrics under a logical
* technology name in the Dynatrace UI.
* Dynatrace authentication token.
*/
private String technologyType = "java";
private String apiToken;

/**
* URI to ship metrics to. Should be used for SaaS, self managed instances or to
* en-route through an internal proxy.
*/
private String uri;

/**
* Group for exported metrics. Used to specify custom device group name in the
* Dynatrace UI.
*/
private String group;

public String getApiToken() {
return this.apiToken;
}
@@ -65,20 +56,26 @@ public void setApiToken(String apiToken) {
this.apiToken = apiToken;
}

@Deprecated
@DeprecatedConfigurationProperty(replacement = "management.metrics.export.dynatrace.v1.device-id")
public String getDeviceId() {
return this.deviceId;
return this.v1.getDeviceId();
}

@Deprecated
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
this.v1.setDeviceId(deviceId);
}

@Deprecated
@DeprecatedConfigurationProperty(replacement = "management.metrics.export.dynatrace.v1.technology-type")
public String getTechnologyType() {
return this.technologyType;
return this.v1.getTechnologyType();
}

@Deprecated
public void setTechnologyType(String technologyType) {
this.technologyType = technologyType;
this.v1.setTechnologyType(technologyType);
}

public String getUri() {
@@ -89,12 +86,112 @@ public void setUri(String uri) {
this.uri = uri;
}

@Deprecated
@DeprecatedConfigurationProperty(replacement = "management.metrics.export.dynatrace.v1.group")
public String getGroup() {
return this.group;
return this.v1.getGroup();
}

@Deprecated
public void setGroup(String group) {
this.group = group;
this.v1.setGroup(group);
}

public V1 getV1() {
return this.v1;
}

public V2 getV2() {
return this.v2;
}

public static class V1 {

/**
* ID of the custom device that is exporting metrics to Dynatrace.
*/
private String deviceId;

/**
* Group for exported metrics. Used to specify custom device group name in the
* Dynatrace UI.
*/
private String group;

/**
* Technology type for exported metrics. Used to group metrics under a logical
* technology name in the Dynatrace UI.
*/
private String technologyType = "java";

public String getDeviceId() {
return this.deviceId;
}

public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}

public String getGroup() {
return this.group;
}

public void setGroup(String group) {
this.group = group;
}

public String getTechnologyType() {
return this.technologyType;
}

public void setTechnologyType(String technologyType) {
this.technologyType = technologyType;
}

}

public static class V2 {

/**
* Default dimensions that are added to all metrics in the form of key-value
* pairs. These are overwritten by Micrometer tags if they use the same key.
*/
private Map<String, String> defaultDimensions;

/**
* Whether to enable Dynatrace metadata export.
*/
private boolean enrichWithDynatraceMetadata = true;

/**
* Prefix string that is added to all exported metrics.
*/
private String metricKeyPrefix;

public Map<String, String> getDefaultDimensions() {
return this.defaultDimensions;
}

public void setDefaultDimensions(Map<String, String> defaultDimensions) {
this.defaultDimensions = defaultDimensions;
}

public boolean isEnrichWithDynatraceMetadata() {
return this.enrichWithDynatraceMetadata;
}

public void setEnrichWithDynatraceMetadata(Boolean enrichWithDynatraceMetadata) {
this.enrichWithDynatraceMetadata = enrichWithDynatraceMetadata;
}

public String getMetricKeyPrefix() {
return this.metricKeyPrefix;
}

public void setMetricKeyPrefix(String metricKeyPrefix) {
this.metricKeyPrefix = metricKeyPrefix;
}

}

}
Original file line number Diff line number Diff line change
@@ -16,14 +16,21 @@

package org.springframework.boot.actuate.autoconfigure.metrics.export.dynatrace;

import java.util.Map;
import java.util.function.Function;

import io.micrometer.dynatrace.DynatraceApiVersion;
import io.micrometer.dynatrace.DynatraceConfig;

import org.springframework.boot.actuate.autoconfigure.metrics.export.dynatrace.DynatraceProperties.V1;
import org.springframework.boot.actuate.autoconfigure.metrics.export.dynatrace.DynatraceProperties.V2;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;

/**
* Adapter to convert {@link DynatraceProperties} to a {@link DynatraceConfig}.
*
* @author Andy Wilkinson
* @author Georg Pirklbauer
pirgeo marked this conversation as resolved.
Show resolved Hide resolved
*/
class DynatracePropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<DynatraceProperties>
implements DynatraceConfig {
@@ -44,12 +51,12 @@ public String apiToken() {

@Override
public String deviceId() {
return get(DynatraceProperties::getDeviceId, DynatraceConfig.super::deviceId);
return get(v1(V1::getDeviceId), DynatraceConfig.super::deviceId);
}

@Override
public String technologyType() {
return get(DynatraceProperties::getTechnologyType, DynatraceConfig.super::technologyType);
return get(v1(V1::getTechnologyType), DynatraceConfig.super::technologyType);
}

@Override
@@ -59,7 +66,36 @@ public String uri() {

@Override
public String group() {
return get(DynatraceProperties::getGroup, DynatraceConfig.super::group);
return get(v1(V1::getGroup), DynatraceConfig.super::group);
}

@Override
public DynatraceApiVersion apiVersion() {
return get((properties) -> (properties.getV1().getDeviceId() != null) ? DynatraceApiVersion.V1
: DynatraceApiVersion.V2, DynatraceConfig.super::apiVersion);
}

@Override
public String metricKeyPrefix() {
return get(v2(V2::getMetricKeyPrefix), DynatraceConfig.super::metricKeyPrefix);
}

@Override
public Map<String, String> defaultDimensions() {
return get(v2(V2::getDefaultDimensions), DynatraceConfig.super::defaultDimensions);
}

@Override
public boolean enrichWithDynatraceMetadata() {
return get(v2(V2::isEnrichWithDynatraceMetadata), DynatraceConfig.super::enrichWithDynatraceMetadata);
}

private <V> Function<DynatraceProperties, V> v1(Function<V1, V> getter) {
return (properties) -> getter.apply(properties.getV1());
}

private <V> Function<DynatraceProperties, V> v2(Function<V2, V> getter) {
return (properties) -> getter.apply(properties.getV2());
}

}
Original file line number Diff line number Diff line change
@@ -48,14 +48,15 @@ void backsOffWithoutAClock() {
}

@Test
void failsWithoutAUri() {
void failsWithADeviceIdWithoutAUri() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.metrics.export.dynatrace.device-id:dev-1")
.run((context) -> assertThat(context).hasFailed());
}

@Test
void autoConfiguresConfigAndMeterRegistry() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class).with(mandatoryProperties())
this.contextRunner.withUserConfiguration(BaseConfiguration.class).with(v1MandatoryProperties())
.run((context) -> assertThat(context).hasSingleBean(DynatraceMeterRegistry.class)
.hasSingleBean(DynatraceConfig.class));
}
@@ -85,22 +86,33 @@ void allowsCustomConfigToBeUsed() {

@Test
void allowsCustomRegistryToBeUsed() {
this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class).with(mandatoryProperties())
this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class).with(v1MandatoryProperties())
.run((context) -> assertThat(context).hasSingleBean(DynatraceMeterRegistry.class)
.hasBean("customRegistry").hasSingleBean(DynatraceConfig.class));
}

@Test
void stopsMeterRegistryWhenContextIsClosed() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class).with(mandatoryProperties()).run((context) -> {
void stopsMeterRegistryForV1ApiWhenContextIsClosed() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class).with(v1MandatoryProperties())
.run((context) -> {
DynatraceMeterRegistry registry = context.getBean(DynatraceMeterRegistry.class);
assertThat(registry.isClosed()).isFalse();
context.close();
assertThat(registry.isClosed()).isTrue();
});
}

@Test
void stopsMeterRegistryForV2ApiWhenContextIsClosed() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class).run((context) -> {
DynatraceMeterRegistry registry = context.getBean(DynatraceMeterRegistry.class);
assertThat(registry.isClosed()).isFalse();
context.close();
assertThat(registry.isClosed()).isTrue();
});
}

private Function<ApplicationContextRunner, ApplicationContextRunner> mandatoryProperties() {
private Function<ApplicationContextRunner, ApplicationContextRunner> v1MandatoryProperties() {
return (runner) -> runner.withPropertyValues(
"management.metrics.export.dynatrace.uri=https://dynatrace.example.com",
"management.metrics.export.dynatrace.api-token=abcde",
Loading