Skip to content

Commit ae2c5cc

Browse files
authored
HADOOP-17893. Improve PrometheusSink for Namenode TopMetrics (#3426)
Signed-off-by: Akira Ajisaka <aajisaka@apache.org>
1 parent feee41a commit ae2c5cc

File tree

2 files changed

+147
-3
lines changed

2 files changed

+147
-3
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/sink/PrometheusMetricsSink.java

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
*/
1818
package org.apache.hadoop.metrics2.sink;
1919

20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.regex.Matcher;
2023
import org.apache.commons.configuration2.SubsetConfiguration;
2124
import org.apache.hadoop.metrics2.AbstractMetric;
2225
import org.apache.hadoop.metrics2.MetricType;
@@ -52,6 +55,13 @@ public class PrometheusMetricsSink implements MetricsSink {
5255
Pattern.compile("(?<!(^|[A-Z_]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])");
5356
private static final Pattern DELIMITERS = Pattern.compile("[^a-zA-Z0-9]+");
5457

58+
private static final Pattern NN_TOPMETRICS_PATTERN =
59+
Pattern.compile(
60+
"^(nn_top_user_op_counts_window_ms_\\d+)_op_.*?(total_count|count)$");
61+
private static final Pattern NN_TOPMETRICS_TAGS_PATTERN =
62+
Pattern
63+
.compile("^op=(?<op>\\w+)(.user=(?<user>.*)|)\\.(TotalCount|count)$");
64+
5565
public PrometheusMetricsSink() {
5666
}
5767

@@ -95,25 +105,28 @@ public void init(SubsetConfiguration conf) {
95105
}
96106

97107
public void writeMetrics(Writer writer) throws IOException {
108+
List<String> extendMetricsTags = new ArrayList<>();
98109
for (Map.Entry<String, Map<Collection<MetricsTag>, AbstractMetric>> promMetric :
99110
promMetrics.entrySet()) {
100111
AbstractMetric firstMetric = promMetric.getValue().values().iterator().next();
112+
String metricKey = getMetricKey(promMetric.getKey(), firstMetric,
113+
extendMetricsTags);
101114

102115
StringBuilder builder = new StringBuilder();
103116
builder.append("# HELP ")
104-
.append(promMetric.getKey())
117+
.append(metricKey)
105118
.append(" ")
106119
.append(firstMetric.description())
107120
.append("\n")
108121
.append("# TYPE ")
109-
.append(promMetric.getKey())
122+
.append(metricKey)
110123
.append(" ")
111124
.append(firstMetric.type().toString().toLowerCase())
112125
.append("\n");
113126

114127
for (Map.Entry<Collection<MetricsTag>, AbstractMetric> metric :
115128
promMetric.getValue().entrySet()) {
116-
builder.append(promMetric.getKey())
129+
builder.append(metricKey)
117130
.append("{");
118131

119132
String sep = "";
@@ -129,6 +142,13 @@ public void writeMetrics(Writer writer) throws IOException {
129142
sep = ",";
130143
}
131144
}
145+
if (!extendMetricsTags.isEmpty()) {
146+
//add extend tags
147+
for (String tagStr : extendMetricsTags) {
148+
builder.append(sep).append(tagStr);
149+
}
150+
extendMetricsTags.clear();
151+
}
132152
builder.append("} ");
133153
builder.append(metric.getValue().value());
134154
builder.append("\n");
@@ -137,4 +157,39 @@ public void writeMetrics(Writer writer) throws IOException {
137157
writer.write(builder.toString());
138158
}
139159
}
160+
161+
private String getMetricKey(String promMetricKey, AbstractMetric metric,
162+
List<String> extendTags) {
163+
Matcher matcher = NN_TOPMETRICS_PATTERN.matcher(promMetricKey);
164+
if (matcher.find() && matcher.groupCount() == 2) {
165+
extendTags.addAll(parseTopMetricsTags(metric.name()));
166+
return String.format("%s_%s",
167+
matcher.group(1), matcher.group(2));
168+
}
169+
return promMetricKey;
170+
}
171+
172+
/**
173+
* Parse Custom tags for TopMetrics.
174+
*
175+
* @param metricName metricName
176+
* @return Tags for TopMetrics
177+
*/
178+
private List<String> parseTopMetricsTags(String metricName) {
179+
List<String> topMetricsTags = new ArrayList<>();
180+
Matcher matcher = NN_TOPMETRICS_TAGS_PATTERN.matcher(metricName);
181+
if (matcher.find()) {
182+
String op = matcher.group("op");
183+
String user = matcher.group("user");
184+
// add tag op = "$op"
185+
topMetricsTags.add(String
186+
.format("op=\"%s\"", op));
187+
if (StringUtils.isNoneEmpty(user)) {
188+
// add tag user = "$user"
189+
topMetricsTags.add(String
190+
.format("user=\"%s\"", user));
191+
}
192+
}
193+
return topMetricsTags;
194+
}
140195
}

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/sink/TestPrometheusMetricsSink.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,25 @@
2121
import java.io.IOException;
2222
import java.io.OutputStreamWriter;
2323

24+
import java.util.ArrayList;
25+
import java.util.List;
26+
import org.apache.commons.lang3.StringUtils;
27+
import org.apache.hadoop.metrics2.MetricsCollector;
28+
import org.apache.hadoop.metrics2.MetricsRecordBuilder;
29+
import org.apache.hadoop.metrics2.MetricsSource;
2430
import org.apache.hadoop.metrics2.MetricsSystem;
2531
import org.apache.hadoop.metrics2.annotation.Metric;
2632
import org.apache.hadoop.metrics2.annotation.Metrics;
2733
import org.apache.hadoop.metrics2.annotation.Metric.Type;
2834
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
35+
import org.apache.hadoop.metrics2.lib.Interns;
2936
import org.apache.hadoop.metrics2.lib.MutableCounterLong;
3037

3138
import org.junit.Assert;
3239
import org.junit.Test;
3340

3441
import static java.nio.charset.StandardCharsets.UTF_8;
42+
import static org.assertj.core.api.Assertions.assertThat;
3543

3644
/**
3745
* Test prometheus Sink.
@@ -219,6 +227,53 @@ public void testNamingWhitespaces() {
219227
sink.prometheusName(recordName, metricName));
220228
}
221229

230+
/**
231+
* testTopMetricsPublish.
232+
*/
233+
@Test
234+
public void testTopMetricsPublish() throws IOException {
235+
MetricsSystem metrics = DefaultMetricsSystem.instance();
236+
237+
metrics.init("test");
238+
239+
//GIVEN
240+
PrometheusMetricsSink sink = new PrometheusMetricsSink();
241+
242+
metrics.register("prometheus", "prometheus", sink);
243+
TestTopMetrics topMetrics = new TestTopMetrics();
244+
topMetrics.add("60000");
245+
topMetrics.add("1500000");
246+
metrics.register(TestTopMetrics.TOPMETRICS_METRICS_SOURCE_NAME,
247+
"Top N operations by user", topMetrics);
248+
249+
metrics.start();
250+
251+
metrics.publishMetricsNow();
252+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
253+
OutputStreamWriter writer = new OutputStreamWriter(stream, UTF_8);
254+
255+
//WHEN
256+
sink.writeMetrics(writer);
257+
writer.flush();
258+
259+
//THEN
260+
String writtenMetrics = stream.toString(UTF_8.name());
261+
System.out.println(writtenMetrics);
262+
263+
assertThat(writtenMetrics)
264+
.contains(
265+
"nn_top_user_op_counts_window_ms_60000_total_count{context=\"dfs\"")
266+
.contains(
267+
"nn_top_user_op_counts_window_ms_60000_count{")
268+
.contains(
269+
"nn_top_user_op_counts_window_ms_1500000_count{")
270+
.contains(
271+
"op=\"rename\",user=\"hadoop/TEST_HOSTNAME.com@HOSTNAME.COM\"");
272+
273+
metrics.stop();
274+
metrics.shutdown();
275+
}
276+
222277
/**
223278
* Example metric pojo.
224279
*/
@@ -242,4 +297,38 @@ String testTag1() {
242297
@Metric
243298
private MutableCounterLong numBucketCreateFails;
244299
}
300+
301+
/**
302+
* Example metric TopMetrics.
303+
*/
304+
private class TestTopMetrics implements MetricsSource {
305+
306+
public static final String TOPMETRICS_METRICS_SOURCE_NAME =
307+
"NNTopUserOpCounts";
308+
private final List<String> windowMsNames = new ArrayList<>();
309+
310+
public void add(String windowMs) {
311+
windowMsNames.add(String.format(".windowMs=%s", windowMs));
312+
}
313+
314+
@Override
315+
public void getMetrics(MetricsCollector collector, boolean all) {
316+
for (String windowMs : windowMsNames) {
317+
MetricsRecordBuilder rb = collector
318+
.addRecord(TOPMETRICS_METRICS_SOURCE_NAME + windowMs)
319+
.setContext("dfs");
320+
rb.addCounter(
321+
Interns.info("op=" + StringUtils.deleteWhitespace("rename")
322+
+ ".TotalCount", "Total operation count"), 2);
323+
rb.addCounter(
324+
Interns.info("op=" + StringUtils.deleteWhitespace("rename")
325+
+ ".user=" + "hadoop/TEST_HOSTNAME.com@HOSTNAME.COM"
326+
+ ".count", "Total operations performed by user"), 3);
327+
rb.addCounter(
328+
Interns.info("op=" + StringUtils.deleteWhitespace("delete")
329+
+ ".user=" + "test_user2"
330+
+ ".count", "Total operations performed by user"), 4);
331+
}
332+
}
333+
}
245334
}

0 commit comments

Comments
 (0)