Skip to content

Commit 1783bc6

Browse files
traskjaydeluca
authored andcommitted
Fix cloudfoundry workflow (open-telemetry#13290)
1 parent 083a797 commit 1783bc6

14 files changed

+877
-3
lines changed

.github/workflows/release-update-cloudfoundry-index.yml

+5-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ jobs:
1919
steps:
2020
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
2121

22+
# need to run this script before we switch branches
23+
# since the script doesn't exist on the cloudfoundry branch
24+
- name: Use CLA approved github bot
25+
run: .github/scripts/use-cla-approved-github-bot.sh
26+
2227
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
2328
with:
2429
ref: 'cloudfoundry'
@@ -39,9 +44,6 @@ jobs:
3944
- name: display changes
4045
run: git diff
4146

42-
- name: Use CLA approved bot
43-
run: .github/scripts/use-cla-approved-bot.sh
44-
4547
- uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3
4648
id: otelbot-token
4749
with:

instrumentation-docs/build.gradle.kts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
}
4+
5+
dependencies {
6+
implementation("com.google.guava:guava")
7+
8+
testImplementation("org.assertj:assertj-core:3.27.3")
9+
10+
testImplementation(enforcedPlatform("org.junit:junit-bom:5.11.4"))
11+
testImplementation("org.junit.jupiter:junit-jupiter-api")
12+
testImplementation("org.junit.jupiter:junit-jupiter-params")
13+
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
14+
}
15+
16+
otelJava {
17+
minJavaVersionSupported.set(JavaVersion.VERSION_17)
18+
}
19+
20+
tasks {
21+
val generateDocs by registering(JavaExec::class) {
22+
dependsOn(classes)
23+
24+
mainClass.set("io.opentelemetry.instrumentation.docs.MetaDataGenerator")
25+
classpath(sourceSets["main"].runtimeClasspath)
26+
}
27+
}

instrumentation-docs/readme.md

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Doc Generator
2+
3+
Runs analysis on instrumentation modules in order to generate documentation.
4+
5+
6+
## Instrumentation Hierarchy
7+
8+
An "InstrumentationEntity" represents a module that that targets specific code in a framework/library/technology.
9+
Each instrumentation uses muzzle to determine which versions of the target code it supports.
10+
11+
Using these structures as examples:
12+
13+
```
14+
├── instrumentation
15+
│ ├── clickhouse-client-05
16+
│ ├── jaxrs
17+
│ │ ├── jaxrs-1.0
18+
│ │ ├── jaxrs-2.0
19+
│ ├── spring
20+
│ │ ├── spring-cloud-gateway
21+
│ │ │ ├── spring-cloud-gateway-2.0
22+
│ │ │ ├── spring-cloud-gateway-2.2
23+
│ │ │ └── spring-cloud-gateway-common
24+
```
25+
26+
* Name
27+
* Ex: `clickhouse-client-05`, `jaxrs-1.0`, `spring-cloud-gateway-2.0`
28+
* Namespace - direct parent. if none, use name and strip version
29+
* `clickhouse-client`, `jaxrs`, `spring-cloud-gateway`
30+
* Group - top most parent
31+
* `clickhouse-client`, `jaxrs`, `spring`
32+
33+
This information is also referenced in `InstrumentationModule` code for each module:
34+
35+
```java
36+
public class SpringWebInstrumentationModule extends InstrumentationModule
37+
implements ExperimentalInstrumentationModule {
38+
public SpringWebInstrumentationModule() {
39+
super("spring-web", "spring-web-3.1");
40+
}
41+
```
42+
43+
## Instrumentation meta-data:
44+
45+
* name
46+
* Identifier for instrumentation module, used to enable/disable
47+
* Configured in `InstrumentationModule` code for each module
48+
* versions
49+
* List of supported versions by the module
50+
* type
51+
* List of instrumentation types, options of either `library` or `javaagent`
52+
* semantic conventions
53+
* List of semantic conventions supported by the module
54+
* Metrics
55+
* List of metrics generated by the instrumentation
56+
* configuration options / env vars
57+
58+
## Methodology
59+
60+
### Versions targeted
61+
62+
Javaagent versions are determined by the `muzzle` plugin, so we can attempt to parse the gradle files
63+
64+
Library versions are determined by the library versions used in the gradle files.
65+
66+
67+
### Semantic Conventions
68+
69+
Using some code identifiers, we can identify some of the semantic conventions used by the instrumentation modules:
70+
71+
For some basic string examples:
72+
73+
- "db_client_metrics" can be inferred by the use of `DbClientMetrics.get()`
74+
75+
### Other
76+
77+
Could we use tests to determine what the instrumentation is doing? Analyze the telemetry collected
78+
for each instrumentation module?
79+
80+
Logs might have data, or we could configure a test run that configures the agent instrumentation test
81+
helpers to do some things differently?
82+
83+
### TODO
84+
85+
- [ ] What is a good way to summarize the `target_version` information? Is it useful to include?
86+
- [ ] Fix target_version when a variable is used, (example: zio - `dev.zio:zio_2.12:[$zioVersion,)`)
87+
- [ ] Map more semantic conventions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.docs;
7+
8+
import static java.nio.charset.StandardCharsets.UTF_8;
9+
10+
import java.io.BufferedReader;
11+
import java.io.IOException;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.nio.file.Paths;
15+
import java.util.ArrayList;
16+
import java.util.HashMap;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.stream.Collectors;
20+
import java.util.stream.Stream;
21+
22+
class FileSearch {
23+
24+
private FileSearch() {}
25+
26+
public static List<String> getJavaCodePaths(String rootDir) {
27+
Path rootPath = Paths.get(rootDir);
28+
29+
try (Stream<Path> walk = Files.walk(rootPath)) {
30+
return walk.filter(Files::isRegularFile)
31+
.map(Path::toString)
32+
.filter(path -> !path.contains("/build/") && !path.contains("/test/"))
33+
.collect(Collectors.toList());
34+
} catch (IOException e) {
35+
System.out.println("Error traversing directory: " + e.getMessage());
36+
return List.of();
37+
}
38+
}
39+
40+
public static Map<String, String> findStringInFiles(
41+
List<String> fileList, Map<String, String> searchStrings) {
42+
Map<String, String> matchingFiles = new HashMap<>();
43+
for (String filePath : fileList) {
44+
if (filePath.endsWith(".java")) {
45+
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath), UTF_8)) {
46+
String line;
47+
while ((line = reader.readLine()) != null) {
48+
for (Map.Entry<String, String> entry : searchStrings.entrySet()) {
49+
if (line.contains(entry.getValue())) {
50+
matchingFiles.put(entry.getKey(), filePath);
51+
break;
52+
}
53+
}
54+
}
55+
} catch (IOException e) {
56+
// File may have been removed or is inaccessible; ignore or log as needed
57+
}
58+
}
59+
}
60+
return matchingFiles;
61+
}
62+
63+
public static List<InstrumentationPath> getInstrumentationList(String rootDir) {
64+
Path rootPath = Paths.get(rootDir);
65+
66+
try (Stream<Path> walk = Files.walk(rootPath)) {
67+
return walk.filter(Files::isDirectory)
68+
.filter(dir -> !dir.toString().contains("/build"))
69+
.filter(dir -> isValidInstrumentationPath(dir.toString()))
70+
.map(dir -> parseInstrumentationPath(dir.toString()))
71+
.collect(Collectors.toList());
72+
} catch (IOException e) {
73+
System.out.println("Error traversing directory: " + e.getMessage());
74+
return new ArrayList<>();
75+
}
76+
}
77+
78+
static InstrumentationPath parseInstrumentationPath(String filePath) {
79+
if (filePath == null || filePath.isEmpty()) {
80+
return null;
81+
}
82+
83+
String instrumentationSegment = "/instrumentation/";
84+
int startIndex = filePath.indexOf(instrumentationSegment) + instrumentationSegment.length();
85+
String[] parts = filePath.substring(startIndex).split("/");
86+
87+
if (parts.length < 2) {
88+
return null;
89+
}
90+
91+
InstrumentationType instrumentationType =
92+
InstrumentationType.fromString(parts[parts.length - 1]);
93+
String name = parts[parts.length - 2];
94+
String namespace = name.contains("-") ? name.split("-")[0] : name;
95+
96+
return new InstrumentationPath(name, filePath, namespace, namespace, instrumentationType);
97+
}
98+
99+
public static boolean isValidInstrumentationPath(String filePath) {
100+
if (filePath == null || filePath.isEmpty()) {
101+
return false;
102+
}
103+
String instrumentationSegment = "instrumentation/";
104+
105+
if (!filePath.startsWith(instrumentationSegment)) {
106+
return false;
107+
}
108+
109+
int javaagentCount = filePath.split("/javaagent", -1).length - 1;
110+
if (javaagentCount > 1) {
111+
return false;
112+
}
113+
114+
if (filePath.contains("/test/")
115+
|| filePath.contains("/testing")
116+
|| filePath.contains("-common/")
117+
|| filePath.contains("bootstrap/src")) {
118+
return false;
119+
}
120+
121+
return filePath.endsWith("javaagent") || filePath.endsWith("library");
122+
}
123+
124+
public static List<String> findBuildGradleFiles(String rootDir) {
125+
Path rootPath = Paths.get(rootDir);
126+
127+
try (Stream<Path> walk = Files.walk(rootPath)) {
128+
return walk.filter(Files::isRegularFile)
129+
.filter(
130+
path ->
131+
path.getFileName().toString().equals("build.gradle.kts")
132+
&& !path.toString().contains("/testing/"))
133+
.map(Path::toString)
134+
.collect(Collectors.toList());
135+
} catch (IOException e) {
136+
System.out.println("Error traversing directory: " + e.getMessage());
137+
return new ArrayList<>();
138+
}
139+
}
140+
141+
public static String readFileToString(String filePath) {
142+
try {
143+
return Files.readString(Paths.get(filePath));
144+
} catch (IOException e) {
145+
System.out.println("Error reading file: " + e.getMessage());
146+
return null;
147+
}
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.docs;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.regex.Matcher;
11+
import java.util.regex.Pattern;
12+
13+
public class GradleParser {
14+
15+
private GradleParser() {}
16+
17+
private static final Pattern passBlockPattern =
18+
Pattern.compile("pass\\s*\\{(.*?)\\}", Pattern.DOTALL);
19+
20+
/**
21+
* Parses the "muzzle" block from the given Gradle file content and extracts information about
22+
* each "pass { ... }" entry, returning a list of version summary strings.
23+
*
24+
* @param gradleFileContents Contents of a Gradle build file as a String
25+
* @return A list of strings summarizing the group, module, and version ranges
26+
*/
27+
public static List<String> parseMuzzleBlock(String gradleFileContents) {
28+
List<String> results = new ArrayList<>();
29+
30+
// Regex to find each "pass { ... }" block within the muzzle block
31+
// Using a reluctant quantifier to match the smallest block
32+
// that starts with "pass {" and ends with "}" at the same nesting level.
33+
// This simplified approach assumes no nested braces in the pass block.
34+
Matcher passBlockMatcher = passBlockPattern.matcher(gradleFileContents);
35+
36+
while (passBlockMatcher.find()) {
37+
String passBlock = passBlockMatcher.group(1);
38+
39+
String group = extractValue(passBlock, "group\\.set\\(\"([^\"]+)\"\\)");
40+
String module = extractValue(passBlock, "module\\.set\\(\"([^\"]+)\"\\)");
41+
String versionRange = extractValue(passBlock, "versions\\.set\\(\"([^\"]+)\"\\)");
42+
43+
if (group != null && module != null && versionRange != null) {
44+
// Convert something like "[5.0,6.4)" to "5.0, 6.4"
45+
// String rangeSummary = simplifyVersionRange(versionRange);
46+
String summary = group + ":" + module + ":" + versionRange;
47+
results.add(summary);
48+
}
49+
}
50+
51+
return results;
52+
}
53+
54+
/**
55+
* Utility method to extract the first captured group from matching the given regex.
56+
*
57+
* @param text Text to search
58+
* @param regex Regex with a capturing group
59+
* @return The first captured group, or null if not found
60+
*/
61+
private static String extractValue(String text, String regex) {
62+
Pattern pattern = Pattern.compile(regex);
63+
Matcher matcher = pattern.matcher(text);
64+
if (matcher.find()) {
65+
return matcher.group(1);
66+
}
67+
return null;
68+
}
69+
}

0 commit comments

Comments
 (0)