From d7d9acfe41de6b505198a893dcb220ebc5e9e4f3 Mon Sep 17 00:00:00 2001 From: Waschndolos Date: Tue, 21 May 2024 20:46:53 +0200 Subject: [PATCH] New metrics which shows the build log size in bytes (#668) * Removing System.out.println * Creating new Gauge which shows the buildlog size in bytes * Fixing codacy issues --- docs/metrics/index.md | 1 + .../plugins/prometheus/JobCollector.java | 7 ++- .../prometheus/collectors/CollectorType.java | 1 + .../builds/BuildCollectorFactory.java | 4 +- .../builds/BuildLogFileSizeGauge.java | 39 ++++++++++++++ .../BuildDurationSummaryTestJobTest.java | 1 - .../builds/BuildLogFileSizeGaugeTest.java | 54 +++++++++++++++++++ 7 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGauge.java create mode 100644 src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGaugeTest.java diff --git a/docs/metrics/index.md b/docs/metrics/index.md index dffdd0db4..7d41b3780 100644 --- a/docs/metrics/index.md +++ b/docs/metrics/index.md @@ -68,6 +68,7 @@ will just return the last build. You can enable per build metrics in the configu | default_jenkins_builds_available_builds_count | Gauge which indicates how many builds are available for the given job | gauge | | default_jenkins_builds_discard_active | Gauge which indicates if the build discard feature is active for the job. | gauge | | default_jenkins_builds_running_build_duration_milliseconds | Gauge which indicates the runtime of the current build. | gauge | +| default_jenkins_builds_<buildname>_last_logfile_size_bytes | Gauge which shows the log file size in bytes. | gauge | diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/JobCollector.java b/src/main/java/org/jenkinsci/plugins/prometheus/JobCollector.java index a35db0b66..57d66224e 100644 --- a/src/main/java/org/jenkinsci/plugins/prometheus/JobCollector.java +++ b/src/main/java/org/jenkinsci/plugins/prometheus/JobCollector.java @@ -45,8 +45,8 @@ private static class BuildMetrics { public MetricCollector, ? extends Collector> jobBuildTestsTotal; public MetricCollector, ? extends Collector> jobBuildTestsSkipped; public MetricCollector, ? extends Collector> jobBuildTestsFailing; - public MetricCollector, ? extends Collector> jobBuildLikelyStuck; + private MetricCollector, ? extends Collector> buildLogFileSizeGauge; private final String buildPrefix; @@ -65,6 +65,7 @@ public void initCollectors(String[] labelNameArray) { this.jobBuildTestsFailing = factory.createRunCollector(CollectorType.FAILED_TESTS_GAUGE, labelNameArray, buildPrefix); this.stageSummary = factory.createRunCollector(CollectorType.STAGE_SUMMARY, ArrayUtils.add(labelNameArray, "stage"), buildPrefix); this.jobBuildLikelyStuck = factory.createRunCollector(CollectorType.BUILD_LIKELY_STUCK_GAUGE, labelNameArray, buildPrefix); + this.buildLogFileSizeGauge = factory.createRunCollector(CollectorType.BUILD_LOGFILE_SIZE_GAUGE, labelNameArray, buildPrefix); } } @@ -97,6 +98,7 @@ public List collect() { // of "parameters" or "status" summary = factory.createRunCollector(CollectorType.BUILD_DURATION_SUMMARY, labelNameArray, null); + // Counter manager acts as a DB to retrieve any counters that are already in memory instead of reinitializing // them with each iteration of collect. var manager = CounterManager.getManager(); @@ -207,6 +209,7 @@ private void addSamples(List allSamples, BuildMetrics build addSamples(allSamples, buildMetrics.jobBuildTestsFailing.collect(), "Adding [{}] samples from gauge ({})"); addSamples(allSamples, buildMetrics.jobBuildLikelyStuck.collect(), "Adding [{}] samples from gauge ({})"); addSamples(allSamples, buildMetrics.stageSummary.collect(), "Adding [{}] samples from summary ({})"); + addSamples(allSamples, buildMetrics.buildLogFileSizeGauge.collect(), "Adding [{}] samples from summary ({})"); } protected void appendJobMetrics(Job job) { @@ -236,6 +239,7 @@ protected void appendJobMetrics(Job job) { String[] labelValueArray = JobLabel.getJobLabelValues(job, run); summary.calculateMetric(run, labelValueArray); + if (isPerBuildMetrics) { labelValueArray = Arrays.copyOf(labelValueArray, labelValueArray.length + 1); labelValueArray[labelValueArray.length - 1] = String.valueOf(run.getNumber()); @@ -259,6 +263,7 @@ private void processRun(Job job, Run run, String[] buildLabelValueAr buildMetrics.jobBuildTestsSkipped.calculateMetric(run, buildLabelValueArray); buildMetrics.jobBuildTestsFailing.calculateMetric(run, buildLabelValueArray); buildMetrics.jobBuildLikelyStuck.calculateMetric(run,buildLabelValueArray); + buildMetrics.buildLogFileSizeGauge.calculateMetric(run, buildLabelValueArray); } } diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java index 11ef275b6..fdbe2e390 100644 --- a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java +++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/CollectorType.java @@ -7,6 +7,7 @@ public enum CollectorType { JENKINS_VERSION_INFO_GAUGE("version"), NODES_ONLINE_GAUGE("nodes_online"), BUILD_DURATION_GAUGE("build_duration_milliseconds"), + BUILD_LOGFILE_SIZE_GAUGE("build_logfile_size_bytes"), BUILD_DURATION_SUMMARY("duration_milliseconds_summary"), BUILD_RESULT_GAUGE("build_result"), BUILD_RESULT_ORDINAL_GAUGE("build_result_ordinal"), diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildCollectorFactory.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildCollectorFactory.java index 692563760..34deed906 100644 --- a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildCollectorFactory.java +++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildCollectorFactory.java @@ -37,10 +37,12 @@ public class BuildCollectorFactory extends BaseCollectorFactory { return saveBuildCollector(new BuildLikelyStuckGauge(labelNames, namespace, subsystem, prefix)); case BUILD_ABORTED_COUNTER: return saveBuildCollector(new BuildAbortedCounter(labelNames, namespace, subsystem, prefix)); - case BUILD_UNSTABLE_COUNTER: + case BUILD_UNSTABLE_COUNTER: return saveBuildCollector(new BuildUnstableCounter(labelNames, namespace, subsystem, prefix)); case BUILD_TOTAL_COUNTER: return saveBuildCollector(new BuildTotalCounter(labelNames, namespace, subsystem, prefix)); + case BUILD_LOGFILE_SIZE_GAUGE: + return saveBuildCollector(new BuildLogFileSizeGauge(labelNames, namespace, subsystem, prefix)); default: return new NoOpMetricCollector<>(); } diff --git a/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGauge.java b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGauge.java new file mode 100644 index 000000000..c568e2225 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGauge.java @@ -0,0 +1,39 @@ +package org.jenkinsci.plugins.prometheus.collectors.builds; + +import hudson.console.AnnotatedLargeText; +import hudson.model.Run; +import io.prometheus.client.Gauge; +import io.prometheus.client.SimpleCollector; +import org.jenkinsci.plugins.prometheus.collectors.CollectorType; + +public class BuildLogFileSizeGauge extends BuildsMetricCollector, Gauge> { + + protected BuildLogFileSizeGauge(String[] labelNames, String namespace, String subsystem, String namePrefix) { + super(labelNames, namespace, subsystem, namePrefix); + } + + @Override + protected CollectorType getCollectorType() { + return CollectorType.BUILD_LOGFILE_SIZE_GAUGE; + } + + @Override + protected String getHelpText() { + return "Build logfile size in bytes"; + } + + @Override + protected SimpleCollector.Builder getCollectorBuilder() { + return Gauge.build(); + } + + @Override + public void calculateMetric(Run jenkinsObject, String[] labelValues) { + if (!jenkinsObject.isBuilding()) { + AnnotatedLargeText logText = jenkinsObject.getLogText(); + long logFileSize = logText.length(); + + collector.labels(labelValues).set(logFileSize); + } + } +} diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildDurationSummaryTestJobTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildDurationSummaryTestJobTest.java index 727338434..f3bf26a95 100644 --- a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildDurationSummaryTestJobTest.java +++ b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildDurationSummaryTestJobTest.java @@ -43,7 +43,6 @@ public void testCollectResult() { Assertions.assertEquals(1, collect.size()); - System.out.println(collect.get(0).samples); Assertions.assertEquals(3, collect.get(0).samples.size(), "Would expect one result"); for (Collector.MetricFamilySamples.Sample sample : collect.get(0).samples) { diff --git a/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGaugeTest.java b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGaugeTest.java new file mode 100644 index 000000000..a60627836 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/prometheus/collectors/builds/BuildLogFileSizeGaugeTest.java @@ -0,0 +1,54 @@ +package org.jenkinsci.plugins.prometheus.collectors.builds; + +import hudson.console.AnnotatedLargeText; +import io.prometheus.client.Collector; +import org.jenkinsci.plugins.prometheus.collectors.testutils.MockedRunCollectorTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class BuildLogFileSizeGaugeTest extends MockedRunCollectorTest { + + @Test + public void testNothingCalculatedWhenRunIsBuilding() { + + when(mock.isBuilding()).thenReturn(true); + + BuildLogFileSizeGauge sut = new BuildLogFileSizeGauge(getLabelNames(), getNamespace(), getSubSystem(), "default"); + + sut.calculateMetric(mock, getLabelValues()); + + List collect = sut.collect(); + + assertEquals(1, collect.size()); + assertEquals(0, collect.get(0).samples.size(), "Would expect no sample created when run is running"); + } + + @Test + public void testCollectResult() { + + when(mock.isBuilding()).thenReturn(false); + AnnotatedLargeText annotatedLargeText = Mockito.mock(AnnotatedLargeText.class); + when(annotatedLargeText.length()).thenReturn(3000L); + + when(mock.getLogText()).thenReturn(annotatedLargeText); + + BuildLogFileSizeGauge sut = new BuildLogFileSizeGauge(getLabelNames(), getNamespace(), getSubSystem(), "default"); + + sut.calculateMetric(mock, getLabelValues()); + + List collect = sut.collect(); + + assertEquals(1, collect.size()); + assertEquals(3000.0, collect.get(0).samples.get(0).value); + + } +} \ No newline at end of file