﻿---
title: Migrate to EDOT Java from the Elastic APM Java agent
description: Migrate from the Elastic APM Java agent to the Elastic Distribution of OpenTelemetry Java (EDOT Java).
url: https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/migration
products:
  - APM Agent
  - Elastic Cloud Serverless
  - Elastic Distribution of OpenTelemetry Java
  - Elastic Distribution of OpenTelemetry SDK
  - Elastic Observability
applies_to:
  - Serverless Observability projects: Generally available
  - Elastic Stack: Generally available
  - Elastic Distribution of OpenTelemetry Java: Generally available
---

# Migrate to EDOT Java from the Elastic APM Java agent
Compared to the Elastic APM Java agent, the Elastic Distribution of OpenTelemetry Java presents a number of advantages:
- Fully automatic instrumentation with zero code changes. No need to modify application code.
- Capture, send, transform, and store data in an OpenTelemetry native way. This includes for example the ability to use all features of the OpenTelemetry SDK for manual tracing, data following semantic conventions, or ability to use intermediate collectors and processors.
- OpenTelemetry Java Instrumentation provides a [broad coverage of libraries, frameworks, and applications](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md).
- EDOT Java is built on top of OpenTelemetry SDK and conventions, ensuring compatibility with community tools, vendor-neutral backends, and so on.


## Migration steps

<agent-skill url="https://github.com/elastic/agent-skills/tree/main/skills/observability/edot-java-migrate">
  A skill is available to help AI agents with this topic.
  Use this skill to migrate from the Elastic APM Java agent to EDOT Java.
</agent-skill>

Follow these steps to migrate from the legacy Elastic APM Java agent to the Elastic Distribution of OpenTelemetry Java.
<stepper>
  <step title="(Optional) Migrate manual instrumentation API">
    Migrate usages of the [Elastic APM Agent API](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/public-api) to OpenTelemetry API:
    - For [Annotation API](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/public-api#api-annotation), refer to [OpenTelemetry Annotations](https://opentelemetry.io/docs/zero-code/java/agent/annotations/).
    - For [Transaction API](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/public-api#api-transaction), refer to [OpenTelemetry API](https://opentelemetry.io/docs/zero-code/java/agent/api/).

    <note>
      Migration of application code using these APIs and annotations is not strictly required when deploying the EDOT agent. If not migrated, spans, transactions, and metrics that were previously created with those custom API calls and annotations will no longer be generated. OpenTelemetry instrumentation coverage might replace the need for some or all of these custom code changes.
    </note>
  </step>

  <step title="Replace configuration options">
    Refer to the [Configuration mapping](#configuration-mapping). Refer to [Configuration](https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/configuration) for ways to provide configuration settings.
  </step>

  <step title="Replace the agent binary">
    Remove the `-javaagent:` argument that contains the Elastic APM Java agent from the JVM arguments. Then add the `-javaagent:` argument to the JVM arguments to use EDOT Java, and restart the application or follow [Kubernetes instructions](https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/setup/k8s) or [Runtime attach instructions](https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/setup/runtime-attach) if applicable. Refer to [Setup](https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/setup).
  </step>
</stepper>


## Configuration mapping

The following describes how Elastic APM Java agent configuration maps to EDOT Java. It includes how resource attributes are handled when using the EDOT Collector, followed by each agent setting and its EDOT equivalent.

### Resource attributes when using the EDOT Collector

Ingesting OpenTelemetry data directly through APM Server is [no longer supported](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/opentelemetry/architecture#limitations). Historically, when ingesting OpenTelemetry data through the Elastic APM Server, unmapped resource attributes were added under `labels.*`. This behavior does not apply when using the EDOT Collector and is not recommended for new deployments. Use the EDOT Collector or Managed OTLP for supported ingestion.
If you rely on specific attribute mappings for querying or filtering in Elastic Observability, configure explicit attribute processors in the EDOT Collector pipeline.

### `server_url`

The Elastic [`server_url`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-reporter#config-server-url) option is replaced by the OpenTelemetry [`OTEL_EXPORTER_OTLP_ENDPOINT`](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_endpoint) option. Refer to [Setup](https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/setup) for endpoint configuration details.

### `server_urls`

The Elastic [`server_urls`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-reporter#config-server-urls) option has no equivalent OpenTelemetry option. You can only specify one endpoint.
Use [OTEL_EXPORTER_OTLP_ENDPOINT](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_endpoint) instead.

### `secret_token`

The Elastic [`secret_token`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-reporter#config-secret-token) option corresponds to the OpenTelemetry [OTEL_EXPORTER_OTLP_HEADERS](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_headers) option.
For example: `OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer an_apm_secret_token"`.

### `api_key`

The Elastic [`api_key`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-reporter#config-api-key) option corresponds to the OpenTelemetry [OTEL_EXPORTER_OTLP_HEADERS](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_headers) option.
For example:`OTEL_EXPORTER_OTLP_HEADERS="Authorization=ApiKey an_api_key"`.

### `service_name`

The Elastic [`service_name`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-service-name) option corresponds to the OpenTelemetry [OTEL_SERVICE_NAME](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_service_name) option.
The service name value can also be set using [OTEL_RESOURCE_ATTRIBUTES](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes).
For example: `OTEL_RESOURCE_ATTRIBUTES=service.name=myservice`. If `OTEL_SERVICE_NAME` is set, it takes precedence over the resource attribute.

### `enabled`

The Elastic [`enabled`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-enabled) option corresponds to the OpenTelemetry [OTEL_JAVAAGENT_ENABLED](https://opentelemetry.io/docs/zero-code/java/agent/disable/) option.

### `service_version`

The Elastic [`service_version`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-service-version) option corresponds to setting the `service.version` key in [OTEL_RESOURCE_ATTRIBUTES](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes).
For example: `OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3`.

### `environment`

The Elastic [`environment`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-environment) option corresponds to setting the `deployment.environment.name` key in [OTEL_RESOURCE_ATTRIBUTES](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes).
For example: `OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=testing`.

### `global_labels`

The Elastic [`global_labels`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-global-labels) option corresponds to adding `key=value` comma separated pairs in [OTEL_RESOURCE_ATTRIBUTES](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes).
For example: `OTEL_RESOURCE_ATTRIBUTES=alice=first,bob=second`

### `trace_methods`

The Elastic [`trace_methods`](https://www.elastic.co/docs/reference/apm/agents/java/config-core#config-trace-methods) option can be replaced by the [`OTEL_INSTRUMENTATION_METHODS_INCLUDE`](https://opentelemetry.io/docs/zero-code/java/agent/annotations/#creating-spans-around-methods-with-otelinstrumentationmethodsinclude) OpenTelemetry option, however the syntax is different and the ability to use wildcards is more limited.

### `capture_jmx_metrics`

The Elastic [`capture_jmx_metrics`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-jmx#config-capture-jmx-metrics) option can be replaced by
[OpenTelemetry JMX Insight](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/jmx-metrics/javaagent/) feature which is included in EDOT Java.
The JMX Insight feature provides the following benefits:
- Ability to define custom metrics using YAML.
- Capturing metrics with pre-defined metrics by using `OTEL_JMX_TARGET_SYSTEM` configuration option.


### `capture_headers`

Replace the Elastic `capture_headers` option with the following options:
- `otel.instrumentation.http.server.capture-request-headers` for HTTP server request
- `otel.instrumentation.http.server.capture-response-headers` for HTTP server response
- `otel.instrumentation.http.client.capture-request-headers` for HTTP client request
- `otel.instrumentation.http.client.capture-response-headers` for HTTP client response
- `otel.instrumentation.messaging.experimental.capture-headers` for messaging

The `capture_headers` option is dynamically adjustable, while the `otel.*` options are statically set by startup and cannot be subsequently adjusted.

### `span_stack_trace_min_duration`

Replace the Elastic `span_stack_trace_min_duration` option with [`OTEL_JAVA_EXPERIMENTAL_SPAN_STACKTRACE_MIN_DURATION`](/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/features#span-stacktrace).

### `disable_instrumentations`

Replace the `disable_instrumentations` option, which allows to selectively turn off instrumentation (opt-out), with `OTEL_INSTRUMENTATION_<name>_ENABLED` where `<name>` is the instrumentation name.
See [OpenTelemetry documentation](https://opentelemetry.io/docs/zero-code/java/agent/disable/) for reference and values.

### `enable_instrumentations`

The `enable_instrumentations` option allows to turn off all instrumentation enabled by default and selectively enable instrumentation (opt-in) can be replaced with:
- `OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED` = `false` to turn off instrumentations enabled by default.
- `OTEL_INSTRUMENTATION_<name>_ENABLED` = `true` where `<name>` is the name of the instrumentation to enable. Refer to [OpenTelemetry documentation](https://opentelemetry.io/docs/zero-code/java/agent/disable/) for reference and values.


### `hostname`

The Elastic [`hostname`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-hostname) option corresponds to setting the `host.name` key in [OTEL_RESOURCE_ATTRIBUTES](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes).
For example: `OTEL_RESOURCE_ATTRIBUTES=host.name=myhost`.

### `service_node_name`

The Elastic [`service_node_name`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-service-node-name) option corresponds to setting the `service.instance.id` key in [OTEL_RESOURCE_ATTRIBUTES](https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#otel_resource_attributes). Warning: by default this is a generated unique ID; if you set this it must be a unique value for each JVM otherwise metric views cannot be correctly aggregated nor disambiguated
For example: `OTEL_RESOURCE_ATTRIBUTES=service.instance.id=myserviceinstance001`.

### `cloud_provider`

The Elastic [`cloud_provider`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-core#config-cloud-provider) option corresponds to the per-provider `otel.resource.providers.{provider}.enabled` configuration options.
By default, with EDOT `otel.resource.providers.{provider}.enabled` is set to `true`, this is equivalent to the `cloud_provider` default value which is `auto`, or automatically detect cloud providers. Notice that this behavior differs from the contrib OpenTelemetry distribution.
When the cloud provider is known, or there is none, turning off the non-relevant providers with `otel.resource.providers.{provider}.enabled = false` allows to [minimize the application startup overhead](/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/overhead#optimizing-application-startup).

### `log_sending`

The Elastic [`log_sending`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-logging#config-log-sending) option allows capturing and
sending application logs directly to APM Server without storing them on disk and ingesting them with a separate tool.
With EDOT, application logs are automatically captured and sent by default.
This feature is controlled by `otel.logs.exporter`, which is set to `otlp` by default. You can turn it off by setting `otel.logs.exporter` to `none`.

### `verify_server_cert`

The Elastic [`verify_server_cert`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/config-reporter#config-verify-server-cert) option allows you to turn off server certificate validation.
With EDOT, the equivalent configuration option is `ELASTIC_OTEL_VERIFY_SERVER_CERT` (default `true`), see [configuration](/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/configuration#exporter-certificate-verification) for details.

### No equivalent for `application_packages`

The Elastic APM Java agent provided an `application_packages` setting with multiple purposes, including startup optimization and stack trace filtering. EDOT Java doesn't provide an equivalent configuration for the following reasons:
- EDOT Java does not support package-based scoping to reduce instrumentation overhead at startup. To reduce startup overhead, turn off unneeded instrumentations instead.
- Package-based stack trace filtering (as provided by `application_packages`) is not currently supported in Kibana for EDOT data because EDOT stack traces are captured as flat strings rather than structured frames.

The `OTEL_JAVA_EXPERIMENTAL_SPAN_STACKTRACE_MIN_DURATION` setting controls when stack traces are captured, but it does not provide package-based filtering.

## Metrics

Metrics in this section are described as they are reported to the backend by EDOT or Elastic APM agent.
Unless an ECS-mapping is being used, the otel-native storage means that the metric names and attributes will be preserved.

### JVM runtime metrics

With EDOT Java, the JVM runtime metrics are provided by the upstream OpenTelemetry Java instrumentation
and adhere to the [JVM runtime semantic conventions](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/).
- `jvm.fd.used` is replaced by [`jvm.file_descriptor.count`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmfile_descriptorcount)
- `jvm.fd.max` does not have a direct equivalent
- `jvm.thread.count` is replaced by [`jvm.thread.count`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount) with two extra optional attributes
  - [`jvm.thread.daemon`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/)
- [`jvm.thread.state`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/)
- `jvm.memory.heap.used` is replaced by [`jvm.memory.used`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemoryused) when filtered and aggregated on metric attribute :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `heap`
- `jvm.memory.heap.committed` is replaced by [`jvm.memory.committed`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorycommitted) when filtered and aggregated on metric attribute :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `heap`
- `jvm.memory.heap.max` is replaced by [`jvm.memory.limit`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorylimit) when filtered and aggregated on metric attribute :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `heap`
- `jvm.memory.non_heap.used` is replaced by [`jvm.memory.used`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemoryused) when filtered and aggregated on metric attribute :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `non_heap`
- `jvm.memory.non_heap.committed` is replaced by [`jvm.memory.committed`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorycommitted) when filtered and aggregated on metric attribute :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `non_heap`
- `jvm.memory.non_heap.max` is replaced by [`jvm.memory.limit`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorylimit) when filtered and aggregated on metric attribute :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `non_heap`
- `jvm.memory.heap.pool.used` with `name` attribute providing the pool name is replaced by [`jvm.memory.used`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemoryused) when filtered and aggregated on metric attributes :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `heap`
- [`jvm.memory.pool.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = name of memory pool (previously in `name` attribute)
- `jvm.memory.heap.pool.committed` with `name` attribute providing the pool name is replaced by [`jvm.memory.committed`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorycommitted) when filtered and aggregated on metric attributes :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `heap`
- [`jvm.memory.pool.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = name of memory pool (previously in `name` attribute)
- `jvm.memory.heap.pool.max` with `name` attribute providing the pool name is replaced by [`jvm.memory.limit`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorylimit) when filtered and aggregated on metric attributes :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `heap`
- [`jvm.memory.pool.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = name of memory pool (previously in `name` attribute)
- `jvm.memory.non_heap.pool.used` with `name` attribute providing the pool name is replaced by [`jvm.memory.used`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemoryused) when filtered and aggregated on metric attributes :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `non_heap`
- [`jvm.memory.pool.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = name of memory pool (previously in `name` attribute)
- `jvm.memory.non_heap.pool.committed` with `name` attribute providing the pool name is replaced by [`jvm.memory.committed`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorycommitted) when filtered and aggregated on metric attributes :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `non_heap`
- [`jvm.memory.pool.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = name of memory pool (previously in `name` attribute)
- `jvm.memory.non_heap.pool.max` with `name` attribute providing the pool name is replaced by [`jvm.memory.limit`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmmemorylimit) when filtered and aggregated on metric attributes :
  - [`jvm.memory.type`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = `non_heap`
- [`jvm.memory.pool.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/) = name of memory pool (previously in `name` attribute)
- `jvm.gc.count` does not have a direct equivalent
  - workaround: however the GC execution count can be derived from [`jvm.gc.duration`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmgcduration) metric histogram.
- workaround: using custom JMX metric to capture the value of `GarbageCollectorMXBean.getCollectionCount`
- `jvm.gc.time` is replaced by [`jvm.gc.duration`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmgcduration), stored as a histogram with the following optional extra attributes:
  - [`jvm.gc.action`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/)
- [`jvm.gc.name`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/)
- [`jvm.gc.cause`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/jvm/)
- `jvm.gc.alloc` does not have a direct equivalent.


### JVM Process metrics

- `system.process.cpu.total.norm.pct` is replaced by [`jvm.cpu.recent_utilization`](https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmcpurecent_utilization)
- `system.process.memory.size` does not have a direct equivalent
  - workaround: using custom JMX metric to capture the value of [`OperatingSystemMXBean.getCommittedVirtualMemorySize`](https://docs.oracle.com/en/java/javase/11/docs/api/jdk.management/com/sun/management/OperatingSystemMXBean.html#getCommittedVirtualMemorySize())


### System metrics

With EDOT Java, the system-wide metrics are not captured through the JVM
but can be captured by using the collector with `hostmetricsreceiver` or by using custom JMX metrics.
- `system.cpu.total.norm.pct` does not have a direct equivalent
  - workaround: using custom JMX metric to capture the value of [`OperatingSystemMXBean.getSystemCpuLoad`](https://docs.oracle.com/en/java/javase/11/docs/api/java.management/java/lang/management/OperatingSystemMXBean.html#getSystemCpuLoad())
- workaround: using the collector with `hostmetricsreceiver` to capture `system.cpu.utilization`
- `system.memory.actual.free` does not have a direct equivalent
  - workaround: using custom JMX metric to capture the value of [`OperatingSystemMXBean.getFreePhysicalMemorySize`](https://docs.oracle.com/en/java/javase/11/docs/api/com/sun/management/OperatingSystemMXBean.html#getFreePhysicalMemorySize())
- workaround: using the collector with `hostmetricsreceiver` to capture `system.memory.usage` and `system.memory.limit` and calculate the difference.
- `system.memory.total` does not have a direct equivalent
  - workaround: using custom JMX metric to capture the value of [`OperatingSystemMXBean.getTotalPhysicalMemorySize`](https://docs.oracle.com/en/java/javase/11/docs/api/com/sun/management/OperatingSystemMXBean.html#getTotalPhysicalMemorySize())
- workaround: using the collector with `hostmetricsreceiver` to capture `system.memory.limit`


### Agent health and overhead metrics

Health metrics are not supported with EDOT Java and do not have a direct equivalent:
- `agent.events.total`
- `agent.events.total`
- `agent.events.dropped`
- `agent.events.queue.max_size.pct`
- `agent.events.queue.min_size.pct`
- `agent.events.requests.count`
- `agent.events.requests.bytes`

Overhead metrics are not supported with EDOT Java and do not have a direct equivalent:
- `agent.background.cpu.overhead.pct`
- `agent.background.cpu.total.pct`
- `agent.background.memory.allocation.bytes`
- `agent.background.threads.count`


### CGroup metrics

CGroup metrics are not supported with EDOT Java and do not have a direct equivalent:
- `system.process.cgroup.memory.mem.usage.bytes`
- `system.process.cgroup.memory.mem.limit.bytes`

As an alternative, you can use:
- collector with [`hostmetricsreceiver`](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/hostmetricsreceiver/README.md) from within the container to capture the following metrics:
  - `system.memory.usage`
- `system.memory.limit`
- using the collector with [`kubeletstatsreceiver`](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/kubeletstatsreceiver/README.md) to capture
  - `container.memory.usage`
- `container.memory.limit`


### OpenTelemetry metrics (bridge)

With Elastic APM Java agent, metrics that are captured through the OpenTelemetry Java API are ["bridged"](https://www.elastic.co/docs/reference/apm/agents/java/opentelemetry-bridge)
and sent as metrics. With EDOT Java, those metrics are captured and sent in an OpenTelemetry native way.
With EDOT Java, the metric scope is stored in `scope.name` attribute. With the Elastic APM Java agent, the metric scope is stored in `otel_instrumentation_scope_name` attribute.
With EDOT Java, the metric name and attributes are preserved and sent as-is.
With the Elastic APM Java agent, the metric name is preserved but the attributes names have dots `.` replaced by underscores `_` and are prefixed with `labels.` or `numeric_labels`.

## Limitations

The following limitations apply to EDOT Java.

### Supported Java versions

EDOT Java agent and OpenTelemetry Java instrumentation are only compatible with Java 8 and later.

### Missing instrumentations

Support for LDAP client instrumentation is not currently available in EDOT Java.

### Central and dynamic configuration

You can manage EDOT Java configurations through the [central configuration feature](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/solutions/observability/apm/apm-agents/central-configuration) in the Applications UI.
Refer to [Central configuration](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/opentelemetry/central-configuration) for more information.

### Span compression

EDOT Java does not implement [span compression](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/solutions/observability/apm/spans#apm-spans-span-compression).

### Breakdown metrics

EDOT Java is not sending metrics that power the [Breakdown metrics](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/solutions/observability/apm/metrics#_breakdown_metrics).

### No remote attach

There is currently no EDOT Java equivalent for starting the agent with the [remote attach](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/setup-attach-cli) capability. The `-javaagent:` option is the preferred startup mechanism.
A migration path is available for starting the agent with [self attach](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/reference/apm/agents/java/setup-attach-api), which is to use [runtime attachment](https://www.elastic.co/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/setup/runtime-attach). Some [limitations](/elastic/docs-builder/docs/3028/reference/opentelemetry/edot-sdks/java/setup/runtime-attach#limitations)
apply, and the agent must be started early during application startup.

### Micrometer turned off by default

By default, Micrometer instrumentation is inactive and doesn't capture metrics. To turn it on, use the `otel.instrumentation.micrometer.enabled=true` setting.

## Troubleshooting

If you're encountering issues during migration, refer to the [EDOT Java troubleshooting guide](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/3028/troubleshoot/ingest/opentelemetry/edot-sdks/java).