The Micrometer OTLP registry exports Micrometer metrics over the OpenTelemetry Protocol (OTLP). It's an alternative to the Dynatrace registry for teams that want to standardize on OpenTelemetry across their applications.
Using the OTLP registry, your Micrometer metrics are exported the same way as any other OpenTelemetry signal, so you can rely on standard OpenTelemetry resource attributes and configuration conventions.
Micrometer version 1.13.15+
Optional Spring Boot version 4.1.x+
The registry dependency must be added to your project:
implementation 'io.micrometer:micrometer-registry-otlp:latest.release'
Replace {micrometer.version} with the latest version of Micrometer or a specific version that you want to use.
A list of released versions is available on Maven Central.
We recommend that you use the latest version.
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-otlp</artifactId><version>{micrometer.version}</version></dependency>
The Dynatrace Operator automatically configures the OTLP exporter by injecting the standard OpenTelemetry environment variables (such as OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS) and Kubernetes resource attributes into your pod. No explicit Spring Boot configuration is required: having the io.micrometer:micrometer-registry-otlp dependency on the classpath is enough.
For the full list of supported environment variables, see the Spring Boot OpenTelemetry environment variables reference.
For configuration examples, see Dynakube examples. For more information about metadata enrichment, see the documentation on enrichment files.
Dynatrace Operator's environment variable injection is all-or-nothing per pod. If any OTEL_EXPORTER_OTLP_* variable is already present in your container spec (regardless of which signal it targets: _METRICS_*, _LOGS_*, _TRACES_*), the Operator skips injecting all signal-specific OTLP variables. Only OTEL_RESOURCE_ATTRIBUTES continues to be injected in that case.
To ensure Dynatrace Operator manages the full OTLP configuration, do not set any OTEL_EXPORTER_OTLP_* variables manually in your pod or container spec.
Dynatrace Operator automatically injects the standard OTEL_EXPORTER_OTLP_* environment variables into your pod, so no explicit endpoint or token configuration is required.
However, the ActiveGate running in the Kubernetes cluster uses a self-signed certificate that is not trusted by the JVM by default. Unlike Spring Boot, Micrometer does not support the OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE environment variable natively (tracked in a Micrometer issue), so you need to provide a custom HTTP sender that reads this variable and builds a scoped SSLContext.
Create the following JdkClientHttpSender class:
import io.micrometer.core.ipc.http.HttpSender;import javax.net.ssl.SSLContext;import javax.net.ssl.TrustManagerFactory;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.security.KeyStore;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.time.Duration;class JdkClientHttpSender implements HttpSender {private final HttpClient httpClient;JdkClientHttpSender() throws Exception {HttpClient.Builder builder = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10));SSLContext sslContext = buildSslContextIfCertPresent();if (sslContext != null) {builder.sslContext(sslContext);}this.httpClient = builder.build();}@Overridepublic Response send(Request request) throws IOException {HttpRequest.Builder httpRequest = HttpRequest.newBuilder().uri(URI.create(request.getUrl().toString())).timeout(Duration.ofSeconds(30));request.getRequestHeaders().forEach(httpRequest::header);httpRequest.method(request.getMethod().name(),HttpRequest.BodyPublishers.ofByteArray(request.getEntity()));try {HttpResponse<String> response = this.httpClient.send(httpRequest.build(), HttpResponse.BodyHandlers.ofString());return new Response(response.statusCode(), response.body());} catch (InterruptedException ex) {Thread.currentThread().interrupt();throw new IOException("HTTP request interrupted", ex);}}private static SSLContext buildSslContextIfCertPresent() throws Exception {String certPath = System.getenv("OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE");if (certPath == null || certPath.isBlank()) return null;KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());trustStore.load(null, null);CertificateFactory cf = CertificateFactory.getInstance("X.509");try (InputStream is = new FileInputStream(certPath)) {Certificate cert = cf.generateCertificate(is);trustStore.setCertificateEntry("custom-ca", cert);}TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(trustStore);SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, tmf.getTrustManagers(), null);return sslContext;}}
OtlpConfig.DEFAULT still picks up all other OTEL_* environment variables:
OtlpMeterRegistry registry = OtlpMeterRegistry.builder(OtlpConfig.DEFAULT).metricsSender(new OtlpHttpMetricsSender(new JdkClientHttpSender())).build();
Dynatrace Operator's environment variable injection is all-or-nothing per pod. If any OTEL_EXPORTER_OTLP_* variable is already present in your container spec (regardless of which signal it targets: _METRICS_*, _LOGS_*, _TRACES_*), the Operator skips injecting all signal-specific OTLP variables. Only OTEL_RESOURCE_ATTRIBUTES continues to be injected in that case.
To ensure the Operator manages the full OTLP configuration, do not set any OTEL_EXPORTER_OTLP_* variables manually in your pod or container spec.
Enrichment adds Dynatrace metadata (host, process group, and Kubernetes attributes) to your metrics so they are associated with the right entities in the topology model.
Enrichment applies to the Standalone and Export to the OpenTelemetry Collector setups. The Dynatrace Operator setup enriches your metrics automatically, so you can skip this section when you use the Operator.
If you export your metrics to an OpenTelemetry Collector, enrich them at the Collector level:
dynatrace detector. For the configuration, see Enrich OTLP with OneAgent data.When OneAgent runs on the host but you export metrics directly to Dynatrace (the Standalone setup), the OTLP registry does not read OneAgent enrichment data automatically. Add custom code that reads the OneAgent metadata enrichment file and adds the attributes to your meter registry.
Add the dynatrace-metric-utils-java dependency, which reads the OneAgent metadata enrichment file:
Gradle
implementation 'com.dynatrace.metric.util:dynatrace-metric-utils-java:2.5.0'
Maven
<dependency><groupId>com.dynatrace.metric.util</groupId><artifactId>dynatrace-metric-utils-java</artifactId><version>2.5.0</version></dependency>
Create a helper class that reads the OneAgent enrichment metadata and converts it to Micrometer tags:
import com.dynatrace.metric.util.MetricException;import com.dynatrace.metric.util.MetricLinePreConfiguration;import io.micrometer.core.instrument.Tag;import java.util.List;public final class DtMetadata {private DtMetadata() {}public static List<Tag> readEnrichmentTags() {try {MetricLinePreConfiguration preConfig = MetricLinePreConfiguration.builder().dynatraceMetadataDimensions().build();return preConfig.getDynatraceMetadataDimensions().entrySet().stream().map(e -> Tag.of(e.getKey(), e.getValue())).toList();} catch (MetricException e) {return List.of();}}}
Register the tags as a MeterFilter bean in a @Configuration class. Spring Boot's MetricsAutoConfiguration picks up all MeterFilter beans and applies them to every MeterRegistry before any meter is registered:
import io.micrometer.core.instrument.Tag;import io.micrometer.core.instrument.config.MeterFilter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.List;@Configurationpublic class MetricsConfig {@BeanMeterFilter dtEnrichmentFilter() {List<Tag> dtTags = DtMetadata.readEnrichmentTags();return MeterFilter.commonTags(dtTags);}}
The enrichment attributes are added as datapoint attributes, not as OTLP resource attributes. Support for injecting them as resource attributes is being tracked in a Spring Boot issue.
If you export metrics to an environment ActiveGate that uses a self-signed certificate, make sure the certificate is properly installed on the ActiveGate. For details, see Configure a custom SSL certificate on ActiveGate.
After you have sent your metrics, verify the data in Data Explorer or query them in Grail.
Micrometer instruments are mapped to OTLP metric types and then to Dynatrace metric types as follows:
| Micrometer instrument | OTLP metric | Dynatrace metric |
|---|---|---|
Counter, FunctionCounter | Sum (delta) | Counter |
Gauge, TimeGauge, MultiGauge | Gauge | Gauge |
Timer, DistributionSummary, LongTaskTimer | Histogram (delta) | Explicit bucket histogram |
FunctionTimer | Sum (delta) | Counter |
Timer, DistributionSummary, and LongTaskTimer export as Histogram by default. If you configure client-side percentiles, they export as the OTLP Summary type instead, which is not supported by Dynatrace. Avoid enabling client-side percentiles when exporting to Dynatrace.
For the full OTLP to Dynatrace metric type mapping, see About OTLP metrics ingest.
For upstream reference documentation, see: