Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ For historical analysis documents from development sessions, see [`docs/archive/
* [DataDog](docs/framework-datadog_javaagent.md) ([Configuration](docs/framework-datadog_javaagent.md#configuration)
* [Debug](docs/framework-debug.md) ([Configuration](docs/framework-debug.md#configuration))
* [Elastic APM Agent](docs/framework-elastic_apm_agent.md) ([Configuration](docs/framework-elastic_apm_agent.md#configuration))
* [Elastic OTel Java Agent](docs/framework-elastic_otel_java_agent.md) ([Configuration](docs/framework-elastic_otel_java_agent.md#configuration))
* [Dynatrace SaaS/Managed OneAgent](docs/framework-dynatrace_one_agent.md) ([Configuration](docs/framework-dynatrace_one_agent.md#configuration))
* [Google Stackdriver Profiler](docs/framework-google_stackdriver_profiler.md) ([Configuration](docs/framework-google_stackdriver_profiler.md#configuration))
* [Introscope Agent](docs/framework-introscope_agent.md) ([Configuration](docs/framework-introscope_agent.md#configuration))
Expand Down
84 changes: 84 additions & 0 deletions docs/framework-elastic_otel_java_agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Elastic OTel Java Agent Framework

The Elastic OTel Java Agent Framework causes an application to be automatically configured to work with the [Elastic Distribution of OpenTelemetry Java][].

<table>
<tr>
<td><strong>Detection Criterion</strong></td>
<td>Existence of a bound Elastic OTel service. The service must have a name containing, or a label or tag matching, <code>elastic-otel</code>, <code>edot-java</code>, or <code>elastic-edot</code>, and the combined service and environment configuration must provide an OTLP endpoint plus authentication credentials. The framework can also be enabled explicitly with <code>ELASTIC_OTEL_AGENT</code> when the same required configuration is present.</td>
</tr>
<tr>
<td><strong>Tags</strong></td>
<td><tt>elastic-otel-javaagent=&lt;version&gt;</tt></td>
</tr>
</table>

Tags are printed to standard output by the buildpack detect script.

## User-Provided Service

Users can provide their own Elastic OTel service. A user-provided service must have a name containing or a tag matching `elastic-otel`, `edot-java`, or `elastic-edot` so that the framework configures the application with the Elastic OTel Java agent.

The credential payload can contain the following entries.

| Name | Description |
| ---- | ----------- |
| `otel.exporter.otlp.endpoint` | The OTLP endpoint for your Elastic deployment or EDOT Collector. Aliases: `otlp_endpoint`, `otlpEndpoint`, `endpoint`. |
| `otel.exporter.otlp.headers` | Explicit OTLP headers, for example `Authorization=ApiKey <key>`. If present, this takes precedence over derived authentication headers. |
| `api_key` | Elastic API key. Converted to `otel.exporter.otlp.headers=Authorization=ApiKey <key>`. |
| `secret_token` | Bearer token. Converted to `otel.exporter.otlp.headers=Authorization=Bearer <token>`. |
| `otel.*` | Any additional OpenTelemetry configuration applied as JVM system properties. |
| `elastic.otel.*` | Any additional Elastic OTel configuration applied as JVM system properties. |

### Creating an Elastic OTel User-Provided Service

Example minimal configuration:

```
cf cups my-elastic-otel-service -p '{"otel.exporter.otlp.endpoint":"https://my-deployment.ingest.us-west1.gcp.cloud.es.io","api_key":"my-api-key"}'
Comment thread
jackshirazi marked this conversation as resolved.
```

Example configuration with explicit OTLP headers and resource attributes:

```
cf cups my-elastic-otel-service -p '{"otel.exporter.otlp.endpoint":"https://my-deployment.ingest.us-west1.gcp.cloud.es.io","otel.exporter.otlp.headers":"Authorization=ApiKey my-api-key","otel.resource.attributes":"deployment.environment.name=production"}'
```

Bind your application to the service using:

`cf bind-service my-app-name my-elastic-otel-service`

or use the `services` block in the application manifest file.

## Configuration

For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].

The framework uses the dependency version configured in `manifest.yml`.

| Name | Description |
| ---- | ----------- |
| `version` | The version of Elastic OTel Java agent to use. The current buildpack manifest uses the `elastic-otel-javaagent` dependency. |

The framework sets `otel.service.name` from service credentials, then `OTEL_SERVICE_NAME`, then `VCAP_APPLICATION.application_name`, using the first configured value. It also sets `deployment.environment.name` from `VCAP_APPLICATION.space_name` when `otel.resource.attributes` is not configured. Generic OTLP environment variables do not select the Elastic distribution by themselves; set `ELASTIC_OTEL_AGENT` to choose it explicitly.

Authentication headers are provided through `OTEL_EXPORTER_OTLP_HEADERS` rather than a JVM system property. When the headers come from service credentials, the framework resolves them from `VCAP_SERVICES` when the application starts. This keeps credentials out of the JVM command line and allows credential rotation with an application restart instead of a restage.

> **Warning**
> Do not bind this framework alongside the Elastic APM Agent, OpenTelemetry Javaagent, Splunk OTel Java Agent, or another Java agent framework for the same application. Running multiple Java agents in the same JVM can cause duplicate telemetry or conflicting instrumentation.

## Troubleshooting: Multiple Agents or Wrong Agent

- **Elastic and Splunk agents both start:** `OTEL_EXPORTER_OTLP_ENDPOINT` is set as an environment variable during staging. Also set `ELASTIC_OTEL_AGENT=true`, or put the endpoint in the service credentials instead.
- **Upstream OpenTelemetry agent starts instead of Elastic:** The Elastic service lacks authentication credentials. Add `api_key` or `otel.exporter.otlp.headers`.
- **Elastic and OpenTelemetry agents both start:** An `otel-collector` service or another `otel`-named or `otel`-tagged service is also bound. Unbind it.
- **Telemetry does not reach Elastic:** `OTEL_EXPORTER_OTLP_ENDPOINT`, set as an environment variable during staging, overrides the endpoint from the service binding. Remove it, or make sure it points to Elastic.

## Migration From Elastic APM Agent

The Elastic OTel Java agent uses OTLP configuration. It does not use Elastic APM agent settings such as `server_url` or `secret_token` with the `elastic.apm.*` prefix.

When migrating, create a new service binding named or tagged for Elastic OTel and provide an OTLP endpoint plus `otel.exporter.otlp.headers` or `api_key`.

[Configuration and Extension]: ../README.md#configuration-and-extension
[Elastic Distribution of OpenTelemetry Java]: https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/java
3 changes: 2 additions & 1 deletion docs/framework-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ Line | Framework Name | Priority | Notes
75 | SpringAutoReconfiguration | 41 | Spring framework
76 | SplunkOtelJavaAgent | 42 | Observability agent
77 | SpringInsight | 43 | Spring monitoring
78 | SkyWalkingAgent | 44 | APM agent
78 | SkyWalkingAgent | 41 | APM agent
78a | ElasticOtelJavaAgent | 44 | Observability agent
79 | YourKitProfiler | 45 | Profiler
80 | JavaSecurity | 47 | Security configuration
81 | JavaOpts | 99 | ⚠️ USER-DEFINED OPTS (ALWAYS LAST)
Expand Down
16 changes: 16 additions & 0 deletions manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ packaging_profiles:
exclude:
- datadog-javaagent
- elastic-apm-agent
- elastic-otel-javaagent
- azure-application-insights
- skywalking-agent
- splunk-otel-javaagent
Expand All @@ -44,6 +45,7 @@ packaging_profiles:
exclude:
- datadog-javaagent
- elastic-apm-agent
- elastic-otel-javaagent
- azure-application-insights
- skywalking-agent
- splunk-otel-javaagent
Expand Down Expand Up @@ -94,6 +96,8 @@ default_versions:
version: 1.x
- name: elastic-apm-agent
version: 1.x
- name: elastic-otel-javaagent
version: 1.x
- name: azure-application-insights
version: 3.x
- name: skywalking-agent
Expand Down Expand Up @@ -182,6 +186,9 @@ url_to_dependency_map:
- match: elastic-apm-agent-(\d+\.\d+\.\d+)
name: elastic-apm-agent
version: "$1"
- match: elastic-otel-javaagent[-_](\d+\.\d+\.\d+)
name: elastic-otel-javaagent
version: "$1"
- match: applicationinsights-agent-(\d+\.\d+\.\d+)
name: azure-application-insights
version: "$1"
Expand Down Expand Up @@ -321,6 +328,15 @@ dependencies:
- cflinuxfs5
source: https://repo1.maven.org/maven2/co/elastic/apm/elastic-apm-agent/1.56.0/elastic-apm-agent-1.56.0.jar
source_sha256: ''
- name: elastic-otel-javaagent
version: 1.11.0
uri: https://repo1.maven.org/maven2/co/elastic/otel/elastic-otel-javaagent/1.11.0/elastic-otel-javaagent-1.11.0.jar
sha256: 61c0aae98e165ddfa3096d5d8ed16c159992b8ae284bf6296800839008cdb707
cf_stacks:
- cflinuxfs4
- cflinuxfs5
source: https://repo1.maven.org/maven2/co/elastic/otel/elastic-otel-javaagent/1.11.0/elastic-otel-javaagent-1.11.0.jar
source_sha256: 61c0aae98e165ddfa3096d5d8ed16c159992b8ae284bf6296800839008cdb707
- name: google-stackdriver-profiler
version: 0.4.0
uri: https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz
Expand Down
39 changes: 39 additions & 0 deletions src/integration/frameworks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,45 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin
})
})

context("with Elastic OTel service binding", func() {
it("detects and installs Elastic OTel Java agent", func() {
deployment, logs, err := platform.Deploy.
WithServices(map[string]switchblade.Service{
"elastic-otel": {
"otel.exporter.otlp.endpoint": "https://elastic-otel.example.com:443",
"api_key": "test-api-key",
},
}).
WithEnv(map[string]string{
"BP_JAVA_VERSION": "17",
}).
Execute(name, filepath.Join(fixtures, "apps", "integration_valid"))
Expect(err).NotTo(HaveOccurred(), logs.String)

Expect(logs.String()).To(ContainSubstring("Elastic OTel"))
Eventually(deployment).Should(matchers.Serve(ContainSubstring("Hello from Java Main!")))
})

it("configures Elastic OTel with explicit OTLP headers", func() {
deployment, logs, err := platform.Deploy.
WithServices(map[string]switchblade.Service{
"my-elastic-otel": {
"otel.exporter.otlp.endpoint": "https://elastic-otel.production.example.com:443",
"otel.exporter.otlp.headers": "Authorization=ApiKey explicit-key",
"otel.service.name": "my-java-app",
},
}).
WithEnv(map[string]string{
"BP_JAVA_VERSION": "17",
}).
Execute(name, filepath.Join(fixtures, "containers", "spring_boot_staged"))
Expect(err).NotTo(HaveOccurred(), logs.String)

Expect(logs.String()).To(ContainSubstring("Elastic OTel"))
Eventually(deployment).Should(matchers.Serve(ContainSubstring("Hello from Spring Boot")))
})
})

context("with OpenTelemetry service binding", func() {
it("detects and installs OpenTelemetry Javaagent", func() {
deployment, logs, err := platform.Deploy.
Expand Down
Loading