---
title: "Java"
description: "Step-by-step guide to configure Middleware APM for Java applications, enabling real-time performance monitoring and observability."
section: "APM"
breadcrumb: "APM > Java"
canonical: "https://docs.middleware.io/get-started/apm/java"
---
# Java

Use the Middleware Java agent to collect traces, metrics, logs and continuous profiles from Java applications without code changes. Attach the agent to your JVM, set your service name and API key, and data appears in APM.

## 1. Prerequisites

Java 8+. Check with:

```bash
  java --version
```

Middleware Host Agent running on the same machine/cluster, or be ready to send data directly to Middleware (serverless path below).

Container networking hint (MW_AGENT_SERVICE):

- Docker: set `MW_AGENT_SERVICE=172.17.0.1` (Docker bridge default) so the app can reach the Host Agent.
- Kubernetes: set `MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local`.
  These are only needed when the app and agent are not on the same network namespace.

Tip: Prefer secret stores (Kubernetes Secrets, AWS Secrets Manager, etc.) for `MW_API_KEY` and `MW_TARGET`. Don’t commit them to source or Dockerfiles.

## 2. Download the agent JAR

Grab the latest `middleware-javaagent` from [Middleware’s GitHub releases](https://github.com/middleware-labs/opentelemetry-java-instrumentation/releases).

## 3. Capture application data (run the app)

Once configured, Middleware will collect metrics, traces, logs, and profiling together.

### Standard host/container (agent present)

Run your JAR with the Java agent. Set your service name and project name:

```bash
  MW_API_KEY="your-initial-token" \
java -javaagent:/PATH/TO/middleware-javaagent-{version}.jar \
  -Dotel.service.name={APM-SERVICE-NAME} \
  -Dotel.resource.attributes=project.name={APM-PROJECT-NAME} \
  -jar {JAR-NAME}.jar
```

### No Host Agent / serverless (send direct to Middleware)

If you’re not running the Host Agent (for example, serverless), set both `MW_API_KEY` and `MW_TARGET` to your tenant OTLP endpoint and then run your app with the agent:

```bash
  export MW_API_KEY="your-initial-token"
export MW_TARGET="your-initial-uid"
java -javaagent:/PATH/TO/middleware-javaagent-{version}.jar \
  -Dotel.service.name={APM-SERVICE-NAME} \
  -Dotel.resource.attributes=project.name={APM-PROJECT-NAME} \
  -jar {JAR-NAME}.jar
```

Tip: Use a secrets manager (not plain env vars) for MW_API_KEY / MW_TARGET on cloud platforms.
Example project: See the [Java example repo](https://github.com/middleware-labs/demo-apm/tree/master/java) for a working setup.

## 4. Advanced configuration (optional)

### A. Custom logs in code

Add the logger dependency and import, then use the helper methods:
`pom.xml`

```java
  <dependency>
  <groupId>io.github.middleware-labs</groupId>
  <artifactId>agent-apm-java</artifactId>
  <version>0.0.16</version>
</dependency>
```

In code:

```java
  import io.github.middlewarelabs.agentapmjava.Logger;

Logger.info("info message");
Logger.debug("debug message");
Logger.warn("warn message");
Logger.error("error message");
```

Record stack traces:

```java
  try {
  // ...
} catch (Throwable e) {
  Logger.recordError(e);
}
```

### B. Continuous Profiling (on by default)

Control with `MW_APM_COLLECT_PROFILING`:

```bash
  # enable
MW_APM_COLLECT_PROFILING=true java -javaagent:... -Dotel.service.name=... -jar ...

# disable
MW_APM_COLLECT_PROFILING=false java -javaagent:... -Dotel.service.name=... -jar ...
```

### C. Head sampling (reduce trace volume)

Pick a sampler via env vars or Java properties:
Through environment variables:

```bash
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1"   # 10% of traces
```

Through Java properties:

```bash
java -javaagent:... \
  -Dotel.traces.sampler=parentbased_traceidratio \
  -Dotel.traces.sampler.arg=0.1 \
  -jar ...
```

Head sampling is a technique that makes sampling decisions at the earliest possible stage. It determines whether to sample or drop a span or trace without inspecting the entire trace.

By default, Java APM samples all the spans and sends them to Middleware backend. If you wish to restrict the amount of spans you send to Middleware, you can introduce a trace sampler and adjust the sampling rate.

Available Samplers:

- `always_on`: Samples every span, ensuring that 100% of traces are captured. Useful in development environments or when comprehensive trace data is needed.
- `always_off`: Does not sample any spans, effectively disabling tracing. Useful in production environments where tracing overhead needs to be minimized.
- `traceidratio`: Samples a fixed percentage of traces, determined by the otel.traces.sampler.arg environment variable. For example, setting otel.traces.sampler.arg to 0.1 will sample 10% of traces.
- `parentbased_always_on`: If the parent span is sampled, the child span will also be sampled. If there is no parent, the span will be sampled.
- `parentbased_always_off`: If the parent span is not sampled, the child span will also not be sampled. If there is no parent, the span will not be sampled.
- `parentbased_traceidratio`: Uses the traceidratio sampler for root spans, with the sampling rate set by otel.traces.sampler.arg.

### D. Debug/console logging (quick verification)

Echo OTEL data to stdout while you test:

```bash
  export OTEL_TRACES_EXPORTER=logging
export OTEL_METRICS_EXPORTER=logging
export OTEL_LOGS_EXPORTER=logging
export OTEL_EXPORTER_LOGGING_PREFIX="OTEL DEBUG: "
export OTEL_JAVAAGENT_DEBUG=true
```

Then run with the agent as usual to see spans/metrics/logs in the console.

### E. Java System Properties

You can set `otel.traces.sampler` Java system property to select the sampler and `otel.traces.sampler.arg` to set the sampling rate.

#### Direct Arguments

`otel.traces.sampler` and `otel.traces.sampler.argproperties` can also be passed directly to the Java executable as shown below:

```bash
# Configure trace sampling for Java app:
# Uses parentbased_traceidratio sampler
# Sets sampling rate to 10% of traces
# Balances data collection and performance
MW_API_KEY="your-initial-token" java -javaagent:/PATH/TO/middleware-javaagent-{version}.jar \
    -Dotel.service.name={APM-SERVICE-NAME} \
    -Dotel.resource.attributes=project.name={APM-PROJECT-NAME} \
    -Dotel.traces.sampler=otel.traces.sampler=parentbased_traceidratio \
    -Dotel.traces.sampler.arg=0.1 \
    -jar <YOUR_APP>.jar
```

This will use `parentbased_traceidratio` to sample 10% of your root spans.

#### Properties file

These properties can be set in a Java system properties file, and based on the java executable, as shown below:

## 5. View your data

After you start the app, give it a few minutes, and then head to:

- [APM → Traces](https://app.middleware.io/apm/traces) (end-to-end traces, errors)
- Logs (application & custom logs)
- [APM → Continuous Profiling](https://app.middleware.io/apm/profiling) (CPU / wall-time, etc.)

## 6. Kubernetes notes (if you’re deploying on K8s)

If you manually instrument as above, set `MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local` in your Deployment so the app can reach the Host Agent Service.

Alternatively, you can use Kubernetes auto-instrumentation (OTel Operator under the hood). For Java Pods, you can enable via annotation:

```bash
  instrumentation.opentelemetry.io/inject-java: "mw-agent-ns/mw-autoinstrumentation"
```

(Restart the workload after changes.)

## 7. Environment variables (Java APM)

Use these to configure the Middleware Java agent and the OpenTelemetry SDK behavior used by the agent.

#### Core (Middleware)

| Environment Variable | Default Value | Description |
| --- | --- | --- |
| `MW_API_KEY` | — | API key used to authenticate the agent with Middleware. |
| `MW_TARGET` | — | Tenant-specific OTLP endpoint (e.g., `https://<TENANT_ID>.middleware.io:443`). |
| `MW_SERVICE_NAME` | "" | Friendly service name for the app (used by the agent when present). |
| `MW_AGENT_SERVICE` | `localhost` | Address of the Middleware Agent / collector. Use Docker bridge (172.17.0.1) or K8s service DNS as applicable. |
| `MW_APM_COLLECT_PROFILING` | `true` | Enable/disable continuous profiling. Not set ⇒ profiling enabled. |
| `MW_AUTH_URL` | `https://app.middleware.io/api/v1/auth` | Authentication URL used by the agent. |
| `MW_PROFILING_SERVER_URL` | `null` | Custom profiling server URL (advanced). |
| `MW_PROFILING_ALLOC` | `"512k"` | Allocation size for profiling. |
| `MW_PROFILING_LOCK` | `"10ms"` | Lock duration for profiling. |

Container tips: For Docker, set `MW_AGENT_SERVICE=172.17.0.1.` For Kubernetes, set `MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local`.

#### OpenTelemetry (used by the Java agent)

| Environment Variable | Default Value | Description |
| --- | --- | --- |
| `OTEL_TRACES_SAMPLER` | `always_on · always_off · traceidratio · parentbased_*` | Selects the sampler. |
| `OTEL_TRACES_SAMPLER_ARG` | e.g., `0.1` | Sampler argument (e.g., 10% sampling). |
| `OTEL_JAVAAGENT_CONFIGURATION_FILE` | path to file | Points to a Java properties file with OTEL settings. |
| `OTEL_TRACES_EXPORTER` | `logging` (for debug) | Log traces to console (debug). |
| `OTEL_METRICS_EXPORTER` | `logging` (for debug) | Log OTEL logs to console (debug). |
| `OTEL_LOGS_EXPORTER` | `logging` (for debug) | Log metrics to console (debug). |
| `OTEL_EXPORTER_LOGGING_PREFIX` | e.g., `"OTEL DEBUG: "` | Prefix for OTEL debug log lines. |
| `OTEL_JAVAAGENT_DEBUG` | `true`/`false` | Enable verbose Java agent debug logging. |
| `OTEL_SERVICE_NAME` (optional) | `"unknown_service:java"` (default) | Sets service.name via env instead of -Dotel.service.name (takes precedence over OTEL_RESOURCE_ATTRIBUTES). Useful if you prefer env vars over -D. |

## 8. Reference behaviours & defaults

- The Java agent exports to the Host Agent at `localhost:9319` by default. In containers, use `MW_AGENT_SERVICE` to point to the agent host/service.
- Keep your agent JAR up to date from the official releases page.

## 9. Troubleshooting quick checks

1. No data in APM:

- Confirm the agent JAR is attached (`-javaagent:...`) and the process started without errors.
- Set `OTEL_TRACES_EXPORTER=logging` and `OTEL_JAVAAGENT_DEBUG=true` to verify spans are being produced.

2. App in container cannot reach the Host Agent:

- Docker: `MW_AGENT_SERVICE=172.17.0.1`.
- Kubernetes: `MW_AGENT_SERVICE=mw-service.mw-agent-ns.svc.cluster.local`.

3. Serverless / no Host Agent:

- Set `MW_API_KEY` and `MW_TARGET="https://<MW_UID>.middleware.io:443"` (use secrets).

Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us page or join our Slack channel.
