# OpenTelemetry Java instrumentation: the javaagent, standalone libraries, and what the docs leave out

> The OpenTelemetry Java instrumentation project ships a JVM agent that injects bytecode at startup plus standalone library instrumentation for teams that prefer explicit wiring. The agent path is the one to understand first, because almost every configuration question about this project resolves to a -D flag or an environment variable.

**open-telemetry/opentelemetry-java-instrumentation** — OpenTelemetry auto-instrumentation and instrumentation libraries for Java.

- Repository: https://github.com/open-telemetry/opentelemetry-java-instrumentation
- Website: https://opentelemetry.io
- Stars: 2,633 · Forks: 1,155
- Language: Java
- License: Apache-2.0
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/open-telemetry-opentelemetry-java-instrumentation

## The problem: telemetry without rewriting your application

Most Java services accumulate observability debt the same way. Someone adds a logging library, someone else adds timing around a few methods, and the result is a partial picture that no single person can assemble. The OpenTelemetry Java instrumentation project targets that gap directly. Its README describes a Java agent JAR that attaches to any Java 8+ application and, in the project's words, "dynamically injects bytecode to capture telemetry from a number of popular libraries and frameworks." No application code changes are required for the automatic path.

The audience is narrower than "everyone running Java." The agent is aimed at teams that already run a supported framework or application server and want traces and metrics without a quarter of refactoring. The project also publishes standalone instrumentation for several libraries, documented in the supported-libraries page, for teams that would rather declare instrumentation as a dependency than attach an agent. Those two paths are the real fork in the road, and the README is explicit that the agent path is the default one.

## How the javaagent works: bytecode injection at JVM startup

The mechanism is the JVM's own `-javaagent` hook. You pass the agent JAR to the JVM, and the agent instruments classes as they load. The README's example is a single flag before the application JAR. Because the instrumentation happens at class-load time, the application itself does not need to reference any OpenTelemetry API, and the agent ships with instrumentations for all supported libraries plus the available data exporters in one package.

Data flow follows a conventional OpenTelemetry shape. The agent produces spans and metrics, then exports them. By default it uses the OTLP exporter, configured to send to an OpenTelemetry collector at `http://localhost:4318`. Nothing about that endpoint is magic; if no collector is listening there, telemetry has nowhere to go, and the README does not describe a fallback. Configuration is applied through Java system properties or environment variables, which means the agent's behaviour is decided at process start, not at runtime through an admin endpoint.

One detail worth noting for anyone debugging: the configuration documentation is separate from the README. The README points to an agent configuration page and an SDK configuration page, and adds a warning that config parameter names are very likely to change over time. That warning is a real constraint on how you should treat any flag you copy from a blog post.

## Installing the agent and getting a first trace out

The README points to the latest release download for `opentelemetry-javaagent.jar`. Download that file, then attach it with the `-javaagent` flag. This is the minimum viable invocation:

```bash
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar
```

With no further configuration the agent exports over OTLP to `http://localhost:4318`, so you should see spans arriving at a collector listening on that address. If you have no collector yet, switch the exporter to the console to confirm the agent is producing data at all. The README gives this exact example, which also sets a service name:

```bash
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -Dotel.resource.attributes=service.name=your-service-name \
     -Dotel.traces.exporter=console \
     -jar myapp.jar
```

The `service.name` attribute is the one you should set before anything else reaches a shared collector, because it is what makes the data attributable to a service. When the agent is misbehaving, the README documents a debug switch:

```bash
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -Dotel.javaagent.debug=true \
     -jar myapp.jar
```

The README warns that these logs are extremely verbose and that debug logging negatively impacts application performance, so treat this as a diagnostic step rather than a setting to leave on. For adding attributes to automatic spans or creating spans for your own code, the README points to a manual instrumentation guide rather than describing the API inline.

## The cost you accept: startup-time injection and a moving config surface

Bytecode injection is not free, and the project does not pretend otherwise. Instrumentation runs as classes load, which puts work on the startup path, and the debug logging note in the README is a candid admission that the agent's own diagnostics carry a performance penalty. The project maintains benchmarks, visible in the repository as `benchmark-overhead/` and `benchmark-overhead-jmh/`, but the README does not publish overhead numbers, so you should measure your own service rather than assume a figure.

The second cost is configuration churn. The README states plainly that config parameter names are very likely to change over time and asks users to check back when trying a new version. That is unusual honesty and also a warning: pin your agent version, and re-read the configuration docs when you bump it. If your deployment process copies flags from an old runbook, an upgrade can silently change behaviour.

The third limitation is coverage. The agent instruments libraries it knows about. The supported-libraries page documents what is covered, what is disabled by default, and how to suppress unwanted instrumentation. A library outside that list gets nothing automatic, and the answer there is manual instrumentation, not configuration. If your stack is mostly in-house frameworks, the agent's value drops sharply.

## Standalone instrumentation libraries versus the agent

The alternative the project itself offers is standalone library instrumentation, listed in the standalone library instrumentation column of the supported-libraries page. The difference is architectural. With the agent, instrumentation is applied to bytecode at load time and your build knows nothing about it. With the standalone libraries, instrumentation is a dependency you add, and it is wired into your application explicitly.

That trade is real in both directions. Standalone libraries make the instrumentation visible to your build tooling and to code review, and they avoid attaching an agent to the JVM. They also require you to write and maintain the wiring, and they only exist for a subset of what the agent covers. The agent covers more ground with less work but puts a component in the runtime that your application source does not mention. Teams with strict build-time review of runtime dependencies tend to prefer the libraries; teams that want coverage across many services quickly tend to prefer the agent.

For teams that need more than either path offers, the repository holds `examples/extension/` for agent extensions and `examples/distro/` for building a separate distribution. The README recommends extensions for most users, noting they are simpler and do not require rebuilding with each agent release, which is a fair summary of why forking a distribution is the heavier option.

## Maintenance, licensing, and what upgrading involves

The repository is not archived, and the last push was on 2026-08-23, which lines up with the v2.31.1 release on the same date. Releases have been frequent: v2.30.0 on 2026-07-22, v2.31.0 on 2026-08-20, and v2.31.1 on 2026-08-23. That cadence is the practical upgrade cost. Because the agent is a single JAR you replace, upgrading is a file swap plus a re-read of the configuration docs for renamed keys. There is no compiled dependency to reconcile, which is the main operational advantage of the agent path.

The licence is Apache-2.0, which is permissive and generally compatible with commercial distribution, but the repository also carries a `licenses/` directory and a `.fossa.yml` file, so the agent bundles third-party components whose terms travel with it. If you redistribute the agent inside your own product, check that directory rather than assuming the top-level licence tells the whole story. This is a description of what is in the repository, not legal advice.

Contributor activity is visible in the README's maintainer and approver lists, which name people from Splunk, Microsoft, Grafana Labs, Elastic, Alibaba, and Sublime Security. That spread matters less for day-to-day use than the release cadence, but it does mean the project is not dependent on a single employer.

## When the javaagent is the wrong tool

There are cases where attaching this agent is the wrong call, and the README's own framing points at them. If your application runs on a Java version below 8, the agent does not apply. If your stack is dominated by libraries absent from the supported list, automatic coverage will be thin and you will spend your time on manual instrumentation anyway, at which point the standalone libraries or plain manual instrumentation are the more honest choice.

There is also the operational question of injection itself. Some production environments restrict JVM agent attachment, whether by policy or by tooling, and the agent needs to be on the classpath at startup rather than attached after the fact. If your deployment pipeline cannot add a `-javaagent` flag, the standalone library path is the way in.

Finally, the default OTLP endpoint at `http://localhost:4318` is a development convenience, not a production topology. The README does not document retry or buffering behaviour when that endpoint is unreachable, so you should confirm what happens in your environment rather than assume telemetry is queued indefinitely.

## Conclusion

Adopt the javaagent if you run a Java 8+ service built on frameworks the supported-libraries list covers and you want telemetry without touching application code; adopt the standalone library instrumentation instead if you want the instrumentation visible in your dependency graph and compiled into your build. Skip it if you cannot accept bytecode injection in production, or if your stack falls outside the supported list, because the agent will not invent instrumentation for a library it does not know. Before rolling it out, verify three things: that your service name is set with -Dotel.resource.attributes, that your collector is reachable at the configured OTLP endpoint, and that the configuration keys you copy still exist in the docs for the version you downloaded, since the README warns that parameter names are likely to change over time.

## FAQ

### What is instrumentation in OpenTelemetry?

In this project, instrumentation is the code that captures telemetry from libraries and frameworks. The agent injects it into bytecode at class-load time, and the project also publishes standalone instrumentation libraries for several frameworks.

### Is OpenTelemetry difficult to learn?

The README's getting-started path is one JVM flag, so producing telemetry is quick. The learning curve sits in configuration: the project maintains separate agent and SDK configuration docs and warns that parameter names are likely to change over time.

### What is instrumentation in Java?

For this project it means capturing telemetry from Java libraries and frameworks, either by attaching the agent JAR with `-javaagent` so bytecode is instrumented as classes load, or by adding standalone instrumentation libraries to the build.

### What is the difference between telemetry and instrumentation?

The README treats instrumentation as the mechanism and telemetry as the output. The agent instruments supported libraries, then exports the resulting spans and metrics, by default through the OTLP exporter to a collector at `http://localhost:4318`.

## Sources

- [Official documentation](https://opentelemetry.io)
- [Official README](https://github.com/open-telemetry/opentelemetry-java-instrumentation#readme)
- [Project repository](https://github.com/open-telemetry/opentelemetry-java-instrumentation)
- [Release notes](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/open-telemetry-opentelemetry-java-instrumentation
