Skip to content

Commit af9d5c2

Browse files
Add OpenTelemetry Java Agent Extension (#165)
* OpenTelemetry Java Agent Extension This extension for the OpenTelemetry Java Agent detects bindings to SAP logging services with active OpenTelemetry support. If such a binding is detected the OpenTelemetry Java Agent is configured with the provided credentials and appropriate resource attributes are configured. The change provides documentation for the OpenTelemetry Java Agent Extension and a sample with the Spring Boot sample app. --------- Signed-off-by: Karsten Schnitter <[email protected]> Co-authored-by: JannikBrand <[email protected]>
1 parent 936f7a7 commit af9d5c2

17 files changed

+860
-6
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# OpenTelemetry Java Agent Extension for SAP Cloud Logging
2+
3+
This module provides an extension for the [OpenTelemetry Java Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/).
4+
The extension scans the service bindings of an application for SAP Cloud Logging.
5+
If such a binding is found, the OpenTelemetry Java Agent is configured to ship observability data to that service.
6+
Thus, this extension provides a convenient auto-instrumentation for Java applications running on SAP BTP.
7+
8+
The extension provides two main features:
9+
10+
* auto-configuration of the OpenTelemetry connection to SAP Cloud Logging
11+
* adding resource attributes to describe the CF application
12+
13+
See the section on [configuration](#configuration) for further details.
14+
15+
## Quickstart Guide
16+
17+
Any Java application can be instrumented with the OpenTelemetry Java Agent and this extension by adding the following arguments to the java command:
18+
19+
```sh
20+
java -javaagent:/path/to/opentelemetry-javaagent-<version>.jar \
21+
-Dotel.javaagent-extensions=/path/to/cf-java-logging-support-opentelemetry-agent-extension-<versions>.jar \
22+
# your Java application command
23+
```
24+
25+
If you are using Spring Boot, you can bundle both dependencies with the application.
26+
See the Maven pom of the [Spring Boot sample application](../sample-spring-boot/pom.xml) for details.
27+
When deployed to a Cloud Foundry runtime environment, the Spring Boot jar is expanded, so that the agent and extension jar are available during application start.
28+
In that case, the following Java arguments are required:
29+
30+
```sh
31+
java -javaagent:BOOT-INF/lib/opentelemetry-javaagent-<version>.jar \
32+
-Dotel.javaagent.extensions=BOOT-INF/lib/cf-java-logging-support-opentelemetry-agent-extension-<version>.jar \
33+
# your Java application command
34+
```
35+
36+
See the [example manifest](../sample-spring-boot/manifest-otel-javaagent.yml), how this translates into a deployment description.
37+
38+
For the instrumentation to send observability data to SAP Cloud Logging, the application needs to be bound to a corresponding service instance.
39+
The service instance can be either managed or [user-provided](#using-user-provided-service-instances).
40+
41+
Note, that the OpenTelemetry Java Agent currently only sends traces and metrics by default.
42+
To enable logs, the additional property `-Dotel.logs.exporter=otlp` is required.
43+
44+
## Configuration
45+
46+
The OpenTelemetry Java Agent supports a wide variety of [configuration options](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/).
47+
As the extension provides configuration via SPI, all its configuration takes lower precedence than other configuration options for OpenTelemetry.
48+
Users can easily overwrite any setting using environment variables or system properties.
49+
50+
### Configuring the Extension
51+
52+
The extension itself can be configured by specifying the following system properties:
53+
54+
| Property | Default Value | Comment |
55+
|----------|---------------|---------|
56+
| `com.sap.otel.extension.cloud-logging.label` | `cloud-logging` | The label of the managed service binding to bind to. |
57+
| `com.sap.otel.extension.cloud-logging.tag` | `Cloud Logging` | The tag of any service binding (managed or user-provided) to bind to. |
58+
| `otel.javaagent.extension.sap.cf.resource.enabled` or `env(OTEL_JAVAAGENT_EXTENSION_SAP_CF_RESOURCE_ENABLED)` | `true` | Whether to add CF resource attributes to all events. |
59+
60+
The extension will scan the environment variable `VCAP_SERVICES` for CF service bindings.
61+
User-provided bindings will take precedence over managed bindings of the configured label ("cloud-logging" by default).
62+
All matching bindings are filtered for the configured tag ("Cloud Logging" by default).
63+
The first binding will be taken for configuration for the OpenTelemetry exporter.
64+
Preferring user-provided services over managed service instances allows better control of the binding properties, e.g. syslog drains.
65+
66+
### Recommended Agent Configuration
67+
68+
The OpenTelemetry Java Agent offers a lot of configuration options.
69+
The following set of properties is recommended to be used with the extension:
70+
71+
```sh
72+
java -javaagent:/path/to/opentelemetry-javaagent-<version>.jar \
73+
-Dotel.javaagent-extensions=/path/to/cf-java-logging-support-opentelemetry-agent-extension-<versions>.jar \
74+
# enable logs \
75+
-Dotel.logs.exporter=otlp \
76+
# reroute agent logs to otlp \
77+
-Dotel.javaagent.logging=application
78+
# configure logback context \
79+
-Dotel.instrumentation.logback-appender.experimental.capture-mdc-attributes=* \
80+
-Dotel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true \
81+
-Dotel.instrumentation.logback-appender.experimental.capture-code-attributes=true \
82+
-Dotel.instrumentation.logback-appender.experimental-log-attributes=true \
83+
# Disable large resource attributes
84+
-Dotel.experimental.resource.disabled-keys=process.command_line,process.command_args,process.executable.path
85+
```
86+
87+
The [OpenTelemetry Java Instrumentation project](https://github.com/open-telemetry/opentelemetry-java-instrumentation) provides detailed documentation on the configuration properties for [Logback](https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/logback/logback-appender-1.0/javaagent) and [Log4j](https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/log4j/log4j-appender-2.17/javaagent).
88+
89+
## Using User-Provided Service Instances
90+
91+
The extension provides support not only for managed service instance of SAP Cloud Logging but also for user-provided service instances.
92+
This helps to fine-tune the configuration, e.g. leave out or reconfigure the syslog drain.
93+
Furthermore, this helps on sharing service instances across CF orgs or landscapes.
94+
95+
The extension requires four fields in the user-provided service credentials and needs to be tagged with the `com.sap.otel.extension.cloud-logging.tag` (default: `Cloud Logging`) documented in section [Configuration](#configuration).
96+
97+
| Field name | Contents |
98+
|------------|---------|
99+
| `ingest-otlp-endpoint` | The OTLP endpoint including port. It will be prefixed with `https://`. |
100+
| `ingest-otlp-key` | The mTLS client key in PCKS#8 format. Line breaks as `\n`. |
101+
| `ingest-otlp-cert`| The mTLS client certificate in PEM format matching the client key. Line breaks as `\n`. |
102+
| `server-ca` | The trusted mTLS server certificate in PEM format. Line breaks as `\n`. |
103+
104+
If you have a SAP Cloud Logging service key, you can generate the required JSON file with jq:
105+
106+
```bash
107+
cf service-key cls test \
108+
| tail -n +2 \
109+
| jq '.credentials | {"ingest-otlp-endpoint":."ingest-otlp-endpoint", "ingest-otlp-cert":."ingest-otlp-cert", "ingest-otlp-key":."ingest-otlp-key", "server-ca":."server-ca"}' \
110+
> ups.json
111+
```
112+
113+
Using this file, you can create the required user-provided service:
114+
115+
```bash
116+
cf cups <your-service-name> -p ups.json -t "Cloud Logging"
117+
```
118+
119+
Note, that you can easily feed arbitrary credentials to the extension.
120+
It does not need to be SAP Cloud Logging.
121+
You can even change the tag using the configuration parameters of the extension.
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<parent>
4+
<artifactId>cf-java-logging-support-parent</artifactId>
5+
<groupId>com.sap.hcp.cf.logging</groupId>
6+
<version>3.7.1</version>
7+
</parent>
8+
<modelVersion>4.0.0</modelVersion>
9+
<artifactId>cf-java-logging-support-opentelemetry-agent-extension</artifactId>
10+
<name>cf-java-logging-support-opentelemetry-agent-extension</name>
11+
<build>
12+
<plugins>
13+
<plugin>
14+
<artifactId>maven-shade-plugin</artifactId>
15+
<version>3.5.1</version>
16+
<executions>
17+
<execution>
18+
<phase>package</phase>
19+
<goals>
20+
<goal>shade</goal>
21+
</goals>
22+
<configuration>
23+
<filters>
24+
<filter>
25+
<artifact>io.pivotal.cfenv:java-cfenv</artifact>
26+
<includes>
27+
<include>io/pivotal/cfenv/**</include>
28+
</includes>
29+
</filter>
30+
</filters>
31+
<artifactSet>
32+
<excludes>
33+
<exclude>io.opentelemetry</exclude>
34+
<exclude>com.fasterxml.jackson.core</exclude>
35+
</excludes>
36+
</artifactSet>
37+
</configuration>
38+
</execution>
39+
</executions>
40+
</plugin>
41+
<plugin>
42+
<artifactId>maven-compiler-plugin</artifactId>
43+
<configuration>
44+
<source>11</source>
45+
<target>11</target>
46+
</configuration>
47+
</plugin>
48+
</plugins>
49+
</build>
50+
<dependencies>
51+
<dependency>
52+
<groupId>io.opentelemetry</groupId>
53+
<artifactId>opentelemetry-sdk-common</artifactId>
54+
<version>1.31.0</version>
55+
<scope>provided</scope>
56+
</dependency>
57+
<dependency>
58+
<groupId>io.opentelemetry</groupId>
59+
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
60+
<version>1.31.0</version>
61+
<scope>provided</scope>
62+
</dependency>
63+
<dependency>
64+
<groupId>org.slf4j</groupId>
65+
<artifactId>slf4j-api</artifactId>
66+
<version>1.7.36</version>
67+
<scope>provided</scope>
68+
</dependency>
69+
<dependency>
70+
<groupId>org.hamcrest</groupId>
71+
<artifactId>hamcrest-library</artifactId>
72+
<version>1.3</version>
73+
<scope>test</scope>
74+
<exclusions>
75+
<exclusion>
76+
<artifactId>hamcrest-core</artifactId>
77+
<groupId>org.hamcrest</groupId>
78+
</exclusion>
79+
</exclusions>
80+
</dependency>
81+
<dependency>
82+
<groupId>junit</groupId>
83+
<artifactId>junit</artifactId>
84+
<version>4.13.2</version>
85+
<scope>test</scope>
86+
<exclusions>
87+
<exclusion>
88+
<artifactId>hamcrest-core</artifactId>
89+
<groupId>org.hamcrest</groupId>
90+
</exclusion>
91+
</exclusions>
92+
</dependency>
93+
<dependency>
94+
<groupId>org.mockito</groupId>
95+
<artifactId>mockito-all</artifactId>
96+
<version>1.10.19</version>
97+
<scope>test</scope>
98+
</dependency>
99+
<dependency>
100+
<groupId>org.openjdk.jmh</groupId>
101+
<artifactId>jmh-core</artifactId>
102+
<version>1.36</version>
103+
<scope>test</scope>
104+
<exclusions>
105+
<exclusion>
106+
<artifactId>jopt-simple</artifactId>
107+
<groupId>net.sf.jopt-simple</groupId>
108+
</exclusion>
109+
<exclusion>
110+
<artifactId>commons-math3</artifactId>
111+
<groupId>org.apache.commons</groupId>
112+
</exclusion>
113+
</exclusions>
114+
</dependency>
115+
<dependency>
116+
<groupId>org.openjdk.jmh</groupId>
117+
<artifactId>jmh-generator-annprocess</artifactId>
118+
<version>1.36</version>
119+
<scope>test</scope>
120+
</dependency>
121+
</dependencies>
122+
<dependencyManagement>
123+
<dependencies>
124+
<dependency>
125+
<groupId>io.opentelemetry</groupId>
126+
<artifactId>opentelemetry-bom</artifactId>
127+
<version>${opentelemetry.sdk.version}</version>
128+
<type>pom</type>
129+
<scope>import</scope>
130+
</dependency>
131+
<dependency>
132+
<groupId>io.opentelemetry</groupId>
133+
<artifactId>opentelemetry-bom-alpha</artifactId>
134+
<version>${opentelemetry.sdk.version}-alpha</version>
135+
<type>pom</type>
136+
<scope>import</scope>
137+
</dependency>
138+
</dependencies>
139+
</dependencyManagement>
140+
<properties>
141+
<maven.compiler.target>11</maven.compiler.target>
142+
<maven.compiler.source>11</maven.compiler.source>
143+
<opentelemetry.sdk.version>1.31.0</opentelemetry.sdk.version>
144+
</properties>
145+
</project>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<artifactId>cf-java-logging-support-opentelemetry-agent-extension</artifactId>
8+
<packaging>jar</packaging>
9+
10+
<name>cf-java-logging-support-opentelemetry-agent-extension</name>
11+
12+
<parent>
13+
<artifactId>cf-java-logging-support-parent</artifactId>
14+
<groupId>com.sap.hcp.cf.logging</groupId>
15+
<version>3.7.1</version>
16+
</parent>
17+
18+
<properties>
19+
<maven.compiler.source>11</maven.compiler.source>
20+
<maven.compiler.target>11</maven.compiler.target>
21+
<opentelemetry.sdk.version>1.31.0</opentelemetry.sdk.version>
22+
</properties>
23+
24+
<dependencyManagement>
25+
<dependencies>
26+
<dependency>
27+
<groupId>io.opentelemetry</groupId>
28+
<artifactId>opentelemetry-bom</artifactId>
29+
<version>${opentelemetry.sdk.version}</version>
30+
<type>pom</type>
31+
<scope>import</scope>
32+
</dependency>
33+
<dependency>
34+
<groupId>io.opentelemetry</groupId>
35+
<artifactId>opentelemetry-bom-alpha</artifactId>
36+
<version>${opentelemetry.sdk.version}-alpha</version>
37+
<type>pom</type>
38+
<scope>import</scope>
39+
</dependency>
40+
</dependencies>
41+
</dependencyManagement>
42+
43+
<dependencies>
44+
<dependency>
45+
<groupId>io.opentelemetry</groupId>
46+
<artifactId>opentelemetry-sdk-common</artifactId>
47+
<scope>provided</scope>
48+
</dependency>
49+
<dependency>
50+
<groupId>io.opentelemetry</groupId>
51+
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
52+
<scope>provided</scope>
53+
</dependency>
54+
<dependency>
55+
<groupId>io.pivotal.cfenv</groupId>
56+
<artifactId>java-cfenv</artifactId>
57+
<version>2.5.0</version>
58+
</dependency>
59+
</dependencies>
60+
61+
<build>
62+
<plugins>
63+
<plugin>
64+
<groupId>org.apache.maven.plugins</groupId>
65+
<artifactId>maven-shade-plugin</artifactId>
66+
<version>3.5.1</version>
67+
<executions>
68+
<execution>
69+
<phase>package</phase>
70+
<goals>
71+
<goal>shade</goal>
72+
</goals>
73+
<configuration>
74+
<filters>
75+
<filter>
76+
<artifact>io.pivotal.cfenv:java-cfenv</artifact>
77+
<includes>
78+
<include>io/pivotal/cfenv/**</include>
79+
</includes>
80+
</filter>
81+
</filters>
82+
<artifactSet>
83+
<excludes>
84+
<exclude>io.opentelemetry</exclude>
85+
<exclude>com.fasterxml.jackson.core</exclude>
86+
</excludes>
87+
</artifactSet>
88+
</configuration>
89+
</execution>
90+
</executions>
91+
</plugin>
92+
<plugin>
93+
<groupId>org.apache.maven.plugins</groupId>
94+
<artifactId>maven-compiler-plugin</artifactId>
95+
<configuration>
96+
<source>11</source>
97+
<target>11</target>
98+
</configuration>
99+
</plugin>
100+
</plugins>
101+
</build>
102+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.sap.hcf.cf.logging.opentelemetry.agent.ext;
2+
3+
import com.sap.hcf.cf.logging.opentelemetry.agent.ext.attributes.CloudFoundryResourceCustomizer;
4+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
5+
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
6+
import io.opentelemetry.sdk.resources.Resource;
7+
import io.pivotal.cfenv.core.CfEnv;
8+
9+
public class CloudFoundryResourceProvider implements ResourceProvider {
10+
11+
private final CloudFoundryResourceCustomizer customizer;
12+
13+
public CloudFoundryResourceProvider() {
14+
this(new CfEnv());
15+
}
16+
17+
public CloudFoundryResourceProvider(CfEnv cfEnv) {
18+
this.customizer = new CloudFoundryResourceCustomizer(cfEnv);
19+
}
20+
21+
@Override
22+
public Resource createResource(ConfigProperties configProperties) {
23+
return customizer.apply(null, configProperties);
24+
}
25+
}

0 commit comments

Comments
 (0)