Wrapping Elastic OTEL Java Agent for internal standardization — best practices & legal considerations

Hi all,

We are integrating the Elastic OpenTelemetry Java agent across multiple services in our organization and are exploring creating a small internal wrapper around the agent (elastic-otel-java) to standardize configuration and simplify adoption.

We are using Elastic with a valid commercial license in our organization.

What we plan to do

  • Use the official Elastic OTEL Java agent

  • Package it internally as:

    -elastic-otel-javaagent

  • Provide a standardized startup wrapper that:

    • sets default OTEL configs (exporter, protocol, compression, etc.)

    • sets default resource attributes (e.g. service naming conventions)

    • abstracts the collector endpoint

  • Allow application teams to override/select key values via environment variables (e.g. service name)

What we are NOT doing

  • Modifying agent bytecode or behavior

  • Repackaging/shading agent internals

  • Changing instrumentation logic

Questions

  1. Licensing / Legal

    • Are there any restrictions or considerations when redistributing the Elastic OTEL Java agent internally (unmodified) under a different artifact name?

    • Any requirements around attribution, notices, or license inclusion?

  2. Best Practices / Do’s & Don’ts

    • Is wrapping the agent this way (config + distribution) a recommended pattern?

    • Any pitfalls to avoid (e.g., version drift, config conflicts, debugging complexity)?

    • Any guidance on how much configuration should be “opinionated defaults” vs left to application teams?

Hi !

EDOT Java is published with Apache 2 license, so my understanding is that it should be fine to re-distribute it as-is without any modification as long as you preserve the original author and ownership (just make sure there is no ambiguity it could be claimed to be your own). If I get this correctly you are just re-distributing a copy of the software and not creating a derivative product out of it.

I am not a lawyer, so I think if you have any doubt you should ask one here.

Regarding the technical aspect, I don’t think there is a lot of value to wrap, and I am not sure it’s worth the cost:

  • you will have to re-package things on your side for every new release
  • default configuration should be fine for most of the cases
  • main config options can be set through env variables (or in the future with declarative configuration), so it’s mostly checking that env variables are set as you’d expect, which can be set and enforced in the execution environment rather than at the application level

Unless you have very specific needs, it is likely simpler to provide standard instructions how to setup EDOT Java rather than package/wrap it in another form that you’ll have to document and extend.

However, it could be worth to create an extension that can capture the resource attributes values from your environment if you can access them programmatically (for example they are set with environment variables, or you can read them from a file somewhere in your filesystem). I am not sure we can do that with configuration, but you could also maybe check and enforce that standard environment variables are set as you would expect (for example check that the collector endpoint is set).

Thanks for the detailed response; this is really helpful.

On the licensing side, that aligns with my understanding as well. We are not modifying the agent or creating a derivative, only redistributing it internally with clear attribution. I’ll still double-check with our internal legal team to be safe.

On the need for wrapping

I understand your point that, in general, using the agent as-is with environment variables should be sufficient, and that wrapping introduces additional maintenance overhead.

However, in our case, we’re at an early adoption stage and operating at some scale:

  • We have 30+ containerized microservices under my unit alone

  • Teams are new to OTEL/Elastic observability

  • We need consistent naming, tagging, and resource attributes across services

What we observed in practice:

  • Setting resource attributes correctly (service name, env, etc.) was error-prone and inconsistent

  • Missing or incorrect attributes led to incomplete or fragmented visibility in Kibana

  • Teams had to redeploy multiple times to fix naming/attribute issues

  • Even small mistakes (e.g. endpoint typos) are costly in our environment, since:

    • deployments (especially PROD) involve long approval cycles

    • feedback loops are slow

Why we see value in a wrapper (for now)

Our intent is not to replace standard configuration, but to reduce operational friction and enforce consistency during initial rollout.

The wrapper we’re proposing is intentionally minimal and opinionated:

  • Teams only provide:

    • service.name

    • an internal application code (used for tagging/grouping)

  • Standard resource attributes and naming conventions are applied centrally

  • Collector endpoints (UAT/PROD) are resolved automatically based on environment

  • Optional overrides remain available for debugging or overriding the collector end point

On maintenance overhead

You’re right that version tracking is a concern.

To keep this manageable:

  • We’ve set this up as a simple Maven wrapper project

  • Updating EDOT version is just a version bump in POM

  • CI handles rebuild + publishing to internal repo

  • No custom code or repackaging of internals involved

So operationally, the overhead is quite low (or so I feel).

This reduces:

  • per-service setup complexity

  • risk of misconfiguration

  • repeated trial-and-error deployments

I see, so here you just want to make onboarding and risk of error lower.

In addition to creating a “wrapper”, I think that you have many other ways you could implement that, the main difference is how it is packaged/deployed into the environment

  • Create a shell script to be invoked on every environment to set the expected environment variables, you would still have to deploy and maintain it in every environment
  • Create another java agent and add it before EDOT in the JVM command line, with that you have the ability to set environment variables and/or java system properties at runtime.
  • Create an extension that will provide endpoint configuration and resource attributes at runtime from the context (service name and internal ID), look for examples of `io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider` and `io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer` implementations to see how to implement that.

For all of those options, you would have to maintain and deploy something alongside the EDOT agent, this extra component would be identical on every deployment as it’s just a way to programmatically provide default configuration for your environment.

Technically, if you go further and re-package the EDOT agent entirely, you could even modify the entry point of EDOT and implement option 2 in a single jar.

To me the option 3 is the most appealing because:

  • there is no risk of mis-configuration if `-javaagent` arguments are not in the right order
  • it’s using extension APIs that are designed for this
  • you provide two jars for deployment, each of them with a dedicated role and if they are mis-used or mixed together the JVM won’t start for the `-javaagent` parameter is wrong and no data would be sent if the extension is not configured (no partially or totally inconsistent data resource attributes because the endpoint would not be configured).
  • If you’d be interested in this approach, we can add an example here.

In fact there is already a similar example in the upstream opentelemetry java instrumentation.

With a single `AutoConfigurationCustomizerProvider` implementation you should be able to set resource attributes and the endpoint programatically.

I have so far managed to create a single JAR and deploy successfully as below:

  1. Create pom.xml with elastic-otel dependency
  2. Update Premain-Class as new custom class in pom.xml maven-shade-plugin configuration
  3. Custom class reads environment variables, set’s system properties and invokes Premain method of com.dbs.otel.agent.ElasticAgent.

However, extensions seem more natural way about to do this.

I could technically go Option 3 (create an option of AutoConfigurationCustomizerProvider) and still package the elastic EDOT? But I will still need maven-shade-plugin?

I think you should be quite good with the current state, while using SPI interfaces feels more natural it’s mostly form over function at this point because your use-case does not require it (you just set/override configuration, you don’t add any custom sampler or anything that requires custom code).

There is another option that could also have been used in your case to simplify things, if the application is deployed as a “single-jar” executable (and out side of an application server) you could also have used runtime-attach to bundle the agent binary directly into the application, the wrapper in this case could be either:

  • a standalone library that sets the environment variables
  • a wrapper that does the same and then delegates to the original agent attacher

However this approach also have a few limitations, in particular it makes the agent part of the application dependencies, so if you have long and lengthy validation process it could make updating the agent harder, whereas if it’s “outside” of the application you can consider it as part of the JVM/runtime and thus deal with updates on a different schedule.