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.
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
.
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, useMW_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
- No data in APM:
- Confirm the agent JAR is attached (
-javaagent:...
) and the process started without errors. - Set
OTEL_TRACES_EXPORTER=logging
andOTEL_JAVAAGENT_DEBUG=true
to verify spans are being produced.
- 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
.
- Serverless / no Host Agent:
- Set
MW_API_KEY
andMW_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.