Skip to content

Commit ba28a2d

Browse files
committed
use DeclarativeConfigUtil for library config usage
1 parent 92475f1 commit ba28a2d

File tree

25 files changed

+782
-218
lines changed

25 files changed

+782
-218
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
### ⚠️ Breaking Changes
88

9+
- Rename `otel.experimental.javascript-snippet` to
10+
`otel.instrumentation.servlet.experimental.javascript-snippet` to follow naming conventions
11+
([#15339](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/15339))
12+
13+
### ⚠️ Breaking Changes
14+
915
- ActiveMQ Classic JMX metrics: rename attributes and metrics to align
1016
with semantic conventions (see PR description for specifics)
1117
([#14996](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14996))

docs/advanced-configuration-options.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ This feature is designed for integrating client-side monitoring.
4545
We plan to integrate OpenTelemetry's own client-side monitoring solution by default once it's available
4646
(see the [browser instrumentation proposal](https://github.com/open-telemetry/community/blob/main/projects/browser-phase-1.md)).
4747

48-
| System property | Environment variable | Purpose |
49-
|--------------------------------------|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
50-
| otel.experimental.javascript-snippet | OTEL_EXPERIMENTAL_JAVASCRIPT_SNIPPET | Experimental setting to inject a JavaScript snippet into HTML responses after the opening `<head>` tag. The value should be a complete JavaScript snippet including `<script>` tags if needed, e.g. `-Dotel.experimental.javascript-snippet="<script>console.log('Hello world!');</script>"` |
48+
| System property | Environment variable | Purpose |
49+
|----------------------------------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
50+
| `otel.instrumentation.servlet.experimental.javascript-snippet` | `OTEL_INSTRUMENTATION_SERVLET_EXPERIMENTAL_JAVASCRIPT_SNIPPET` | Experimental setting to inject a JavaScript snippet into HTML responses after the opening `<head>` tag. The value should be a complete JavaScript snippet including `<script>` tags if needed, e.g. `-Dotel.instrumentation.servlet.experimental.javascript-snippet="<script>console.log('Hello world!');</script>"` |
5151

5252
**Important notes:**
5353

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
Comparing source compatibility of opentelemetry-instrumentation-api-2.24.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.23.0.jar
2-
No changes.
2+
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder (not serializable)
3+
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
4+
GENERIC TEMPLATES: === REQUEST:java.lang.Object, === RESPONSE:java.lang.Object
5+
+++ NEW METHOD: PUBLIC(+) STATIC(+) boolean isDeclarativeConfig(io.opentelemetry.api.OpenTelemetry)

instrumentation-api-incubator/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies {
2424
testImplementation("io.opentelemetry:opentelemetry-sdk")
2525
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
2626
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
27+
testImplementation("org.junit-pioneer:junit-pioneer")
2728
}
2829

2930
val jflexSourceDir = layout.projectDirectory.dir("src/main/jflex")
@@ -98,4 +99,11 @@ tasks {
9899
check {
99100
dependsOn(testStableSemconv, testBothSemconv)
100101
}
102+
103+
withType<Test>().configureEach {
104+
// required on jdk17 for junit pioneer
105+
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
106+
jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
107+
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
108+
}
101109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.config.internal;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.api.incubator.ExtendedOpenTelemetry;
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
12+
/**
13+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
14+
* any time.
15+
*/
16+
public final class DeclarativeConfigUtil {
17+
18+
// this is a temporary convenience until getConfigProvider is available on OpenTelemetry
19+
public static DeclarativeConfigProperties getStructured(
20+
OpenTelemetry openTelemetry, String name, DeclarativeConfigProperties defaultValue) {
21+
22+
if (openTelemetry instanceof ExtendedOpenTelemetry) {
23+
ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry;
24+
DeclarativeConfigProperties instrumentationConfig =
25+
extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig();
26+
if (instrumentationConfig == null) {
27+
return defaultValue;
28+
}
29+
return instrumentationConfig.getStructured(name, defaultValue);
30+
} else {
31+
return SystemPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig()
32+
.getStructured(name, defaultValue);
33+
}
34+
}
35+
36+
private DeclarativeConfigUtil() {}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.config.internal;
7+
8+
import static java.util.Collections.emptySet;
9+
10+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
11+
import io.opentelemetry.common.ComponentLoader;
12+
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
13+
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.Collections;
16+
import java.util.List;
17+
import java.util.Set;
18+
import java.util.stream.Collectors;
19+
import javax.annotation.Nullable;
20+
21+
/**
22+
* Implementation of {@link DeclarativeConfigProperties} backed by system properties and
23+
* environmental variables.
24+
*
25+
* <p>It tracks the navigation path and only resolves to system properties at the leaf node when a
26+
* value is actually requested.
27+
*/
28+
final class SystemPropertiesBackedDeclarativeConfigProperties
29+
implements DeclarativeConfigProperties {
30+
31+
private final List<String> path;
32+
33+
public static DeclarativeConfigProperties createInstrumentationConfig() {
34+
return new SystemPropertiesBackedDeclarativeConfigProperties(Collections.emptyList());
35+
}
36+
37+
private SystemPropertiesBackedDeclarativeConfigProperties(List<String> path) {
38+
this.path = path;
39+
}
40+
41+
@Nullable
42+
@Override
43+
public String getString(String name) {
44+
String fullPath = pathWithName(name);
45+
return ConfigPropertiesUtil.getString(toPropertyKey(fullPath));
46+
}
47+
48+
@Nullable
49+
@Override
50+
public Boolean getBoolean(String name) {
51+
String value = getString(name);
52+
return value == null ? null : Boolean.parseBoolean(value);
53+
}
54+
55+
@Nullable
56+
@Override
57+
public Integer getInt(String name) {
58+
String strValue = getString(name);
59+
if (strValue == null) {
60+
return null;
61+
}
62+
try {
63+
return Integer.parseInt(strValue);
64+
} catch (NumberFormatException ignored) {
65+
return null;
66+
}
67+
}
68+
69+
@Nullable
70+
@Override
71+
public Long getLong(String name) {
72+
String strValue = getString(name);
73+
if (strValue == null) {
74+
return null;
75+
}
76+
try {
77+
return Long.getLong(strValue);
78+
} catch (NumberFormatException ignored) {
79+
return null;
80+
}
81+
}
82+
83+
@Nullable
84+
@Override
85+
public Double getDouble(String name) {
86+
String strValue = getString(name);
87+
if (strValue == null) {
88+
return null;
89+
}
90+
try {
91+
return Double.parseDouble(strValue);
92+
} catch (NumberFormatException ignored) {
93+
return null;
94+
}
95+
}
96+
97+
/**
98+
* Important: this method should return null if there is no structured child with the given name,
99+
* but unfortunately that is not implementable on top of ConfigProperties.
100+
*
101+
* <p>This will be misleading if anyone is comparing the return value to null.
102+
*/
103+
@Override
104+
public DeclarativeConfigProperties getStructured(String name) {
105+
List<String> newPath = new ArrayList<>(path);
106+
newPath.add(name);
107+
return new SystemPropertiesBackedDeclarativeConfigProperties(newPath);
108+
}
109+
110+
@Nullable
111+
@Override
112+
@SuppressWarnings("unchecked") // Safe because T is known to be String via scalarType check
113+
public <T> List<T> getScalarList(String name, Class<T> scalarType) {
114+
if (scalarType != String.class) {
115+
return null;
116+
}
117+
String value = getString(name);
118+
return value == null ? null : (List<T>) filterBlanksAndNulls(value.split(","));
119+
}
120+
121+
@Nullable
122+
@Override
123+
public List<DeclarativeConfigProperties> getStructuredList(String name) {
124+
return null;
125+
}
126+
127+
@Override
128+
public Set<String> getPropertyKeys() {
129+
// this is not supported when using system properties based configuration
130+
return emptySet();
131+
}
132+
133+
@Override
134+
public ComponentLoader getComponentLoader() {
135+
throw new UnsupportedOperationException();
136+
}
137+
138+
private String pathWithName(String name) {
139+
if (path.isEmpty()) {
140+
return name;
141+
}
142+
return String.join(".", path) + "." + name;
143+
}
144+
145+
private static String toPropertyKey(String fullPath) {
146+
String translatedPath = translatePath(fullPath);
147+
// Standard mapping
148+
return "otel.instrumentation." + translatedPath;
149+
}
150+
151+
private static String translatePath(String path) {
152+
StringBuilder result = new StringBuilder();
153+
for (String segment : path.split("\\.")) {
154+
// Skip "java" segment - it doesn't exist in system properties
155+
if ("java".equals(segment)) {
156+
continue;
157+
}
158+
if (result.length() > 0) {
159+
result.append(".");
160+
}
161+
result.append(translateName(segment));
162+
}
163+
return result.toString();
164+
}
165+
166+
private static String translateName(String name) {
167+
if (name.endsWith("/development")) {
168+
return "experimental."
169+
+ name.substring(0, name.length() - "/development".length()).replace('_', '-');
170+
}
171+
return name.replace('_', '-');
172+
}
173+
174+
private static List<String> filterBlanksAndNulls(String[] values) {
175+
return Arrays.stream(values)
176+
.map(String::trim)
177+
.filter(s -> !s.isEmpty())
178+
.collect(Collectors.toList());
179+
}
180+
}

0 commit comments

Comments
 (0)