Skip to content

Commit 6521fc8

Browse files
authored
Introduce instrumentation classifications to metadata (#13672)
1 parent 377781c commit 6521fc8

File tree

35 files changed

+447
-318
lines changed

35 files changed

+447
-318
lines changed

docs/instrumentation-list.yaml

Lines changed: 201 additions & 188 deletions
Large diffs are not rendered by default.

instrumentation-docs/readme.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ Run the doc generator:
1010

1111
## Instrumentation Hierarchy
1212

13-
An "InstrumentationEntity" represents a module that that targets specific code in a framework/library/technology.
14-
Each instrumentation uses muzzle to determine which versions of the target code it supports.
13+
An "InstrumentationEntity" represents a module that that targets specific code in a
14+
framework/library/technology. Each entity will have a name, a namespace, and a group.
1515

1616
Using these structures as examples:
1717

@@ -28,8 +28,10 @@ Using these structures as examples:
2828
│ │ │ └── spring-cloud-gateway-common
2929
```
3030

31-
* Name
32-
* Ex: `clickhouse-client-05`, `jaxrs-1.0`, `spring-cloud-gateway-2.0`
31+
Results in the following:
32+
33+
* Name - the full name of the instrumentation module
34+
* `clickhouse-client-05`, `jaxrs-1.0`, `spring-cloud-gateway-2.0`
3335
* Namespace - direct parent. if none, use name and strip version
3436
* `clickhouse-client`, `jaxrs`, `spring-cloud-gateway`
3537
* Group - top most parent
@@ -47,6 +49,10 @@ public class SpringWebInstrumentationModule extends InstrumentationModule
4749

4850
## Instrumentation metadata
4951

52+
* classification
53+
* `library` - Instrumentation that targets a library
54+
* `internal` - Instrumentation that is used internally by the OpenTelemetry Java Agent
55+
* `custom` - Utilities that are used to create custom instrumentation
5056
* name
5157
* Identifier for instrumentation module, used to enable/disable
5258
* Configured in `InstrumentationModule` code for each module
@@ -69,15 +75,12 @@ public class SpringWebInstrumentationModule extends InstrumentationModule
6975
Within each instrumentation source directory, a `metadata.yaml` file can be created to provide
7076
additional information about the instrumentation module.
7177

72-
As of now, the following fields are supported:
78+
As of now, the following fields are supported, all of which are optional:
7379

7480
```yaml
75-
description: "Description of what the instrumentation does."
76-
disabled_by_default: true
77-
78-
# used to mark modules that do not instrument traditional libraries (e.g. methods, annotations)
79-
# defaults to true
80-
isLibraryInstrumentation: false
81+
description: "Instruments..." # Description of the instrumentation module
82+
disabled_by_default: true # Defaults to `false`
83+
classification: internal # instrumentation classification: library | internal | custom
8184
```
8285

8386
### Gradle File Derived Information

instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/DocGeneratorApplication.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static void main(String[] args) {
3131
writer.write("# The structure and contents are a work in progress and subject to change.\n");
3232
writer.write(
3333
"# For more information see: https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/13468\n\n");
34-
YamlHelper.printInstrumentationList(entities, writer);
34+
YamlHelper.generateInstrumentationYaml(entities, writer);
3535
} catch (IOException e) {
3636
logger.severe("Error writing instrumentation list: " + e.getMessage());
3737
}

instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/InstrumentationAnalyzer.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,17 @@
2222

2323
class InstrumentationAnalyzer {
2424

25-
private final FileManager fileSearch;
25+
private final FileManager fileManager;
2626

27-
InstrumentationAnalyzer(FileManager fileSearch) {
28-
this.fileSearch = fileSearch;
27+
InstrumentationAnalyzer(FileManager fileManager) {
28+
this.fileManager = fileManager;
2929
}
3030

3131
/**
32-
* Converts a list of InstrumentationPath objects into a list of InstrumentationEntity objects.
33-
* Each InstrumentationEntity represents a unique combination of group, namespace, and
34-
* instrumentation name. The types of instrumentation (e.g., library, javaagent) are aggregated
35-
* into a list within each entity.
32+
* Converts a list of {@link InstrumentationPath} into a list of {@link InstrumentationEntity},
3633
*
37-
* @param paths the list of InstrumentationPath objects to be converted
38-
* @return a list of InstrumentationEntity objects with aggregated types
34+
* @param paths the list of {@link InstrumentationPath} objects to be converted
35+
* @return a list of {@link InstrumentationEntity} objects with aggregated types
3936
*/
4037
public static List<InstrumentationEntity> convertToEntities(List<InstrumentationPath> paths) {
4138
Map<String, InstrumentationEntity> entityMap = new HashMap<>();
@@ -62,17 +59,17 @@ public static List<InstrumentationEntity> convertToEntities(List<Instrumentation
6259
* Extracts version information from each instrumentation's build.gradle file. Extracts
6360
* information from metadata.yaml files.
6461
*
65-
* @return a list of InstrumentationEntity objects with target versions
62+
* @return a list of {@link InstrumentationEntity}
6663
*/
6764
List<InstrumentationEntity> analyze() {
68-
List<InstrumentationPath> paths = fileSearch.getInstrumentationPaths();
65+
List<InstrumentationPath> paths = fileManager.getInstrumentationPaths();
6966
List<InstrumentationEntity> entities = convertToEntities(paths);
7067

7168
for (InstrumentationEntity entity : entities) {
72-
List<String> gradleFiles = fileSearch.findBuildGradleFiles(entity.getSrcPath());
69+
List<String> gradleFiles = fileManager.findBuildGradleFiles(entity.getSrcPath());
7370
analyzeVersions(gradleFiles, entity);
7471

75-
String metadataFile = fileSearch.getMetaDataFile(entity.getSrcPath());
72+
String metadataFile = fileManager.getMetaDataFile(entity.getSrcPath());
7673
if (metadataFile != null) {
7774
entity.setMetadata(YamlHelper.metaDataParser(metadataFile));
7875
}
@@ -83,7 +80,7 @@ List<InstrumentationEntity> analyze() {
8380
void analyzeVersions(List<String> files, InstrumentationEntity entity) {
8481
Map<InstrumentationType, Set<String>> versions = new HashMap<>();
8582
for (String file : files) {
86-
String fileContents = fileSearch.readFileToString(file);
83+
String fileContents = fileManager.readFileToString(file);
8784
DependencyInfo results = null;
8885

8986
if (file.contains("/javaagent/")) {
Lines changed: 37 additions & 0 deletions
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.docs.internal;
7+
8+
import java.util.Locale;
9+
import javax.annotation.Nullable;
10+
11+
/**
12+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
13+
* any time.
14+
*/
15+
public enum InstrumentationClassification {
16+
LIBRARY,
17+
CUSTOM,
18+
INTERNAL;
19+
20+
@Nullable
21+
public static InstrumentationClassification fromString(@Nullable String type) {
22+
if (type == null) {
23+
return null;
24+
}
25+
return switch (type.toLowerCase(Locale.getDefault())) {
26+
case "library" -> LIBRARY;
27+
case "internal" -> INTERNAL;
28+
case "custom" -> CUSTOM;
29+
default -> null;
30+
};
31+
}
32+
33+
@Override
34+
public String toString() {
35+
return name().toLowerCase(Locale.getDefault());
36+
}
37+
}

instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationEntity.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ public InstrumentationScopeInfo getScopeInfo() {
7070
return scopeInfo;
7171
}
7272

73-
@Nullable
7473
public InstrumentationMetaData getMetadata() {
74+
if (metadata == null) {
75+
metadata = new InstrumentationMetaData();
76+
}
77+
7578
return metadata;
7679
}
7780

instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/internal/InstrumentationMetaData.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.instrumentation.docs.internal;
77

88
import java.util.Objects;
9+
import javax.annotation.Nonnull;
910
import javax.annotation.Nullable;
1011

1112
/**
@@ -14,18 +15,14 @@
1415
*/
1516
public class InstrumentationMetaData {
1617
@Nullable private String description;
17-
@Nullable private Boolean isLibraryInstrumentation;
1818
@Nullable private Boolean disabledByDefault;
19+
private String classification;
1920

2021
public InstrumentationMetaData() {}
2122

22-
public InstrumentationMetaData(String description) {
23-
this.description = description;
24-
}
25-
2623
public InstrumentationMetaData(
27-
String description, Boolean isLibraryInstrumentation, Boolean disabledByDefault) {
28-
this.isLibraryInstrumentation = isLibraryInstrumentation;
24+
String description, String classification, Boolean disabledByDefault) {
25+
this.classification = classification;
2926
this.disabledByDefault = disabledByDefault;
3027
this.description = description;
3128
}
@@ -35,8 +32,11 @@ public String getDescription() {
3532
return description;
3633
}
3734

38-
public Boolean getIsLibraryInstrumentation() {
39-
return Objects.requireNonNullElse(isLibraryInstrumentation, true);
35+
@Nonnull
36+
public InstrumentationClassification getClassification() {
37+
return Objects.requireNonNullElse(
38+
InstrumentationClassification.fromString(classification),
39+
InstrumentationClassification.LIBRARY);
4040
}
4141

4242
public Boolean getDisabledByDefault() {
@@ -47,8 +47,8 @@ public void setDescription(@Nullable String description) {
4747
this.description = description;
4848
}
4949

50-
public void setIsLibraryInstrumentation(@Nullable Boolean libraryInstrumentation) {
51-
isLibraryInstrumentation = libraryInstrumentation;
50+
public void setClassification(@Nullable String classification) {
51+
this.classification = classification;
5252
}
5353

5454
public void setDisabledByDefault(@Nullable Boolean disabledByDefault) {

instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/utils/YamlHelper.java

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.instrumentation.docs.utils;
77

8+
import io.opentelemetry.instrumentation.docs.internal.InstrumentationClassification;
89
import io.opentelemetry.instrumentation.docs.internal.InstrumentationEntity;
910
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
1011
import java.io.BufferedWriter;
@@ -26,45 +27,53 @@ public class YamlHelper {
2627
TypeDescription customDescriptor = new TypeDescription(InstrumentationMetaData.class);
2728
customDescriptor.substituteProperty(
2829
"disabled_by_default", Boolean.class, "getDisabledByDefault", "setDisabledByDefault");
30+
customDescriptor.substituteProperty(
31+
"classification", String.class, "getClassification", "setClassification");
2932
metaDataYaml.addTypeDescription(customDescriptor);
3033
}
3134

32-
public static void printInstrumentationList(
35+
public static void generateInstrumentationYaml(
3336
List<InstrumentationEntity> list, BufferedWriter writer) {
34-
Map<String, List<InstrumentationEntity>> groupedByGroup =
37+
DumperOptions options = new DumperOptions();
38+
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
39+
40+
Yaml yaml = new Yaml(options);
41+
42+
Map<String, Object> libraries = getLibraryInstrumentations(list);
43+
if (!libraries.isEmpty()) {
44+
yaml.dump(getLibraryInstrumentations(list), writer);
45+
}
46+
47+
Map<String, Object> internal = generateBaseYaml(list, InstrumentationClassification.INTERNAL);
48+
if (!internal.isEmpty()) {
49+
yaml.dump(internal, writer);
50+
}
51+
52+
Map<String, Object> custom = generateBaseYaml(list, InstrumentationClassification.CUSTOM);
53+
if (!custom.isEmpty()) {
54+
yaml.dump(custom, writer);
55+
}
56+
}
57+
58+
private static Map<String, Object> getLibraryInstrumentations(List<InstrumentationEntity> list) {
59+
Map<String, List<InstrumentationEntity>> libraryInstrumentations =
3560
list.stream()
36-
.filter(entity -> isLibraryInstrumentation(entity.getMetadata()))
61+
.filter(
62+
entity ->
63+
entity
64+
.getMetadata()
65+
.getClassification()
66+
.equals(InstrumentationClassification.LIBRARY))
3767
.collect(
3868
Collectors.groupingBy(
3969
InstrumentationEntity::getGroup, TreeMap::new, Collectors.toList()));
4070

4171
Map<String, Object> output = new TreeMap<>();
42-
groupedByGroup.forEach(
72+
libraryInstrumentations.forEach(
4373
(group, entities) -> {
44-
Map<String, Object> groupMap = new LinkedHashMap<>();
4574
List<Map<String, Object>> instrumentations = new ArrayList<>();
4675
for (InstrumentationEntity entity : entities) {
47-
Map<String, Object> entityMap = new LinkedHashMap<>();
48-
entityMap.put("name", entity.getInstrumentationName());
49-
50-
if (entity.getMetadata() != null) {
51-
if (entity.getMetadata().getDescription() != null) {
52-
entityMap.put("description", entity.getMetadata().getDescription());
53-
}
54-
55-
if (entity.getMetadata().getDisabledByDefault()) {
56-
entityMap.put("disabled_by_default", entity.getMetadata().getDisabledByDefault());
57-
}
58-
}
59-
60-
entityMap.put("source_path", entity.getSrcPath());
61-
62-
if (entity.getMinJavaVersion() != null) {
63-
entityMap.put("minimum_java_version", entity.getMinJavaVersion());
64-
}
65-
66-
Map<String, Object> scopeMap = getScopeMap(entity);
67-
entityMap.put("scope", scopeMap);
76+
Map<String, Object> entityMap = baseProperties(entity);
6877

6978
Map<String, Object> targetVersions = new TreeMap<>();
7079
if (entity.getTargetVersions() != null && !entity.getTargetVersions().isEmpty()) {
@@ -81,23 +90,60 @@ public static void printInstrumentationList(
8190

8291
instrumentations.add(entityMap);
8392
}
84-
groupMap.put("instrumentations", instrumentations);
85-
output.put(group, groupMap);
93+
output.put(group, instrumentations);
8694
});
8795

88-
DumperOptions options = new DumperOptions();
89-
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
96+
Map<String, Object> newOutput = new TreeMap<>();
97+
if (output.isEmpty()) {
98+
return newOutput;
99+
}
100+
newOutput.put("libraries", output);
101+
return newOutput;
102+
}
90103

91-
Yaml yaml = new Yaml(options);
92-
yaml.dump(output, writer);
104+
private static Map<String, Object> generateBaseYaml(
105+
List<InstrumentationEntity> list, InstrumentationClassification classification) {
106+
List<InstrumentationEntity> filtered =
107+
list.stream()
108+
.filter(entity -> entity.getMetadata().getClassification().equals(classification))
109+
.toList();
110+
111+
List<Map<String, Object>> instrumentations = new ArrayList<>();
112+
for (InstrumentationEntity entity : filtered) {
113+
instrumentations.add(baseProperties(entity));
114+
}
115+
116+
Map<String, Object> newOutput = new TreeMap<>();
117+
if (instrumentations.isEmpty()) {
118+
return newOutput;
119+
}
120+
newOutput.put(classification.toString(), instrumentations);
121+
return newOutput;
93122
}
94123

95-
// We assume true unless explicitly overridden
96-
private static Boolean isLibraryInstrumentation(InstrumentationMetaData metadata) {
97-
if (metadata == null) {
98-
return true;
124+
private static Map<String, Object> baseProperties(InstrumentationEntity entity) {
125+
Map<String, Object> entityMap = new LinkedHashMap<>();
126+
entityMap.put("name", entity.getInstrumentationName());
127+
128+
if (entity.getMetadata() != null) {
129+
if (entity.getMetadata().getDescription() != null) {
130+
entityMap.put("description", entity.getMetadata().getDescription());
131+
}
132+
133+
if (entity.getMetadata().getDisabledByDefault()) {
134+
entityMap.put("disabled_by_default", entity.getMetadata().getDisabledByDefault());
135+
}
99136
}
100-
return metadata.getIsLibraryInstrumentation();
137+
138+
entityMap.put("source_path", entity.getSrcPath());
139+
140+
if (entity.getMinJavaVersion() != null) {
141+
entityMap.put("minimum_java_version", entity.getMinJavaVersion());
142+
}
143+
144+
Map<String, Object> scopeMap = getScopeMap(entity);
145+
entityMap.put("scope", scopeMap);
146+
return entityMap;
101147
}
102148

103149
private static Map<String, Object> getScopeMap(InstrumentationEntity entity) {

0 commit comments

Comments
 (0)