Add scope name to metadata (#13531)

This commit is contained in:
Jay DeLuca 2025-03-19 22:51:48 -04:00 committed by GitHub
parent 511a9c93ea
commit 786512b792
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 1153 additions and 231 deletions

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ otelJava {
dependencies {
implementation("org.yaml:snakeyaml:2.4")
implementation("io.opentelemetry:opentelemetry-sdk-common")
testImplementation(enforcedPlatform("org.junit:junit-bom:5.12.1"))
testImplementation("org.assertj:assertj-core:3.27.3")

View File

@ -2,6 +2,12 @@
Runs analysis on instrumentation modules in order to generate documentation.
## How to use
Run the doc generator:
`./gradlew :instrumentation-docs:generateDocs`
## Instrumentation Hierarchy
An "InstrumentationEntity" represents a module that that targets specific code in a framework/library/technology.
@ -46,10 +52,15 @@ public class SpringWebInstrumentationModule extends InstrumentationModule
* Configured in `InstrumentationModule` code for each module
* srcPath
* Path to the source code of the instrumentation module
* minimumJavaVersion
* Minimum Java version required by the instrumentation module. If not specified, it is assumed to
be Java 8
* description
* Short description of what the instrumentation does
* target_versions
* List of supported versions by the module, broken down by `library` or `javaagent` support
* scope
* Name: The scope name of the instrumentation, `io.opentelemetry.{instrumentation name}`
## Methodology
@ -62,12 +73,25 @@ As of now, the following fields are supported:
```yaml
description: "Description of what the instrumentation does."
# used to mark modules that do not instrument traditional libraries (e.g. methods, annotations)
# defaults to true
isLibraryInstrumentation: false
```
### Versions targeted
### Gradle File Derived Information
We parse gradle files in order to determine the target versions.
We parse gradle files in order to determine several pieces of metadata:
- Javaagent versions are determined by the `muzzle` plugin configurations
- Library versions are determined by the library dependency versions
- when available, latestDepTestLibrary is used to determine the latest supported version
- Minimum Java version is determined by the `otelJava` configurations
### Scope
For now, the scope name is the only value that is implemented in our instrumentations. The scope
name is determined by the instrumentation module name: `io.opentelemetry.{instrumentation name}`
We will implement gatherers for the schemaUrl and scope attributes when instrumentations start
implementing them.

View File

@ -5,6 +5,7 @@
package io.opentelemetry.instrumentation.docs;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.utils.FileManager;
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
import java.io.BufferedWriter;
@ -26,6 +27,10 @@ public class DocGeneratorApplication {
try (BufferedWriter writer =
Files.newBufferedWriter(
Paths.get("docs/instrumentation-list.yaml"), Charset.defaultCharset())) {
writer.write("# This file is generated and should not be manually edited.\n");
writer.write("# The structure and contents are a work in progress and subject to change.\n");
writer.write(
"# For more information see: https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/13468\n\n");
YamlHelper.printInstrumentationList(entities, writer);
} catch (IOException e) {
logger.severe("Error writing instrumentation list: " + e.getMessage());

View File

@ -5,8 +5,11 @@
package io.opentelemetry.instrumentation.docs;
import static io.opentelemetry.instrumentation.docs.GradleParser.parseGradleFile;
import static io.opentelemetry.instrumentation.docs.parsers.GradleParser.parseGradleFile;
import io.opentelemetry.instrumentation.docs.internal.DependencyInfo;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import io.opentelemetry.instrumentation.docs.utils.FileManager;
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
@ -42,11 +45,12 @@ class InstrumentationAnalyzer {
if (!entityMap.containsKey(key)) {
entityMap.put(
key,
new InstrumentationEntity(
path.srcPath().replace("/javaagent", "").replace("/library", ""),
path.instrumentationName(),
path.namespace(),
path.group()));
new InstrumentationEntity.Builder()
.srcPath(path.srcPath().replace("/javaagent", "").replace("/library", ""))
.instrumentationName(path.instrumentationName())
.namespace(path.namespace())
.group(path.group())
.build());
}
}
@ -80,15 +84,21 @@ class InstrumentationAnalyzer {
Map<InstrumentationType, Set<String>> versions = new HashMap<>();
for (String file : files) {
String fileContents = fileSearch.readFileToString(file);
DependencyInfo results = null;
if (file.contains("/javaagent/")) {
Set<String> results = parseGradleFile(fileContents, InstrumentationType.JAVAAGENT);
results = parseGradleFile(fileContents, InstrumentationType.JAVAAGENT);
versions
.computeIfAbsent(InstrumentationType.JAVAAGENT, k -> new HashSet<>())
.addAll(results);
.addAll(results.versions());
} else if (file.contains("/library/")) {
Set<String> results = parseGradleFile(fileContents, InstrumentationType.LIBRARY);
versions.computeIfAbsent(InstrumentationType.LIBRARY, k -> new HashSet<>()).addAll(results);
results = parseGradleFile(fileContents, InstrumentationType.LIBRARY);
versions
.computeIfAbsent(InstrumentationType.LIBRARY, k -> new HashSet<>())
.addAll(results.versions());
}
if (results != null && results.minJavaVersionSupported() != null) {
entity.setMinJavaVersion(results.minJavaVersionSupported());
}
}
entity.setTargetVersions(versions);

View File

@ -1,74 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs;
import java.util.Map;
import java.util.Set;
public class InstrumentationEntity {
private final String srcPath;
private final String instrumentationName;
private final String namespace;
private final String group;
private InstrumentationMetaData metadata;
private Map<InstrumentationType, Set<String>> targetVersions;
public InstrumentationEntity(
String srcPath, String instrumentationName, String namespace, String group) {
this.srcPath = srcPath;
this.instrumentationName = instrumentationName;
this.namespace = namespace;
this.group = group;
}
public InstrumentationEntity(
String srcPath,
String instrumentationName,
String namespace,
String group,
Map<InstrumentationType, Set<String>> targetVersions,
InstrumentationMetaData metadata) {
this.srcPath = srcPath;
this.instrumentationName = instrumentationName;
this.namespace = namespace;
this.group = group;
this.targetVersions = targetVersions;
this.metadata = metadata;
}
public void setMetadata(InstrumentationMetaData metadata) {
this.metadata = metadata;
}
public InstrumentationMetaData getMetadata() {
return metadata;
}
public String getSrcPath() {
return srcPath;
}
public String getInstrumentationName() {
return instrumentationName;
}
public String getNamespace() {
return namespace;
}
public String getGroup() {
return group;
}
public Map<InstrumentationType, Set<String>> getTargetVersions() {
return targetVersions;
}
public void setTargetVersions(Map<InstrumentationType, Set<String>> targetVersions) {
this.targetVersions = targetVersions;
}
}

View File

@ -1,25 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs;
public class InstrumentationMetaData {
public InstrumentationMetaData() {}
public InstrumentationMetaData(String description) {
this.description = description;
}
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -0,0 +1,14 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.internal;
import java.util.Set;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public record DependencyInfo(Set<String> versions, Integer minJavaVersionSupported) {}

View File

@ -0,0 +1,159 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.internal;
import static java.util.Objects.requireNonNull;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public class InstrumentationEntity {
private final String srcPath;
private final String instrumentationName;
private final String namespace;
private final String group;
private final InstrumentationScopeInfo scopeInfo;
@Nullable private Map<InstrumentationType, Set<String>> targetVersions;
@Nullable private Integer minJavaVersion;
@Nullable private InstrumentationMetaData metadata;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public InstrumentationEntity(Builder builder) {
requireNonNull(builder.srcPath, "srcPath required");
requireNonNull(builder.instrumentationName, "instrumentationName required");
requireNonNull(builder.namespace, "namespace required");
requireNonNull(builder.group, "group required");
this.srcPath = builder.srcPath;
this.instrumentationName = builder.instrumentationName;
this.namespace = builder.namespace;
this.group = builder.group;
this.metadata = builder.metadata;
this.targetVersions = builder.targetVersions;
this.minJavaVersion = builder.minJavaVersion;
this.scopeInfo = InstrumentationScopeInfo.create("io.opentelemetry." + instrumentationName);
}
public String getSrcPath() {
return srcPath;
}
public String getInstrumentationName() {
return instrumentationName;
}
public String getNamespace() {
return namespace;
}
public String getGroup() {
return group;
}
public InstrumentationScopeInfo getScopeInfo() {
return scopeInfo;
}
@Nullable
public InstrumentationMetaData getMetadata() {
return metadata;
}
@Nullable
public Map<InstrumentationType, Set<String>> getTargetVersions() {
return targetVersions;
}
@Nullable
public Integer getMinJavaVersion() {
return minJavaVersion;
}
public void setTargetVersions(Map<InstrumentationType, Set<String>> targetVersions) {
this.targetVersions = targetVersions;
}
public void setMetadata(InstrumentationMetaData metadata) {
this.metadata = metadata;
}
public void setMinJavaVersion(Integer minJavaVersion) {
this.minJavaVersion = minJavaVersion;
}
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public static class Builder {
private String srcPath;
private String instrumentationName;
private String namespace;
private String group;
private Integer minJavaVersion;
private InstrumentationMetaData metadata;
private Map<InstrumentationType, Set<String>> targetVersions;
@CanIgnoreReturnValue
public Builder srcPath(String srcPath) {
this.srcPath = srcPath;
return this;
}
@CanIgnoreReturnValue
public Builder instrumentationName(String instrumentationName) {
this.instrumentationName = instrumentationName;
return this;
}
@CanIgnoreReturnValue
public Builder namespace(String namespace) {
this.namespace = namespace;
return this;
}
@CanIgnoreReturnValue
public Builder minJavaVersion(Integer minJavaVersion) {
this.minJavaVersion = minJavaVersion;
return this;
}
@CanIgnoreReturnValue
public Builder group(String group) {
this.group = group;
return this;
}
@CanIgnoreReturnValue
public Builder metadata(InstrumentationMetaData metadata) {
this.metadata = metadata;
return this;
}
@CanIgnoreReturnValue
public Builder targetVersions(Map<InstrumentationType, Set<String>> targetVersions) {
this.targetVersions = targetVersions;
return this;
}
public InstrumentationEntity build() {
return new InstrumentationEntity(this);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs.internal;
import javax.annotation.Nullable;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public class InstrumentationMetaData {
public InstrumentationMetaData() {}
public InstrumentationMetaData(String description) {
this.description = description;
this.isLibraryInstrumentation = true;
}
public InstrumentationMetaData(String description, Boolean isLibraryInstrumentation) {
this.isLibraryInstrumentation = isLibraryInstrumentation;
this.description = description;
}
@Nullable private String description;
private Boolean isLibraryInstrumentation;
@Nullable
public String getDescription() {
return description;
}
public Boolean getIsLibraryInstrumentation() {
if (isLibraryInstrumentation == null) {
return true;
}
return isLibraryInstrumentation;
}
public void setDescription(String description) {
this.description = description;
}
public void setIsLibraryInstrumentation(Boolean libraryInstrumentation) {
isLibraryInstrumentation = libraryInstrumentation;
}
}

View File

@ -3,10 +3,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs;
package io.opentelemetry.instrumentation.docs.internal;
import java.util.Locale;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public enum InstrumentationType {
JAVAAGENT,
LIBRARY;

View File

@ -3,16 +3,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs;
package io.opentelemetry.instrumentation.docs.parsers;
import io.opentelemetry.instrumentation.docs.internal.DependencyInfo;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class GradleParser {
public class GradleParser {
private static final Pattern variablePattern =
Pattern.compile("val\\s+(\\w+)\\s*=\\s*\"([^\"]+)\"");
@ -31,20 +35,32 @@ class GradleParser {
private static final Pattern latestDepTestLibraryPattern =
Pattern.compile("latestDepTestLibrary\\(\"([^\"]+:[^\"]+):([^\"]+)\"\\)");
private static final Pattern coreJdkPattern = Pattern.compile("coreJdk\\(\\)");
private static final Pattern ifBlockPattern =
Pattern.compile("if\\s*\\([^)]*\\)\\s*\\{.*?}", Pattern.DOTALL);
private static final Pattern otelJavaBlockPattern =
Pattern.compile("otelJava\\s*\\{.*?}", Pattern.DOTALL);
private static final Pattern minJavaVersionPattern =
Pattern.compile("minJavaVersionSupported\\.set\\(JavaVersion\\.VERSION_(\\d+)\\)");
/**
* Parses gradle files for muzzle and dependency information
*
* @param gradleFileContents Contents of a Gradle build file as a String
* @return A set of strings summarizing the group, module, and version ranges
*/
public static Set<String> parseGradleFile(String gradleFileContents, InstrumentationType type) {
Set<String> results = new HashSet<>();
public static DependencyInfo parseGradleFile(
String gradleFileContents, InstrumentationType type) {
DependencyInfo results;
Map<String, String> variables = extractVariables(gradleFileContents);
if (type.equals(InstrumentationType.JAVAAGENT)) {
results.addAll(parseMuzzle(gradleFileContents, variables));
results = parseMuzzle(gradleFileContents, variables);
} else {
results.addAll(parseLibraryDependencies(gradleFileContents, variables));
results = parseLibraryDependencies(gradleFileContents, variables);
}
return results;
@ -58,13 +74,24 @@ class GradleParser {
* @param variables Map of variable names to their values
* @return A set of strings summarizing the group, module, and version ranges
*/
private static Set<String> parseMuzzle(String gradleFileContents, Map<String, String> variables) {
private static DependencyInfo parseMuzzle(
String gradleFileContents, Map<String, String> variables) {
Set<String> results = new HashSet<>();
Matcher passBlockMatcher = muzzlePassBlockPattern.matcher(gradleFileContents);
Integer minJavaVersion = parseMinJavaVersion(gradleFileContents);
while (passBlockMatcher.find()) {
String passBlock = passBlockMatcher.group(1);
if (coreJdkPattern.matcher(passBlock).find()) {
if (minJavaVersion != null) {
results.add("Java " + minJavaVersion + "+");
} else {
results.add("Java 8+");
}
}
String group = extractValue(passBlock, "group\\.set\\(\"([^\"]+)\"\\)");
String module = extractValue(passBlock, "module\\.set\\(\"([^\"]+)\"\\)");
String versionRange = extractValue(passBlock, "versions\\.set\\(\"([^\"]+)\"\\)");
@ -74,7 +101,7 @@ class GradleParser {
results.add(summary);
}
}
return results;
return new DependencyInfo(results, minJavaVersion);
}
/**
@ -86,7 +113,7 @@ class GradleParser {
* @param variables Map of variable names to their values
* @return A set of strings summarizing the group, module, and versions
*/
private static Set<String> parseLibraryDependencies(
private static DependencyInfo parseLibraryDependencies(
String gradleFileContents, Map<String, String> variables) {
Map<String, String> versions = new HashMap<>();
@ -123,7 +150,45 @@ class GradleParser {
}
}
return results;
Integer minJavaVersion = parseMinJavaVersion(gradleFileContents);
return new DependencyInfo(results, minJavaVersion);
}
public static Integer parseMinJavaVersion(String gradleFileContents) {
List<int[]> excludedRanges = new ArrayList<>();
// Identify all if-block ranges so we can exclude them
Matcher ifBlockMatcher = ifBlockPattern.matcher(gradleFileContents);
while (ifBlockMatcher.find()) {
excludedRanges.add(new int[] {ifBlockMatcher.start(), ifBlockMatcher.end()});
}
Matcher otelJavaMatcher = otelJavaBlockPattern.matcher(gradleFileContents);
while (otelJavaMatcher.find()) {
int blockStart = otelJavaMatcher.start();
if (isInExcludedRange(blockStart, excludedRanges)) {
continue; // Skip blocks inside 'if' statements
}
String otelJavaBlock = otelJavaMatcher.group();
Matcher versionMatcher = minJavaVersionPattern.matcher(otelJavaBlock);
if (versionMatcher.find()) {
return Integer.parseInt(versionMatcher.group(1));
}
}
return null;
}
private static boolean isInExcludedRange(int position, List<int[]> ranges) {
for (int[] range : ranges) {
if (position >= range[0] && position <= range[1]) {
return true;
}
}
return false;
}
/**

View File

@ -5,7 +5,7 @@
package io.opentelemetry.instrumentation.docs.utils;
import io.opentelemetry.instrumentation.docs.InstrumentationType;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -29,7 +29,6 @@ public class FileManager {
try (Stream<Path> walk = Files.walk(rootPath)) {
return walk.filter(Files::isDirectory)
.filter(dir -> !dir.toString().contains("/build"))
.filter(dir -> isValidInstrumentationPath(dir.toString()))
.map(dir -> parseInstrumentationPath(dir.toString()))
.collect(Collectors.toList());
@ -77,7 +76,9 @@ public class FileManager {
if (filePath.contains("/test/")
|| filePath.contains("/testing")
|| filePath.contains("/build/")
|| filePath.contains("-common/")
|| filePath.contains("-common-")
|| filePath.contains("bootstrap/src")) {
return false;
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.instrumentation.docs.utils;
import io.opentelemetry.instrumentation.docs.InstrumentationType;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
public record InstrumentationPath(
String instrumentationName,

View File

@ -5,8 +5,8 @@
package io.opentelemetry.instrumentation.docs.utils;
import io.opentelemetry.instrumentation.docs.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.InstrumentationMetaData;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
import java.io.BufferedWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@ -16,7 +16,6 @@ import java.util.TreeMap;
import java.util.stream.Collectors;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
public class YamlHelper {
@ -24,6 +23,7 @@ public class YamlHelper {
List<InstrumentationEntity> list, BufferedWriter writer) {
Map<String, List<InstrumentationEntity>> groupedByGroup =
list.stream()
.filter(entity -> isLibraryInstrumentation(entity.getMetadata()))
.collect(
Collectors.groupingBy(
InstrumentationEntity::getGroup, TreeMap::new, Collectors.toList()));
@ -43,6 +43,13 @@ public class YamlHelper {
entityMap.put("srcPath", entity.getSrcPath());
if (entity.getMinJavaVersion() != null) {
entityMap.put("minimumJavaVersion", entity.getMinJavaVersion());
}
Map<String, Object> scopeMap = getScopeMap(entity);
entityMap.put("scope", scopeMap);
Map<String, Object> targetVersions = new TreeMap<>();
if (entity.getTargetVersions() != null && !entity.getTargetVersions().isEmpty()) {
entity
@ -52,6 +59,7 @@ public class YamlHelper {
targetVersions.put(type.toString(), new ArrayList<>(versions)));
}
entityMap.put("target_versions", targetVersions);
instrumentations.add(entityMap);
}
groupMap.put("instrumentations", instrumentations);
@ -60,12 +68,25 @@ public class YamlHelper {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Representer representer = new Representer(options);
representer.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(representer, options);
Yaml yaml = new Yaml(options);
yaml.dump(output, writer);
}
// We assume true unless explicitly overridden
private static Boolean isLibraryInstrumentation(InstrumentationMetaData metadata) {
if (metadata == null) {
return true;
}
return metadata.getIsLibraryInstrumentation();
}
private static Map<String, Object> getScopeMap(InstrumentationEntity entity) {
Map<String, Object> scopeMap = new LinkedHashMap<>();
scopeMap.put("name", entity.getScopeInfo().getName());
return scopeMap;
}
public static InstrumentationMetaData metaDataParser(String input) {
return new Yaml().loadAs(input, InstrumentationMetaData.class);
}

View File

@ -7,6 +7,8 @@ package io.opentelemetry.instrumentation.docs;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
import java.util.Arrays;
import java.util.List;
@ -50,6 +52,8 @@ class InstrumentationAnalyzerTest {
assertThat(log4jEntity.getNamespace()).isEqualTo("log4j");
assertThat(log4jEntity.getGroup()).isEqualTo("log4j");
assertThat(log4jEntity.getSrcPath()).isEqualTo("instrumentation/log4j/log4j-appender-2.17");
assertThat(log4jEntity.getScopeInfo().getName())
.isEqualTo("io.opentelemetry.log4j-appender-2.17");
InstrumentationEntity springEntity =
entities.stream()
@ -61,5 +65,6 @@ class InstrumentationAnalyzerTest {
assertThat(springEntity.getNamespace()).isEqualTo("spring");
assertThat(springEntity.getGroup()).isEqualTo("spring");
assertThat(springEntity.getSrcPath()).isEqualTo("instrumentation/spring/spring-web");
assertThat(springEntity.getScopeInfo().getName()).isEqualTo("io.opentelemetry.spring-web");
}
}

View File

@ -3,11 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.docs;
package io.opentelemetry.instrumentation.docs.parsers;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Set;
import io.opentelemetry.instrumentation.docs.internal.DependencyInfo;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import org.junit.jupiter.api.Test;
class GradleParserTest {
@ -22,10 +23,10 @@ class GradleParserTest {
+ " versions.set(\"[5.0,6.4)\")\n"
+ " }\n"
+ "}";
Set<String> versions =
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.JAVAAGENT);
assertThat(versions.size()).isEqualTo(1);
assertThat(versions.stream().findFirst().get())
assertThat(info.versions().size()).isEqualTo(1);
assertThat(info.versions().stream().findFirst().get())
.isEqualTo("org.elasticsearch.client:rest:[5.0,6.4)");
}
@ -33,10 +34,10 @@ class GradleParserTest {
void testExtractLibraryVersion() {
String gradleBuildFileContent =
"dependencies {\n" + " library(\"org.apache.httpcomponents:httpclient:4.3\")\n" + "}";
Set<String> versions =
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.LIBRARY);
assertThat(versions.size()).isEqualTo(1);
assertThat(versions.stream().findFirst().get())
assertThat(info.versions().size()).isEqualTo(1);
assertThat(info.versions().stream().findFirst().get())
.isEqualTo("org.apache.httpcomponents:httpclient:4.3");
}
@ -49,13 +50,75 @@ class GradleParserTest {
+ " latestDepTestLibrary(\"org.apache.httpcomponents:httpclient:4.+\") // see apache-httpclient-5.0 module\n"
+ "}";
Set<String> versions =
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.LIBRARY);
assertThat(versions.size()).isEqualTo(1);
assertThat(versions.stream().findFirst().get())
assertThat(info.versions().size()).isEqualTo(1);
assertThat(info.versions().stream().findFirst().get())
.isEqualTo("org.apache.httpcomponents:httpclient:[4.3,4.+)");
}
@Test
void testExtractCoreJdk() {
String gradleBuildFileContent =
"""
muzzle {
pass {
coreJdk()
}
}
""";
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.JAVAAGENT);
assertThat(info.versions().size()).isEqualTo(1);
assertThat(info.versions().stream().findFirst().get()).isEqualTo("Java 8+");
}
@Test
void testExtractMinimumJavaVersion() {
String gradleBuildFileContent =
"""
muzzle {
pass {
coreJdk()
}
}
otelJava {
minJavaVersionSupported.set(JavaVersion.VERSION_11)
}
""";
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.JAVAAGENT);
assertThat(info.versions().size()).isEqualTo(1);
assertThat(info.minJavaVersionSupported()).isEqualTo(11);
assertThat(info.versions().stream().findFirst().get()).isEqualTo("Java 11+");
}
@Test
void testExtractMinimumJavaVersionIgnoredWithinIfCondition() {
String gradleBuildFileContent =
"""
muzzle {
pass {
coreJdk()
}
}
if (latestDepTest) {
otelJava {
minJavaVersionSupported.set(JavaVersion.VERSION_11)
}
}
""";
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.JAVAAGENT);
assertThat(info.versions().size()).isEqualTo(1);
assertThat(info.versions().stream().findFirst().get()).isEqualTo("Java 8+");
}
@Test
void testExtractMuzzleVersions_MultiplePassBlocks() {
String gradleBuildFileContent =
@ -89,9 +152,9 @@ class GradleParserTest {
+ " }\n"
+ "}\n";
Set<String> versions =
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.JAVAAGENT);
assertThat(versions)
assertThat(info.versions())
.containsExactlyInAnyOrder(
"dev.zio:zio_2.12:[2.0.0,)", "dev.zio:zio_2.13:[2.0.0,)", "dev.zio:zio_3:[2.0.0,)");
}
@ -117,9 +180,9 @@ class GradleParserTest {
+ " }\n"
+ "}\n";
Set<String> versions =
DependencyInfo info =
GradleParser.parseGradleFile(gradleBuildFileContent, InstrumentationType.LIBRARY);
assertThat(versions)
assertThat(info.versions())
.containsExactlyInAnyOrder(
"ch.qos.logback:logback-classic:1.3.0",
"org.slf4j:slf4j-api:2.0.0",

View File

@ -44,6 +44,14 @@ class FileManagerTest {
assertThat(FileManager.isValidInstrumentationPath("/instrumentation/test/javaagent")).isFalse();
}
@Test
void testExcludesCommonModules() {
assertThat(
FileManager.isValidInstrumentationPath(
"instrumentation/elasticsearch/elasticsearch-rest-common-5.0"))
.isFalse();
}
@Test
void testFindBuildGradleFiles() throws IOException {
Path gradleFile = Files.createFile(tempDir.resolve("build.gradle.kts"));

View File

@ -7,9 +7,9 @@ package io.opentelemetry.instrumentation.docs.utils;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.instrumentation.docs.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.InstrumentationMetaData;
import io.opentelemetry.instrumentation.docs.InstrumentationType;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationEntity;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
import java.io.BufferedWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@ -33,26 +33,29 @@ class YamlHelperTest {
new InstrumentationMetaData("Spring Web 6.0 instrumentation");
entities.add(
new InstrumentationEntity(
"instrumentation/spring/spring-web/spring-web-6.0",
"spring-web-6.0",
"spring",
"spring",
targetVersions1,
metadata1));
new InstrumentationEntity.Builder()
.srcPath("instrumentation/spring/spring-web/spring-web-6.0")
.instrumentationName("spring-web-6.0")
.namespace("spring")
.group("spring")
.targetVersions(targetVersions1)
.metadata(metadata1)
.minJavaVersion(11)
.build());
Map<InstrumentationType, Set<String>> targetVersions2 = new HashMap<>();
targetVersions2.put(
InstrumentationType.LIBRARY,
new HashSet<>(List.of("org.apache.struts:struts2-core:2.1.0")));
entities.add(
new InstrumentationEntity(
"instrumentation/struts/struts-2.3",
"struts-2.3",
"struts",
"struts",
targetVersions2,
null));
new InstrumentationEntity.Builder()
.srcPath("instrumentation/struts/struts-2.3")
.instrumentationName("struts-2.3")
.namespace("struts")
.group("struts")
.targetVersions(targetVersions2)
.build());
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
@ -61,22 +64,115 @@ class YamlHelperTest {
writer.flush();
String expectedYaml =
"spring:\n"
+ " instrumentations:\n"
+ " - name: spring-web-6.0\n"
+ " description: Spring Web 6.0 instrumentation\n"
+ " srcPath: instrumentation/spring/spring-web/spring-web-6.0\n"
+ " target_versions:\n"
+ " javaagent:\n"
+ " - org.springframework:spring-web:[6.0.0,)\n"
+ "struts:\n"
+ " instrumentations:\n"
+ " - name: struts-2.3\n"
+ " srcPath: instrumentation/struts/struts-2.3\n"
+ " target_versions:\n"
+ " library:\n"
+ " - org.apache.struts:struts2-core:2.1.0\n";
"""
spring:
instrumentations:
- name: spring-web-6.0
description: Spring Web 6.0 instrumentation
srcPath: instrumentation/spring/spring-web/spring-web-6.0
minimumJavaVersion: 11
scope:
name: io.opentelemetry.spring-web-6.0
target_versions:
javaagent:
- org.springframework:spring-web:[6.0.0,)
struts:
instrumentations:
- name: struts-2.3
srcPath: instrumentation/struts/struts-2.3
scope:
name: io.opentelemetry.struts-2.3
target_versions:
library:
- org.apache.struts:struts2-core:2.1.0
""";
assertThat(expectedYaml).isEqualTo(stringWriter.toString());
}
@Test
void testPrintInstrumentationListIgnoresNonLibraryInstrumentation() throws Exception {
List<InstrumentationEntity> entities = new ArrayList<>();
Map<InstrumentationType, Set<String>> targetVersions1 = new HashMap<>();
targetVersions1.put(
InstrumentationType.JAVAAGENT,
new HashSet<>(List.of("org.springframework:spring-web:[6.0.0,)")));
InstrumentationMetaData metadata1 =
new InstrumentationMetaData("Spring Web 6.0 instrumentation");
entities.add(
new InstrumentationEntity.Builder()
.srcPath("instrumentation/spring/spring-web/spring-web-6.0")
.instrumentationName("spring-web-6.0")
.namespace("spring")
.group("spring")
.targetVersions(targetVersions1)
.metadata(metadata1)
.minJavaVersion(11)
.build());
InstrumentationMetaData metadata2 = new InstrumentationMetaData(null, false);
entities.add(
new InstrumentationEntity.Builder()
.srcPath("instrumentation/internal/internal-application-logger")
.instrumentationName("internal-application-logger")
.namespace("internal")
.group("internal")
.metadata(metadata2)
.targetVersions(new HashMap<>())
.build());
StringWriter stringWriter = new StringWriter();
BufferedWriter writer = new BufferedWriter(stringWriter);
YamlHelper.printInstrumentationList(entities, writer);
writer.flush();
String expectedYaml =
"""
spring:
instrumentations:
- name: spring-web-6.0
description: Spring Web 6.0 instrumentation
srcPath: instrumentation/spring/spring-web/spring-web-6.0
minimumJavaVersion: 11
scope:
name: io.opentelemetry.spring-web-6.0
target_versions:
javaagent:
- org.springframework:spring-web:[6.0.0,)
""";
assertThat(expectedYaml).isEqualTo(stringWriter.toString());
}
@Test
void testMetadataParser() {
String input =
"""
description: test description
isLibraryInstrumentation: false
""";
InstrumentationMetaData metadata = YamlHelper.metaDataParser(input);
assertThat(metadata.getIsLibraryInstrumentation()).isFalse();
assertThat(metadata.getDescription()).isEqualTo("test description");
}
@Test
void testMetadataParserWithOnlyLibraryEntry() {
String input = "isLibraryInstrumentation: false";
InstrumentationMetaData metadata = YamlHelper.metaDataParser(input);
assertThat(metadata.getIsLibraryInstrumentation()).isFalse();
assertThat(metadata.getDescription()).isNull();
}
@Test
void testMetadataParserWithOnlyDescription() {
String input = "description: false";
InstrumentationMetaData metadata = YamlHelper.metaDataParser(input);
assertThat(metadata.getIsLibraryInstrumentation()).isTrue();
}
}

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false

View File

@ -0,0 +1 @@
isLibraryInstrumentation: false