Java

1. Prerequisites

Java 8+. Check with:

1java --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.

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:

1MW_API_KEY="<MW_API_KEY>" \
2java -javaagent:/PATH/TO/middleware-javaagent-{version}.jar \
3  -Dotel.service.name={APM-SERVICE-NAME} \
4  -Dotel.resource.attributes=project.name={APM-PROJECT-NAME} \
5  -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:

1export MW_API_KEY="<MW_API_KEY>"
2export MW_TARGET="https://<MW_UID>.middleware.io:443"
3java -javaagent:/PATH/TO/middleware-javaagent-{version}.jar \
4  -Dotel.service.name={APM-SERVICE-NAME} \
5  -Dotel.resource.attributes=project.name={APM-PROJECT-NAME} \
6  -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 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

1<dependency>
2  <groupId>io.github.middleware-labs</groupId>
3  <artifactId>agent-apm-java</artifactId>
4  <version>0.0.16</version>
5</dependency>

In code:

1import io.github.middlewarelabs.agentapmjava.Logger;
2
3Logger.info("info message");
4Logger.debug("debug message");
5Logger.warn("warn message");
6Logger.error("error message");

Record stack traces:

1try {
2  // ...
3} catch (Throwable e) {
4  Logger.recordError(e);
5}

B. Continuous Profiling (on by default)

Control with MW_APM_COLLECT_PROFILING:

1# enable
2MW_APM_COLLECT_PROFILING=true java -javaagent:... -Dotel.service.name=... -jar ...
3
4# disable
5MW_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:

1export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
2export OTEL_TRACES_SAMPLER_ARG="0.1"   # 10% of traces

Through Java properties:

1java -javaagent:... \
2  -Dotel.traces.sampler=parentbased_traceidratio \
3  -Dotel.traces.sampler.arg=0.1 \
4  -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:

1export OTEL_TRACES_EXPORTER=logging
2export OTEL_METRICS_EXPORTER=logging
3export OTEL_LOGS_EXPORTER=logging
4export OTEL_EXPORTER_LOGGING_PREFIX="OTEL DEBUG: "
5export 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:

1# Configure trace sampling for Java app:
2# Uses parentbased_traceidratio sampler
3# Sets sampling rate to 10% of traces
4# Balances data collection and performance
5MW_API_KEY="<MW_API_KEY>" java -javaagent:/PATH/TO/middleware-javaagent-{version}.jar \
6    -Dotel.service.name={APM-SERVICE-NAME} \
7    -Dotel.resource.attributes=project.name={APM-PROJECT-NAME} \
8    -Dotel.traces.sampler=otel.traces.sampler=parentbased_traceidratio \
9    -Dotel.traces.sampler.arg=0.1 \
10    -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 (end-to-end traces, errors)
  • Logs (application & custom logs)
  • APM → Continuous 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:

1instrumentation.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)
OpenTelemetry (used by the Java agent)
Environment VariableDefault ValueDescription
MW_API_KEYAPI key used to authenticate the agent with Middleware.
MW_TARGETTenant-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_SERVICElocalhostAddress of the Middleware Agent / collector. Use Docker bridge (172.17.0.1) or K8s service DNS as applicable.
MW_APM_COLLECT_PROFILINGtrueEnable/disable continuous profiling. Not set ⇒ profiling enabled.
MW_AUTH_URLhttps://app.middleware.io/api/v1/authAuthentication URL used by the agent.
MW_PROFILING_SERVER_URLnullCustom 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.

Environment VariableDefault ValueDescription
OTEL_TRACES_SAMPLERalways_on · always_off · traceidratio · parentbased_*Selects the sampler.
OTEL_TRACES_SAMPLER_ARGe.g., 0.1Sampler argument (e.g., 10% sampling).
OTEL_JAVAAGENT_CONFIGURATION_FILEpath to filePoints to a Java properties file with OTEL settings.
OTEL_TRACES_EXPORTERlogging (for debug)Log traces to console (debug).
OTEL_METRICS_EXPORTERlogging (for debug)Log OTEL logs to console (debug).
OTEL_LOGS_EXPORTERlogging (for debug)Log metrics to console (debug).
OTEL_EXPORTER_LOGGING_PREFIXe.g., "OTEL DEBUG: "Prefix for OTEL debug log lines.
OTEL_JAVAAGENT_DEBUGtrue/falseEnable 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.
  1. 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.
  1. 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? Contact our support team at [email protected] or join our Slack channel.