diff options
author | Gjøran Voldengen <gjoranv@gmail.com> | 2023-09-28 23:51:21 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-28 23:51:21 +0200 |
commit | ccf8d906232f9382bf31e658f2112840857e41b5 (patch) | |
tree | 5bb28bc760b1c37a10126d4a6b5a1cf99ed25111 | |
parent | 6a71489adaa5df6f6f6a3347dac7b076d87d9725 (diff) | |
parent | 19d86646f1b7c59c607364ea09e736e97d3bd994 (diff) |
Merge pull request #28713 from vespa-engine/olaa/metric-doc-generator
Metric doc generator
4 files changed, 283 insertions, 0 deletions
diff --git a/metrics/src/main/java/ai/vespa/metrics/docs/DocumentationGenerator.java b/metrics/src/main/java/ai/vespa/metrics/docs/DocumentationGenerator.java new file mode 100644 index 00000000000..9d63d4af159 --- /dev/null +++ b/metrics/src/main/java/ai/vespa/metrics/docs/DocumentationGenerator.java @@ -0,0 +1,65 @@ +package ai.vespa.metrics.docs; + +import ai.vespa.metrics.ClusterControllerMetrics; +import ai.vespa.metrics.ConfigServerMetrics; +import ai.vespa.metrics.ContainerMetrics; +import ai.vespa.metrics.DistributorMetrics; +import ai.vespa.metrics.LogdMetrics; +import ai.vespa.metrics.NodeAdminMetrics; +import ai.vespa.metrics.SearchNodeMetrics; +import ai.vespa.metrics.SentinelMetrics; +import ai.vespa.metrics.SlobrokMetrics; +import ai.vespa.metrics.StorageMetrics; +import ai.vespa.metrics.Unit; +import ai.vespa.metrics.VespaMetrics; +import ai.vespa.metrics.set.DefaultMetrics; +import ai.vespa.metrics.set.MetricSet; +import ai.vespa.metrics.set.VespaMetricSet; + +import java.util.Map; +import static ai.vespa.metrics.docs.MetricDocumentation.writeMetricDocumentation; +import static ai.vespa.metrics.docs.MetricSetDocumentation.writeMetricSetDocumentation; + +/** + * @author olaa + * + * Helper class to generate metric reference documentation for docs.vespa.ai + */ +public class DocumentationGenerator { + + public static void main(String[] args) { + + if (args.length != 1) { + throw new IllegalArgumentException("Expected exactly one argument: directory to write to"); + } + var path = args[0]; + + var metrics = getMetrics(); + metrics.forEach((metricType, metricArray) -> writeMetricDocumentation(path, metricArray, metricType)); + + var metricSets = getMetricSets(); + metricSets.forEach((name, metricSet) -> writeMetricSetDocumentation(path, name, metricSet, metrics)); + + UnitDocumentation.writeUnitDocumentation(path, Unit.values()); + } + + private static Map<String, VespaMetrics[]> getMetrics() { + return Map.of( + "Container", ContainerMetrics.values(), + "SearchNode", SearchNodeMetrics.values(), + "Storage", StorageMetrics.values(), + "Distributor", DistributorMetrics.values(), + "ConfigServer", ConfigServerMetrics.values(), + "Logd", LogdMetrics.values(), + "NodeAdmin", NodeAdminMetrics.values(), + "Slobrok", SlobrokMetrics.values(), + "Sentinel", SentinelMetrics.values(), + "ClusterController", ClusterControllerMetrics.values() + ); + } + + private static Map<String, MetricSet> getMetricSets() { + return Map.of("Vespa", VespaMetricSet.vespaMetricSet, + "Default", DefaultMetrics.defaultMetricSet); + } +} diff --git a/metrics/src/main/java/ai/vespa/metrics/docs/MetricDocumentation.java b/metrics/src/main/java/ai/vespa/metrics/docs/MetricDocumentation.java new file mode 100644 index 00000000000..fc46c785f0e --- /dev/null +++ b/metrics/src/main/java/ai/vespa/metrics/docs/MetricDocumentation.java @@ -0,0 +1,57 @@ +package ai.vespa.metrics.docs; + +import ai.vespa.metrics.VespaMetrics; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author olaa + */ +public class MetricDocumentation { + + protected static void writeMetricDocumentation(String path, VespaMetrics[] metrics, String metricType) { + var referenceBuilder = new StringBuilder(); + referenceBuilder.append(String.format(""" + --- + # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + title: "%s Metrics" + --- + + <table class="table"> + <thead> + <tr><th>Name</th><th>Description</th><th>Unit</th></tr> + </thead> + <tbody> + %s </tbody> + </table> + """, metricType, htmlRows(metrics))); + + try (FileWriter fileWriter = new FileWriter(path + "/" + metricType.toLowerCase() + "-metrics-reference.html")) { + fileWriter.write(referenceBuilder.toString()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static String htmlRows(VespaMetrics[] metrics) { + return Stream.of(metrics) + .map(metric -> + String.format( + """ + <tr> + <td><p id="%s">%s</p></td> + <td>%s</td> + <td>%s</td> + </tr> + """, + metric.baseName().replaceAll("\\.", "_"), + metric.baseName(), + metric.description(), + metric.unit().toString().toLowerCase()) + ).collect(Collectors.joining()); + } +} diff --git a/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java b/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java new file mode 100644 index 00000000000..bafefbfedd6 --- /dev/null +++ b/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java @@ -0,0 +1,105 @@ +package ai.vespa.metrics.docs; + +import ai.vespa.metrics.Suffix; +import ai.vespa.metrics.VespaMetrics; +import ai.vespa.metrics.set.MetricSet; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author olaa + */ +public class MetricSetDocumentation { + + protected static void writeMetricSetDocumentation(String path, String name, MetricSet metricSet, Map<String, VespaMetrics[]> metricsByType) { + + var groupedBySuffix = metricSet.getMetrics() + .keySet() + .stream() + .map(MetricSetDocumentation::withSuffix) + .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toCollection(LinkedHashSet::new)))); + + var metricTypeByName = metricsByType.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + entry -> Arrays.stream(entry.getValue()) + .filter(val -> groupedBySuffix.containsKey(val.baseName())) + .collect(Collectors.toMap( + val -> val, + val -> groupedBySuffix.get(val.baseName()), + (a, b) -> a, + LinkedHashMap::new + )), + (a, b) -> a, + LinkedHashMap::new)); + + var referenceBuilder = new StringBuilder(); + referenceBuilder.append(String.format(""" + --- + # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + title: "%s Metric Set" + ---""", name)); + metricsByType.keySet() + .stream() + .sorted() + .filter(m -> !metricTypeByName.get(m).isEmpty()) + .forEach(type -> + referenceBuilder.append(String.format(""" + + <h2 id="%s-metrics">%s Metrics</h2> + <table class="table"> + <thead> + <tr><th>Name</th><th>Description</th><th>Unit</th><th>Suffixes</th></tr> + </thead> + <tbody> + %s </tbody> + </table> + """, type.toLowerCase(), type, htmlRows(metricTypeByName.get(type)))) + ); + try (FileWriter fileWriter = new FileWriter(path + "/" + metricSet.getId().toLowerCase() + "-set-metrics-reference.html")) { + fileWriter.write(referenceBuilder.toString()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static String htmlRows(Map<VespaMetrics, LinkedHashSet<String>> metrics) { + return metrics.entrySet() + .stream() + .map(entry -> + String.format( + """ + <tr> + <td><p id="%s">%s</p></td> + <td>%s</td> + <td>%s</td> + <td>%s</td> + </tr> + """, + entry.getKey().baseName().replaceAll("\\.", "_"), + entry.getKey().baseName(), + entry.getKey().description(), + entry.getKey().unit().toString().toLowerCase(), + String.join(", ", entry.getValue())) + + ).collect(Collectors.joining()); + } + + private static Map.Entry<String, String> withSuffix(String metricName) { + try { + var suffixIndex = metricName.lastIndexOf("."); + var suffix = Suffix.valueOf(metricName.substring(suffixIndex + 1)); + return Map.entry(metricName.substring(0, suffixIndex), suffix.toString()); + } catch (Exception e) { + return Map.entry(metricName, "N/A"); + } + } + +} diff --git a/metrics/src/main/java/ai/vespa/metrics/docs/UnitDocumentation.java b/metrics/src/main/java/ai/vespa/metrics/docs/UnitDocumentation.java new file mode 100644 index 00000000000..03f99a076b1 --- /dev/null +++ b/metrics/src/main/java/ai/vespa/metrics/docs/UnitDocumentation.java @@ -0,0 +1,56 @@ +package ai.vespa.metrics.docs; + + +import ai.vespa.metrics.Unit; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author olaa + */ +public class UnitDocumentation { + + protected static void writeUnitDocumentation(String path, Unit[] units) { + var referenceBuilder = new StringBuilder(); + referenceBuilder.append(String.format(""" + --- + # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + title: "Metric Units Reference" + --- + + + <table class="table"> + <thead> + <tr><th>Unit</th><th>Description</th></tr> + </thead> + <tbody> + %s </tbody> + </table> + """, htmlRows(units))); + + try (FileWriter fileWriter = new FileWriter(path + "/unit-metrics-reference.html")) { + fileWriter.write(referenceBuilder.toString()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static String htmlRows(Unit[] units) { + return Stream.of(units) + .map(unit -> + String.format( + """ + <tr> + <td>%s</td> + <td>%s</td> + </tr> + """, + unit.fullName(), + unit.getDescription()) + ).collect(Collectors.joining()); + } +} |