Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5aa5583
Add build version metrics as label
errose28 Jun 11, 2026
5e23d06
Temp workaround to fix metrics from a master regression
errose28 Jun 12, 2026
6a3a654
Temporary acceptance test config to create a pre-finalized cluster
errose28 Jun 12, 2026
487ed1c
Add support for injecting datanode startup versions
errose28 Jun 12, 2026
0e64e79
Add S3 gateway client metrics
errose28 Jun 12, 2026
92c05a7
Initial dashboard
errose28 Jun 23, 2026
c6f94f8
updates
errose28 Jun 23, 2026
c0760d8
fix tables
errose28 Jun 23, 2026
d25e60a
Initial pie chart
errose28 Jun 23, 2026
d89a4e0
Add more compact dashboard
errose28 Jun 23, 2026
17ba023
Add instance filter, remove verbose panels
errose28 Jun 23, 2026
098cf4b
Remove unused panels and labels, add sv and av cols
errose28 Jun 23, 2026
bb0842d
Update column names
errose28 Jun 23, 2026
cde13da
Add third most compact dashboard
errose28 Jun 23, 2026
00ef727
Reduce to one compact dashboard
errose28 Jun 24, 2026
e87dccd
Update text on top panels
errose28 Jun 24, 2026
752d29b
Update descriptions
errose28 Jun 24, 2026
81074c9
Inject mixed software and apparent versions
errose28 Jun 24, 2026
2a7d327
Finishing touches on dashboard
errose28 Jun 24, 2026
97fb99d
Remove ability to inject DN versions
errose28 Jun 24, 2026
96eb96c
Revert custom changes to inject mixed versions
errose28 Jun 24, 2026
0d2db5b
Adjust fill colors for finalized nodes
errose28 Jun 24, 2026
2390d1f
Make lines for finalization stacking
errose28 Jun 24, 2026
7baa9b1
Remove filter selector
errose28 Jun 24, 2026
96a0dd4
Fix chekcstyle and improve top row
errose28 Jun 24, 2026
e663baf
Add "Finalizing" status to top panel
errose28 Jun 24, 2026
99249e5
Merge branch 'HDDS-14496-zdu' into worktree-version-metrics
errose28 Jun 25, 2026
d9457e8
Add metric for finalizing marker
errose28 Jun 25, 2026
5755087
Add metric to indicate presence of finalizing marker
errose28 Jun 25, 2026
c6c2d83
Fix dashboard table pagination, alignment, and status panel
errose28 Jun 25, 2026
3495482
Checkstyle
errose28 Jun 26, 2026
623924c
Fix duplicate metrics issues and test cleanup
errose28 Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ public void start() throws IOException {
.register("prometheus", "Hadoop metrics prometheus exporter",
prometheusMetricsSink);
}
BuildInfoMetrics.create(name);
updateConnectorAddress();
}

Expand All @@ -341,6 +342,7 @@ private boolean isEnabled() {
public void stop() throws Exception {
if (httpServer != null) {
httpServer.stop();
BuildInfoMetrics.unregister();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.hadoop.hdds.server.http;

import org.apache.hadoop.hdds.utils.HddsVersionInfo;
import org.apache.hadoop.hdds.utils.VersionInfo;
import org.apache.hadoop.metrics2.MetricsCollector;
import org.apache.hadoop.metrics2.MetricsRecordBuilder;
import org.apache.hadoop.metrics2.MetricsSource;
import org.apache.hadoop.metrics2.MetricsSystem;
import org.apache.hadoop.metrics2.MetricsTag;
import org.apache.hadoop.metrics2.annotation.Metrics;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.lib.Interns;
import org.apache.hadoop.ozone.OzoneConsts;

/**
* Exposes build version and git revision as a Prometheus info metric.
*
* <p>Follows the OpenMetrics Info pattern: the value is always 1, and the
* identifying strings are carried as labels. The metric name uses the
* conventional {@code _build_info} suffix.</p>
*
* <pre>
* ozone_build_info{component="OM",version="2.0.0",revision="abc1234"} 1
* </pre>
*/
@Metrics(about = "Ozone build version info", context = OzoneConsts.OZONE)
public final class BuildInfoMetrics implements MetricsSource {

public static final String METRICS_SOURCE_NAME = "OzoneBuildInfo";
/**
* Record name chosen so that prometheusName("Ozone", "BuildInfo") produces
* the conventional "ozone_build_info" metric name.
*/
public static final String RECORD_NAME = "Ozone";

private final String component;
private final String version;
private final String revision;

private BuildInfoMetrics(String component, VersionInfo versionInfo) {
this.component = component;
this.version = versionInfo.getVersion();
this.revision = versionInfo.getRevision();
}

/**
* Return the existing build-info source if one is already registered,
* otherwise create a new one, register it and return it. Build info is
* process-wide, so a single source is shared by every {@code BaseHttpServer}
* in the JVM (e.g. the S3 Gateway runs two). Making this idempotent keeps a
* second caller from failing with a duplicate-source error.
*
* @return a new or existing {@link BuildInfoMetrics}
*/
public static synchronized BuildInfoMetrics create(String component) {
MetricsSystem ms = DefaultMetricsSystem.instance();
MetricsSource existing = ms.getSource(METRICS_SOURCE_NAME);
if (existing != null) {
return (BuildInfoMetrics) existing;
}
BuildInfoMetrics source =
new BuildInfoMetrics(component, HddsVersionInfo.HDDS_VERSION_INFO);
return ms.register(METRICS_SOURCE_NAME, "Ozone build version info", source);
}

/**
* Unregister the build-info source. Idempotent: a no-op if it was never
* registered or was already removed. Called when the owning server stops so
* that a later {@link #create(String)} does not fail with a duplicate source.
*/
public static synchronized void unregister() {
DefaultMetricsSystem.instance().unregisterSource(METRICS_SOURCE_NAME);
}

@Override
public void getMetrics(MetricsCollector collector, boolean all) {
MetricsRecordBuilder builder = collector.addRecord(RECORD_NAME)
.add(new MetricsTag(Interns.info("component", "Ozone component name"), component))
.add(new MetricsTag(Interns.info("revision", "Source control revision"), revision))
.add(new MetricsTag(Interns.info("version", "Ozone build version"), version))
.addGauge(Interns.info("BuildInfo", "Always 1; identifying info is in labels"), 1L);
builder.endRecord();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.hadoop.hdds.server.http;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.concurrent.TimeoutException;
import org.apache.hadoop.metrics2.MetricsSystem;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.ozone.test.GenericTestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
* Tests for {@link BuildInfoMetrics}.
*/
public class TestBuildInfoMetrics {

private MetricsSystem metricsSystem;
private PrometheusMetricsSink sink;

@BeforeEach
public void setUp() {
metricsSystem = DefaultMetricsSystem.instance();
metricsSystem.init("test");
sink = new PrometheusMetricsSink("testserver");
metricsSystem.register("Prometheus", "Prometheus", sink);
}

@AfterEach
public void tearDown() {
metricsSystem.unregisterSource(BuildInfoMetrics.METRICS_SOURCE_NAME);
metricsSystem.stop();
metricsSystem.shutdown();
}

@Test
public void testBuildInfoMetricPublished()
throws IOException, InterruptedException, TimeoutException {
BuildInfoMetrics.create("OM");

String output = waitForMetricsToPublish("ozone_build_info");

assertThat(output).contains("# TYPE ozone_build_info gauge");
assertThat(output).contains("ozone_build_info{");
assertThat(output).contains("component=\"OM\"");
assertThat(output).contains("version=");
assertThat(output).contains("revision=");
// Info metrics always have value 1
assertThat(output).containsPattern("ozone_build_info\\{.*\\} 1");
}

@Test
public void testBuildInfoMetricOnlyOneTypeComment()
throws IOException, InterruptedException, TimeoutException {
BuildInfoMetrics.create("SCM");

String output = waitForMetricsToPublish("ozone_build_info");

assertEquals(1, countOccurrences(output, "# TYPE ozone_build_info gauge"),
"Expected exactly one TYPE comment for ozone_build_info");
}

private String publishAndGet() throws IOException {
metricsSystem.publishMetricsNow();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(bos, UTF_8);
sink.writeMetrics(writer);
writer.flush();
return bos.toString(UTF_8.name());
}

private String waitForMetricsToPublish(String metric)
throws InterruptedException, TimeoutException {
String[] result = new String[1];
GenericTestUtils.waitFor(() -> {
try {
result[0] = publishAndGet();
} catch (IOException e) {
throw new RuntimeException(e);
}
return result[0].contains(metric);
}, 500, 30000);
return result[0];
}

private static int countOccurrences(String text, String substring) {
int count = 0;
int idx = 0;
while ((idx = text.indexOf(substring, idx)) != -1) {
count++;
idx += substring.length();
}
return count;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ public void addRatisEvent(String event) {
}
}

@Metric("Ratis state machine events")
public String getRatisEvents() {
synchronized (ratisEvents) {
return String.join("\n", ratisEvents);
Expand Down
Loading