Polish "Add properties for Dynatrace metrics API v2 ingest with Micrometer"

pull/27405/head
Andy Wilkinson 3 years ago
parent 3161164912
commit 84455f86fd

@ -18,10 +18,9 @@ package org.springframework.boot.actuate.autoconfigure.metrics.export.dynatrace;
import java.util.Map;
import io.micrometer.dynatrace.DynatraceApiVersion;
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
@ -34,73 +33,21 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "management.metrics.export.dynatrace")
public class DynatraceProperties extends StepRegistryProperties {
/**
* The Dynatrace metrics API version that metrics should be sent to. Defaults to v1.
* Required to define which API is used for export.
*/
private DynatraceApiVersion apiVersion = DynatraceApiVersion.V1;
private final V1 v1 = new V1();
private final V2 v2 = new V2();
/**
* Dynatrace authentication token.
*
* API v1: required, API v2: optional
*/
private String apiToken;
/**
* ID of the custom device that is exporting metrics to Dynatrace.
*
* API v1: required, API v2: not applicable (ignored)
*/
private String deviceId;
/**
* Technology type for exported metrics. Used to group metrics under a logical
* technology name in the Dynatrace UI.
*
* API v1: required, API v2: not applicable (ignored)
*/
private String technologyType = "java";
/**
* URI to ship metrics to. Should be used for SaaS, self managed instances or to
* en-route through an internal proxy.
*
* API v1: required, API v2: optional
*/
private String uri;
/**
* Group for exported metrics. Used to specify custom device group name in the
* Dynatrace UI.
*
* API v1: required, API v2: not applicable (ignored)
*/
private String group;
/**
* An optional prefix string that is added to all metrics exported.
*
* API v1: not applicable (ignored), API v2: optional
*/
private String metricKeyPrefix;
/**
* An optional Boolean that allows enabling of the Dynatrace metadata export. On by
* default.
*
* API v1: not applicable (ignored), API v2: optional
*/
private Boolean enrichWithDynatraceMetadata = true;
/**
* Optional 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.
*
* API v1: not applicable (ignored), API v2: optional
*/
private Map<String, String> defaultDimensions;
public String getApiToken() {
return this.apiToken;
}
@ -109,20 +56,26 @@ public class DynatraceProperties extends StepRegistryProperties {
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() {
@ -133,44 +86,112 @@ public class DynatraceProperties extends StepRegistryProperties {
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 String getMetricKeyPrefix() {
return this.metricKeyPrefix;
public V1 getV1() {
return this.v1;
}
public void setMetricKeyPrefix(String metricKeyPrefix) {
this.metricKeyPrefix = metricKeyPrefix;
public V2 getV2() {
return this.v2;
}
public Boolean getEnrichWithDynatraceMetadata() {
return this.enrichWithDynatraceMetadata;
}
public static class V1 {
public void setEnrichWithDynatraceMetadata(Boolean enrichWithDynatraceMetadata) {
this.enrichWithDynatraceMetadata = enrichWithDynatraceMetadata;
}
/**
* ID of the custom device that is exporting metrics to Dynatrace.
*/
private String deviceId;
public Map<String, String> getDefaultDimensions() {
return this.defaultDimensions;
}
/**
* Group for exported metrics. Used to specify custom device group name in the
* Dynatrace UI.
*/
private String group;
public void setDefaultDimensions(Map<String, String> defaultDimensions) {
this.defaultDimensions = defaultDimensions;
}
/**
* 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 DynatraceApiVersion getApiVersion() {
return this.apiVersion;
}
public void setApiVersion(DynatraceApiVersion apiVersion) {
this.apiVersion = apiVersion;
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;
}
}
}

@ -17,10 +17,13 @@
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;
/**
@ -48,12 +51,12 @@ class DynatracePropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapt
@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
@ -63,27 +66,36 @@ class DynatracePropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapt
@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(DynatraceProperties::getApiVersion, DynatraceConfig.super::apiVersion);
return get((properties) -> (properties.getV1().getDeviceId() != null) ? DynatraceApiVersion.V1
: DynatraceApiVersion.V2, DynatraceConfig.super::apiVersion);
}
@Override
public String metricKeyPrefix() {
return get(DynatraceProperties::getMetricKeyPrefix, DynatraceConfig.super::metricKeyPrefix);
return get(v2(V2::getMetricKeyPrefix), DynatraceConfig.super::metricKeyPrefix);
}
@Override
public Map<String, String> defaultDimensions() {
return get(DynatraceProperties::getDefaultDimensions, DynatraceConfig.super::defaultDimensions);
return get(v2(V2::getDefaultDimensions), DynatraceConfig.super::defaultDimensions);
}
@Override
public boolean enrichWithDynatraceMetadata() {
return get(DynatraceProperties::getEnrichWithDynatraceMetadata,
DynatraceConfig.super::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());
}
}

@ -48,14 +48,15 @@ class DynatraceMetricsExportAutoConfigurationTests {
}
@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,14 +86,25 @@ class DynatraceMetricsExportAutoConfigurationTests {
@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();
@ -100,7 +112,7 @@ class DynatraceMetricsExportAutoConfigurationTests {
});
}
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",

@ -46,6 +46,7 @@ class DynatracePropertiesConfigAdapterTests {
}
@Test
@Deprecated
void whenPropertiesDeviceIdIsSetAdapterDeviceIdReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.setDeviceId("dev-1");
@ -53,6 +54,15 @@ class DynatracePropertiesConfigAdapterTests {
}
@Test
@Deprecated
void whenPropertiesV1DeviceIdIsSetAdapterDeviceIdReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.getV1().setDeviceId("dev-1");
assertThat(new DynatracePropertiesConfigAdapter(properties).deviceId()).isEqualTo("dev-1");
}
@Test
@Deprecated
void whenPropertiesTechnologyTypeIsSetAdapterTechnologyTypeReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.setTechnologyType("tech-1");
@ -60,6 +70,14 @@ class DynatracePropertiesConfigAdapterTests {
}
@Test
void whenPropertiesV1TechnologyTypeIsSetAdapterTechnologyTypeReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.getV1().setTechnologyType("tech-1");
assertThat(new DynatracePropertiesConfigAdapter(properties).technologyType()).isEqualTo("tech-1");
}
@Test
@Deprecated
void whenPropertiesGroupIsSetAdapterGroupReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.setGroup("group-1");
@ -67,53 +85,73 @@ class DynatracePropertiesConfigAdapterTests {
}
@Test
void whenPropertiesApiVersionIsSetAdapterGroupReturnsIt() {
void whenPropertiesV1GroupIsSetAdapterGroupReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.setApiVersion(DynatraceApiVersion.V1);
properties.getV1().setGroup("group-1");
assertThat(new DynatracePropertiesConfigAdapter(properties).group()).isEqualTo("group-1");
}
@Test
@SuppressWarnings("deprecation")
void whenDeviceIdIsSetThenAdapterApiVersionIsV1() {
DynatraceProperties properties = new DynatraceProperties();
properties.setDeviceId("dev-1");
assertThat(new DynatracePropertiesConfigAdapter(properties).apiVersion()).isSameAs(DynatraceApiVersion.V1);
}
@Test
void whenV1DeviceIdIsSetThenAdapterApiVersionIsV1() {
DynatraceProperties properties = new DynatraceProperties();
properties.getV1().setDeviceId("dev-1");
assertThat(new DynatracePropertiesConfigAdapter(properties).apiVersion()).isSameAs(DynatraceApiVersion.V1);
}
@Test
void whenDeviceIdIsNotSetThenAdapterApiVersionIsV2() {
DynatraceProperties properties = new DynatraceProperties();
assertThat(new DynatracePropertiesConfigAdapter(properties).apiVersion()).isSameAs(DynatraceApiVersion.V2);
}
@Test
void whenPropertiesMetricKeyPrefixIsSetAdapterGroupReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.setMetricKeyPrefix("my.prefix");
properties.getV2().setMetricKeyPrefix("my.prefix");
assertThat(new DynatracePropertiesConfigAdapter(properties).metricKeyPrefix()).isEqualTo("my.prefix");
}
@Test
void whenPropertiesEnrichWithOneAgentMetadataIsSetAdapterGroupReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
properties.setEnrichWithDynatraceMetadata(true);
properties.getV2().setEnrichWithDynatraceMetadata(true);
assertThat(new DynatracePropertiesConfigAdapter(properties).enrichWithDynatraceMetadata()).isTrue();
}
@Test
void whenPropertiesDefaultDimensionsIsSetAdapterGroupReturnsIt() {
DynatraceProperties properties = new DynatraceProperties();
HashMap<String, String> defaultDimensions = new HashMap<String, String>() {
{
put("dim1", "value1");
put("dim2", "value2");
}
};
properties.setDefaultDimensions(defaultDimensions);
HashMap<String, String> defaultDimensions = new HashMap<>();
defaultDimensions.put("dim1", "value1");
defaultDimensions.put("dim2", "value2");
properties.getV2().setDefaultDimensions(defaultDimensions);
assertThat(new DynatracePropertiesConfigAdapter(properties).defaultDimensions())
.containsExactlyEntriesOf(defaultDimensions);
}
@Test
@SuppressWarnings("deprecation")
void defaultValues() {
DynatraceProperties properties = new DynatraceProperties();
assertThat(properties.getApiToken()).isNull();
assertThat(properties.getUri()).isNull();
assertThat(properties.getV1().getDeviceId()).isNull();
assertThat(properties.getV1().getTechnologyType()).isEqualTo("java");
assertThat(properties.getV1().getGroup()).isNull();
assertThat(properties.getV2().getMetricKeyPrefix()).isNull();
assertThat(properties.getV2().isEnrichWithDynatraceMetadata()).isTrue();
assertThat(properties.getV2().getDefaultDimensions()).isNull();
assertThat(properties.getDeviceId()).isNull();
assertThat(properties.getTechnologyType()).isEqualTo("java");
assertThat(properties.getUri()).isNull();
assertThat(properties.getGroup()).isNull();
assertThat(properties.getApiVersion()).isSameAs(DynatraceApiVersion.V1);
assertThat(properties.getMetricKeyPrefix()).isNull();
assertThat(properties.getEnrichWithDynatraceMetadata()).isTrue();
assertThat(properties.getDefaultDimensions()).isNull();
}
}

@ -30,12 +30,14 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class DynatracePropertiesTests extends StepRegistryPropertiesTests {
@SuppressWarnings("deprecation")
@Test
void defaultValuesAreConsistent() {
DynatraceProperties properties = new DynatraceProperties();
DynatraceConfig config = (key) -> null;
assertStepRegistryDefaultValues(properties, config);
assertThat(properties.getTechnologyType()).isEqualTo(config.technologyType());
assertThat(properties.getV1().getTechnologyType()).isEqualTo(config.technologyType());
}
}

@ -150,23 +150,15 @@ You can also change the interval at which metrics are sent to Datadog:
Dynatrace offers two metrics ingest APIs, both of which are implemented for {micrometer-registry-docs}/dynatrace[Micrometer]:
[[actuator.metrics.export.dynatrace.api-v2]]
===== API v2
[[actuator.metrics.export.dynatrace.v2-api]]
===== V2 API
The API v2 can be used in two ways:
The V2 API can be used in two ways:
If a local OneAgent is running on the host, it is enough to set the API version to v2 and metrics will be automatically exported to the https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/local-api/[local OneAgent ingest endpoint], which forwards them to the Dynatrace backend:
If a local OneAgent is running on the host, metrics will be automatically exported to the https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/local-api/[local OneAgent ingest endpoint], which forwards them to the Dynatrace backend.
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
----
management:
metrics:
export:
dynatrace:
api-version: "v2"
----
If no local OneAgent is running, the endpoint of the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/[Metrics API v2] and an API token are required.
If no local OneAgent is running, the endpoint of the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/[Metrics v2 API] and an API token are required.
The https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/[API token] must have the "Ingest metrics" (`metrics.ingest`) permission set.
It is recommended to limit scope to only this one permission.
Please ensure that the endpoint URI contains the path (e.g. `/api/v2/metrics/ingest`).
@ -177,7 +169,6 @@ Please ensure that the endpoint URI contains the path (e.g. `/api/v2/metrics/ing
metrics:
export:
dynatrace:
api-version: "v2"
# uri: "https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest" for managed deployments.
uri: "https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest"
api-token: "YOUR_TOKEN" # should be read from a secure source and not hard-coded.
@ -196,23 +187,21 @@ If tags with the same key are specified using Micrometer, they overwrite the def
metrics:
export:
dynatrace:
# specify token and uri or leave blank for OneAgent export
api-version: "v2"
metric-key-prefix: "your.key.prefix"
enrich-with-dynatrace-metadata: true
default-dimensions:
key1: "value1"
key2: "value2"
v2:
# specify token and uri or leave blank for OneAgent export
metric-key-prefix: "your.key.prefix"
enrich-with-dynatrace-metadata: true
default-dimensions:
key1: "value1"
key2: "value2"
----
[[actuator.metrics.export.dynatrace.api-v1]]
===== API v1 (Legacy)
[[actuator.metrics.export.dynatrace.v1-api]]
===== v1 API (Legacy)
The Dynatrace API v1 registry pushes metrics to the configured URI periodically using the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/[Timeseries API v1].
The Dynatrace v1 API registry pushes metrics to the configured URI periodically using the https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/[Timeseries v1 API].
For backwards-compatibility with existing setups, `api-version` defaults to `"v1"` but can also be specified explicitly as such.
To export metrics to {micrometer-registry-docs}/dynatrace[Dynatrace], your API token, device ID, and URI must be provided:
For the API v1, the base environment URI needs to be specified, without any path.
The API v1 endpoint path will be added automatically.
[source,yaml,indent=0,subs="verbatim",configprops,configblocks]
----
@ -220,12 +209,15 @@ The API v1 endpoint path will be added automatically.
metrics:
export:
dynatrace:
device-id: "YOUR_DEVICE_ID"
# uri: "https://{your-domain}/e/{your-environment-id}" on managed deployments.
uri: "https://{your-environment-id}.live.dynatrace.com"
api-token: "YOUR_TOKEN" # should be read from a secure property source
v1:
device-id: "YOUR_DEVICE_ID"
----
For the v1 API, the base environment URI must be specified without a path as the v1 endpoint path will be added automatically.
[[actuator.metrics.export.dynatrace.version-independent-settings]]
===== Version-independent settings

Loading…
Cancel
Save