Skip to content

Commit 7689228

Browse files
authored
Filter metrics by scope (#14136)
1 parent 236f2fb commit 7689228

File tree

12 files changed

+582
-196
lines changed

12 files changed

+582
-196
lines changed

docs/instrumentation-list.yaml

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2836,24 +2836,6 @@ libraries:
28362836
target_versions:
28372837
javaagent:
28382838
- io.projectreactor.netty:reactor-netty:[0.8.2.RELEASE,1.0.0)
2839-
telemetry:
2840-
- when: default
2841-
metrics:
2842-
- name: http.client.request.duration
2843-
description: Duration of HTTP client requests.
2844-
type: HISTOGRAM
2845-
unit: s
2846-
attributes:
2847-
- name: http.request.method
2848-
type: STRING
2849-
- name: http.response.status_code
2850-
type: LONG
2851-
- name: network.protocol.version
2852-
type: STRING
2853-
- name: server.address
2854-
type: STRING
2855-
- name: server.port
2856-
type: LONG
28572839
- name: reactor-netty-1.0
28582840
source_path: instrumentation/reactor/reactor-netty/reactor-netty-1.0
28592841
scope:
@@ -3214,21 +3196,6 @@ libraries:
32143196
type: STRING
32153197
- name: server.port
32163198
type: LONG
3217-
- name: http.server.request.duration
3218-
description: Duration of HTTP server requests.
3219-
type: HISTOGRAM
3220-
unit: s
3221-
attributes:
3222-
- name: http.request.method
3223-
type: STRING
3224-
- name: http.response.status_code
3225-
type: LONG
3226-
- name: http.route
3227-
type: STRING
3228-
- name: network.protocol.version
3229-
type: STRING
3230-
- name: url.scheme
3231-
type: STRING
32323199
- name: spring-webflux-5.3
32333200
source_path: instrumentation/spring/spring-webflux/spring-webflux-5.3
32343201
scope:

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

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,17 @@
77

88
import com.fasterxml.jackson.core.JsonProcessingException;
99
import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
10-
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
1110
import io.opentelemetry.instrumentation.docs.internal.InstrumentationMetaData;
1211
import io.opentelemetry.instrumentation.docs.internal.InstrumentationModule;
1312
import io.opentelemetry.instrumentation.docs.internal.InstrumentationType;
14-
import io.opentelemetry.instrumentation.docs.parsers.EmittedMetricsParser;
1513
import io.opentelemetry.instrumentation.docs.parsers.GradleParser;
14+
import io.opentelemetry.instrumentation.docs.parsers.MetricParser;
1615
import io.opentelemetry.instrumentation.docs.parsers.ModuleParser;
1716
import io.opentelemetry.instrumentation.docs.parsers.SpanParser;
1817
import io.opentelemetry.instrumentation.docs.utils.FileManager;
1918
import io.opentelemetry.instrumentation.docs.utils.InstrumentationPath;
2019
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
2120
import java.io.IOException;
22-
import java.util.HashMap;
2321
import java.util.List;
2422
import java.util.Map;
2523
import java.util.Set;
@@ -65,7 +63,7 @@ private void enrichModule(InstrumentationModule module) throws IOException {
6563
}
6664

6765
module.setTargetVersions(getVersionInformation(module));
68-
module.setMetrics(MetricsProcessor.getMetrics(module, fileManager));
66+
module.setMetrics(MetricParser.getMetrics(module, fileManager));
6967
module.setSpans(SpanParser.getSpans(module, fileManager));
7068
}
7169

@@ -89,26 +87,4 @@ private Map<InstrumentationType, Set<String>> getVersionInformation(
8987
List<String> gradleFiles = fileManager.findBuildGradleFiles(module.getSrcPath());
9088
return GradleParser.extractVersions(gradleFiles, module);
9189
}
92-
93-
/** Handles processing of metrics data for instrumentation modules. */
94-
static class MetricsProcessor {
95-
96-
public static Map<String, List<EmittedMetrics.Metric>> getMetrics(
97-
InstrumentationModule module, FileManager fileManager) {
98-
Map<String, EmittedMetrics> metrics =
99-
EmittedMetricsParser.getMetricsFromFiles(fileManager.rootDir(), module.getSrcPath());
100-
101-
Map<String, List<EmittedMetrics.Metric>> result = new HashMap<>();
102-
metrics.entrySet().stream()
103-
.filter(MetricsProcessor::hasValidMetrics)
104-
.forEach(entry -> result.put(entry.getKey(), entry.getValue().getMetrics()));
105-
return result;
106-
}
107-
108-
private static boolean hasValidMetrics(Map.Entry<String, EmittedMetrics> entry) {
109-
return entry.getValue() != null && entry.getValue().getMetrics() != null;
110-
}
111-
112-
private MetricsProcessor() {}
113-
}
11490
}

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

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

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

8+
import static java.util.Collections.emptyList;
9+
10+
import com.fasterxml.jackson.annotation.JsonProperty;
811
import java.util.ArrayList;
912
import java.util.List;
1013

@@ -16,16 +19,18 @@
1619
public class EmittedMetrics {
1720
// Condition in which the metrics are emitted (ex: default, or configuration option names).
1821
private String when;
19-
private List<Metric> metrics;
22+
23+
@JsonProperty("metrics_by_scope")
24+
private List<MetricsByScope> metricsByScope;
2025

2126
public EmittedMetrics() {
2227
this.when = "";
23-
this.metrics = new ArrayList<>();
28+
this.metricsByScope = emptyList();
2429
}
2530

26-
public EmittedMetrics(String when, List<Metric> metrics) {
27-
this.when = "";
28-
this.metrics = metrics;
31+
public EmittedMetrics(String when, List<MetricsByScope> metricsByScope) {
32+
this.when = when;
33+
this.metricsByScope = metricsByScope;
2934
}
3035

3136
public String getWhen() {
@@ -36,12 +41,49 @@ public void setWhen(String when) {
3641
this.when = when;
3742
}
3843

39-
public List<Metric> getMetrics() {
40-
return metrics;
44+
@JsonProperty("metrics_by_scope")
45+
public List<MetricsByScope> getMetricsByScope() {
46+
return metricsByScope;
47+
}
48+
49+
@JsonProperty("metrics_by_scope")
50+
public void setMetricsByScope(List<MetricsByScope> metricsByScope) {
51+
this.metricsByScope = metricsByScope;
4152
}
4253

43-
public void setMetrics(List<Metric> metrics) {
44-
this.metrics = metrics;
54+
/**
55+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
56+
* any time.
57+
*/
58+
public static class MetricsByScope {
59+
private String scope;
60+
private List<Metric> metrics;
61+
62+
public MetricsByScope(String scope, List<Metric> metrics) {
63+
this.scope = scope;
64+
this.metrics = metrics;
65+
}
66+
67+
public MetricsByScope() {
68+
this.scope = "";
69+
this.metrics = new ArrayList<>();
70+
}
71+
72+
public String getScope() {
73+
return scope;
74+
}
75+
76+
public void setScope(String scope) {
77+
this.scope = scope;
78+
}
79+
80+
public List<Metric> getMetrics() {
81+
return metrics;
82+
}
83+
84+
public void setMetrics(List<Metric> metrics) {
85+
this.metrics = metrics;
86+
}
4587
}
4688

4789
/**

instrumentation-docs/src/main/java/io/opentelemetry/instrumentation/docs/parsers/EmittedMetricsParser.java

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

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

8+
import com.fasterxml.jackson.core.JsonProcessingException;
89
import io.opentelemetry.instrumentation.docs.internal.EmittedMetrics;
910
import io.opentelemetry.instrumentation.docs.utils.FileManager;
1011
import io.opentelemetry.instrumentation.docs.utils.YamlHelper;
@@ -35,9 +36,24 @@ public class EmittedMetricsParser {
3536
*/
3637
public static Map<String, EmittedMetrics> getMetricsFromFiles(
3738
String rootDir, String instrumentationDirectory) {
38-
Map<String, StringBuilder> metricsByWhen = new HashMap<>();
3939
Path telemetryDir = Paths.get(rootDir + "/" + instrumentationDirectory, ".telemetry");
4040

41+
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen =
42+
parseAllMetricFiles(telemetryDir);
43+
44+
return aggregateMetricsByScope(metricsByWhen);
45+
}
46+
47+
/**
48+
* Parses all metric files in the given .telemetry directory and returns a map where the key is
49+
* the 'when' condition and the value is a list of metrics grouped by scope.
50+
*
51+
* @param telemetryDir the path to the .telemetry directory
52+
* @return a map of 'when' to list of metrics by scope
53+
*/
54+
private static Map<String, List<EmittedMetrics.MetricsByScope>> parseAllMetricFiles(
55+
Path telemetryDir) {
56+
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen = new HashMap<>();
4157
if (Files.exists(telemetryDir) && Files.isDirectory(telemetryDir)) {
4258
try (Stream<Path> files = Files.list(telemetryDir)) {
4359
files
@@ -49,52 +65,102 @@ public static Map<String, EmittedMetrics> getMetricsFromFiles(
4965
String when = content.substring(0, content.indexOf('\n'));
5066
String whenKey = when.replace("when: ", "");
5167

52-
metricsByWhen.putIfAbsent(whenKey, new StringBuilder("metrics:\n"));
53-
54-
// Skip the metric label ("metrics:") so we can aggregate into one list
55-
int metricsIndex = content.indexOf("metrics:\n");
68+
int metricsIndex = content.indexOf("metrics_by_scope:");
5669
if (metricsIndex != -1) {
57-
String contentAfterMetrics =
58-
content.substring(metricsIndex + "metrics:\n".length());
59-
metricsByWhen.get(whenKey).append(contentAfterMetrics);
70+
String yaml = "when: " + whenKey + "\n" + content.substring(metricsIndex);
71+
EmittedMetrics parsed;
72+
try {
73+
parsed = YamlHelper.emittedMetricsParser(yaml);
74+
} catch (Exception e) {
75+
logger.severe(
76+
"Error parsing metrics file (" + path + "): " + e.getMessage());
77+
return;
78+
}
79+
if (parsed.getMetricsByScope() != null) {
80+
metricsByWhen.putIfAbsent(whenKey, new ArrayList<>());
81+
metricsByWhen.get(whenKey).addAll(parsed.getMetricsByScope());
82+
}
6083
}
6184
}
6285
});
6386
} catch (IOException e) {
6487
logger.severe("Error reading metrics files: " + e.getMessage());
6588
}
6689
}
90+
return metricsByWhen;
91+
}
6792

68-
return parseMetrics(metricsByWhen);
93+
/**
94+
* Aggregates metrics under the same scope for each 'when' condition, deduplicating metrics by
95+
* name.
96+
*
97+
* @param metricsByWhen map of 'when' to list of metrics by scope
98+
* @return a map of 'when' to aggregated EmittedMetrics
99+
*/
100+
private static Map<String, EmittedMetrics> aggregateMetricsByScope(
101+
Map<String, List<EmittedMetrics.MetricsByScope>> metricsByWhen) {
102+
Map<String, EmittedMetrics> result = new HashMap<>();
103+
for (Map.Entry<String, List<EmittedMetrics.MetricsByScope>> entry : metricsByWhen.entrySet()) {
104+
String when = entry.getKey();
105+
List<EmittedMetrics.MetricsByScope> allScopes = entry.getValue();
106+
Map<String, Map<String, EmittedMetrics.Metric>> metricsByScopeName = new HashMap<>();
107+
108+
for (EmittedMetrics.MetricsByScope scopeEntry : allScopes) {
109+
String scope = scopeEntry.getScope();
110+
metricsByScopeName.putIfAbsent(scope, new HashMap<>());
111+
Map<String, EmittedMetrics.Metric> metricMap = metricsByScopeName.get(scope);
112+
113+
for (EmittedMetrics.Metric metric : scopeEntry.getMetrics()) {
114+
metricMap.put(metric.getName(), metric); // deduplicate by name
115+
}
116+
}
117+
118+
List<EmittedMetrics.MetricsByScope> mergedScopes = new ArrayList<>();
119+
for (Map.Entry<String, Map<String, EmittedMetrics.Metric>> scopeEntry :
120+
metricsByScopeName.entrySet()) {
121+
mergedScopes.add(
122+
new EmittedMetrics.MetricsByScope(
123+
scopeEntry.getKey(), new ArrayList<>(scopeEntry.getValue().values())));
124+
}
125+
result.put(when, new EmittedMetrics(when, mergedScopes));
126+
}
127+
return result;
69128
}
70129

71130
/**
72131
* Takes in a raw string representation of the aggregated EmittedMetrics yaml map, separated by
73-
* the `when`, indicating the conditions under which the metrics are emitted. deduplicates the
74-
* metrics by name and then returns a new map of EmittedMetrics objects.
132+
* the {@code when}, indicating the conditions under which the metrics are emitted. Deduplicates
133+
* the metrics by name and then returns a new map of EmittedMetrics objects.
75134
*
76135
* @param input raw string representation of EmittedMetrics yaml
77-
* @return {@code Map<String, EmittedMetrics>} where the key is the `when` condition
136+
* @return map where the key is the {@code when} condition and the value is the corresponding
137+
* EmittedMetrics
138+
* @throws JsonProcessingException if parsing fails
78139
*/
79140
// visible for testing
80-
public static Map<String, EmittedMetrics> parseMetrics(Map<String, StringBuilder> input) {
141+
public static Map<String, EmittedMetrics> parseMetrics(Map<String, StringBuilder> input)
142+
throws JsonProcessingException {
81143
Map<String, EmittedMetrics> metricsMap = new HashMap<>();
82144
for (Map.Entry<String, StringBuilder> entry : input.entrySet()) {
83145
String when = entry.getKey();
84146
StringBuilder content = entry.getValue();
85147

86148
EmittedMetrics metrics = YamlHelper.emittedMetricsParser(content.toString());
87-
if (metrics.getMetrics() == null) {
149+
if (metrics.getMetricsByScope() == null) {
88150
continue;
89151
}
90152

91-
Map<String, EmittedMetrics.Metric> deduplicatedMetrics = new HashMap<>();
92-
for (EmittedMetrics.Metric metric : metrics.getMetrics()) {
93-
deduplicatedMetrics.put(metric.getName(), metric);
153+
List<EmittedMetrics.MetricsByScope> deduplicatedScopes = new ArrayList<>();
154+
for (EmittedMetrics.MetricsByScope scopeEntry : metrics.getMetricsByScope()) {
155+
String scope = scopeEntry.getScope();
156+
Map<String, EmittedMetrics.Metric> dedupedMetrics = new HashMap<>();
157+
for (EmittedMetrics.Metric metric : scopeEntry.getMetrics()) {
158+
dedupedMetrics.put(metric.getName(), metric);
159+
}
160+
deduplicatedScopes.add(
161+
new EmittedMetrics.MetricsByScope(scope, new ArrayList<>(dedupedMetrics.values())));
94162
}
95-
96-
List<EmittedMetrics.Metric> uniqueMetrics = new ArrayList<>(deduplicatedMetrics.values());
97-
metricsMap.put(when, new EmittedMetrics(when, uniqueMetrics));
163+
metricsMap.put(when, new EmittedMetrics(when, deduplicatedScopes));
98164
}
99165
return metricsMap;
100166
}

0 commit comments

Comments
 (0)