Skip to content

Commit 317db5d

Browse files
authored
Merge pull request #805 from TE-WenjunMao/master
Add per-build metrics configuration options and help documentation
2 parents faa3468 + e1cfe34 commit 317db5d

9 files changed

Lines changed: 419 additions & 4 deletions

File tree

docs/configuration/configuration.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@ If checked the plugin will collect data of up/down status of your Jenkins agents
5252
## Collect metrics for each run per build
5353
If checked it will cause the plugin to add metrics for every build available. The build number will be added as label. Use with caution!
5454

55+
## Per-build metrics max age (hours)
56+
Maximum age in hours for per-build metrics. Runs whose end time is older than this value will not be included in per-build metrics.
57+
Set to `0` (default) to disable the age limit and include all builds.
58+
59+
Example values:
60+
- `1` - Only include builds that ended within the last hour
61+
- `24` - Only include builds that ended within the last 24 hours
62+
- `168` - Only include builds that ended within the last 7 days (168 hours)
63+
64+
This setting helps control the number of time series generated when per-build metrics are enabled,
65+
preventing unbounded growth and reducing Prometheus storage and query costs.
66+
67+
## Per-build metrics max builds per job
68+
Maximum number of builds per job to include in per-build metrics. Only the latest N builds will be included.
69+
Set to `0` (default) to disable the count limit and include all builds.
70+
71+
Example values:
72+
- `5` - Only include the 5 most recent builds per job
73+
- `10` - Only include the 10 most recent builds per job
74+
- `50` - Only include the 50 most recent builds per job
75+
76+
If both max age and max builds are configured, the stricter limit will apply.
77+
5578
## Collect Code coverage (since v2.3.0)
5679
If checked and you publish your code coverage results with [https://plugins.jenkins.io/coverage](https://plugins.jenkins.io/coverage)
5780
the plugin will output metrics for:

src/main/java/org/jenkinsci/plugins/prometheus/JobCollector.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ protected void appendJobMetrics(Job<?, ?> job) {
259259
processRun(job, buildToCheck, baseLabelValueArray, lastBuildMetrics);
260260

261261
Run<?, ?> run = buildToCheck;
262+
int buildIndex = 0;
262263
while (run != null) {
263264
LOGGER.debug("getting metrics for run [{}] from job [{}], include per run metrics [{}]", run.getNumber(), job.getName(), isPerBuildMetrics);
264265
if (Runs.includeBuildInMetrics(run)) {
@@ -267,12 +268,13 @@ protected void appendJobMetrics(Job<?, ?> job) {
267268

268269
summary.calculateMetric(run, labelValueArray);
269270

270-
if (isPerBuildMetrics) {
271+
if (isPerBuildMetrics && Runs.includeRunInPerBuildMetrics(run, buildIndex)) {
271272
labelValueArray = Arrays.copyOf(labelValueArray, labelValueArray.length + 1);
272273
labelValueArray[labelValueArray.length - 1] = String.valueOf(run.getNumber());
273274

274275
processRun(job, run, labelValueArray, perBuildMetrics);
275276
}
277+
buildIndex++;
276278
}
277279
run = run.getPreviousBuild();
278280
}

src/main/java/org/jenkinsci/plugins/prometheus/config/PrometheusConfiguration.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class PrometheusConfiguration extends GlobalConfiguration {
5454
private boolean appendParamLabel = false;
5555
private boolean appendStatusLabel = false;
5656
private boolean perBuildMetrics = false;
57+
private long perBuildMetricsMaxAgeInHours = 0L; // 0 means no limit
58+
private int perBuildMetricsMaxBuilds = 0; // 0 means no limit
5759

5860

5961
private transient boolean collectDiskUsageEnvironmentVariableSet = false;
@@ -264,6 +266,36 @@ public void setPerBuildMetrics(boolean perBuildMetrics) {
264266
this.perBuildMetrics = perBuildMetrics;
265267
}
266268

269+
/**
270+
* Gets the maximum age in hours for per-build metrics.
271+
* Runs older than this value will not be included in per-build metrics.
272+
* A value of 0 means no age limit.
273+
* @return the maximum age in hours, or 0 for no limit
274+
*/
275+
public long getPerBuildMetricsMaxAgeInHours() {
276+
return perBuildMetricsMaxAgeInHours;
277+
}
278+
279+
@DataBoundSetter
280+
public void setPerBuildMetricsMaxAgeInHours(long perBuildMetricsMaxAgeInHours) {
281+
this.perBuildMetricsMaxAgeInHours = Math.max(0, perBuildMetricsMaxAgeInHours);
282+
}
283+
284+
/**
285+
* Gets the maximum number of builds to include in per-build metrics per job.
286+
* Only the latest N builds will be included.
287+
* A value of 0 means no count limit.
288+
* @return the maximum number of builds, or 0 for no limit
289+
*/
290+
public int getPerBuildMetricsMaxBuilds() {
291+
return perBuildMetricsMaxBuilds;
292+
}
293+
294+
@DataBoundSetter
295+
public void setPerBuildMetricsMaxBuilds(int perBuildMetricsMaxBuilds) {
296+
this.perBuildMetricsMaxBuilds = Math.max(0, perBuildMetricsMaxBuilds);
297+
}
298+
267299
public boolean isCollectNodeStatus() {
268300
return collectNodeStatus;
269301
}

src/main/java/org/jenkinsci/plugins/prometheus/util/Runs.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.HashMap;
1010
import java.util.List;
1111
import java.util.Map;
12+
import java.util.concurrent.TimeUnit;
1213

1314
public class Runs {
1415

@@ -34,6 +35,38 @@ public static boolean includeBuildInMetrics(Run build) {
3435
return include;
3536
}
3637

38+
/**
39+
* Checks if a run should be included in per-build metrics based on retention settings.
40+
* This method checks both time-based (max age) and count-based (max builds) limits.
41+
*
42+
* @param run the run to check
43+
* @param buildIndex the 0-based index of this build (0 = latest build)
44+
* @return true if the run should be included in per-build metrics, false otherwise
45+
*/
46+
public static boolean includeRunInPerBuildMetrics(Run<?, ?> run, int buildIndex) {
47+
PrometheusConfiguration config = PrometheusConfiguration.get();
48+
49+
// Check count-based limit
50+
int maxBuilds = config.getPerBuildMetricsMaxBuilds();
51+
if (maxBuilds > 0 && buildIndex >= maxBuilds) {
52+
return false;
53+
}
54+
55+
// Check time-based limit (based on build end time)
56+
long maxAgeInHours = config.getPerBuildMetricsMaxAgeInHours();
57+
if (maxAgeInHours > 0) {
58+
long maxAgeInMillis = TimeUnit.HOURS.toMillis(maxAgeInHours);
59+
// Calculate build end time: start time + duration
60+
long buildEndTime = run.getTimeInMillis() + run.getDuration();
61+
long now = System.currentTimeMillis();
62+
if ((now - buildEndTime) > maxAgeInMillis) {
63+
return false;
64+
}
65+
}
66+
67+
return true;
68+
}
69+
3770
public static Map<String, Object> getBuildParameters(Run build) {
3871
List<ParametersAction> actions = build.getActions(ParametersAction.class);
3972
Map<String, Object> answer = new HashMap<>();

src/main/resources/org/jenkinsci/plugins/prometheus/config/PrometheusConfiguration/config.jelly

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,14 @@
5353
<f:entry title="${%Collect node status}" field="collectNodeStatus">
5454
<f:checkbox/>
5555
</f:entry>
56-
<f:entry title="${%Collect metrics for each run per build [Important: read help before enabling this option]}" field="perBuildMetrics">
57-
<f:checkbox/>
58-
</f:entry>
56+
<f:optionalBlock title="${%Collect metrics for each run per build [Important: read help before enabling this option]}" field="perBuildMetrics" inline="true">
57+
<f:entry title="${%Per-build metrics max age in hours}" field="perBuildMetricsMaxAgeInHours">
58+
<f:number/>
59+
</f:entry>
60+
<f:entry title="${%Per-build metrics max builds per job}" field="perBuildMetricsMaxBuilds">
61+
<f:number/>
62+
</f:entry>
63+
</f:optionalBlock>
5964
<f:entry title="${%Collect code coverage}" field="collectCodeCoverage">
6065
<j:set var="readOnlyMode" value="!${instance.isCodeCoverageApiPluginInstalled()}"/>
6166
<f:checkbox/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?jelly escape-by-default='true'?>
2+
<j:jelly xmlns:j="jelly:core">
3+
<div>
4+
<p>
5+
Maximum age in hours for per-build metrics. Runs whose end time is older than this value will not be included in per-build metrics.
6+
</p>
7+
<p>
8+
Set to <code>0</code> (default) to disable the age limit and include all builds.
9+
</p>
10+
<p>
11+
Example values:
12+
<ul>
13+
<li><code>1</code> - Only include builds that ended within the last hour</li>
14+
<li><code>24</code> - Only include builds that ended within the last 24 hours</li>
15+
<li><code>168</code> - Only include builds that ended within the last 7 days (168 hours)</li>
16+
</ul>
17+
</p>
18+
<p>
19+
This setting helps control the number of time series generated when per-build metrics are enabled,
20+
preventing unbounded growth and reducing Prometheus storage and query costs.
21+
</p>
22+
<p>
23+
If both max age and max builds are configured, the stricter limit will apply.
24+
</p>
25+
</div>
26+
</j:jelly>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?jelly escape-by-default='true'?>
2+
<j:jelly xmlns:j="jelly:core">
3+
<div>
4+
<p>
5+
Maximum number of builds per job to include in per-build metrics. Only the latest N builds will be included.
6+
</p>
7+
<p>
8+
Set to <code>0</code> (default) to disable the count limit and include all builds.
9+
</p>
10+
<p>
11+
Example values:
12+
<ul>
13+
<li><code>5</code> - Only include the 5 most recent builds per job</li>
14+
<li><code>10</code> - Only include the 10 most recent builds per job</li>
15+
<li><code>50</code> - Only include the 50 most recent builds per job</li>
16+
</ul>
17+
</p>
18+
<p>
19+
This setting helps control the number of time series generated when per-build metrics are enabled,
20+
preventing unbounded growth and reducing Prometheus storage and query costs.
21+
</p>
22+
<p>
23+
If both max age and max builds are configured, the stricter limit will apply.
24+
</p>
25+
</div>
26+
</j:jelly>

src/test/java/org/jenkinsci/plugins/prometheus/config/PrometheusConfigurationTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,61 @@ private JSONObject getDefaultConfig() {
217217
config.accumulate("collectDiskUsage", "true");
218218
config.accumulate("collectNodeStatus", "true");
219219
config.accumulate("perBuildMetrics", "false");
220+
config.accumulate("perBuildMetricsMaxAgeInHours", "0");
221+
config.accumulate("perBuildMetricsMaxBuilds", "0");
220222
return config;
221223
}
222224

225+
@Test
226+
public void shouldSetPerBuildMetricsMaxAgeInHours() {
227+
// given
228+
Mockito.doCallRealMethod().when(configuration).setPerBuildMetricsMaxAgeInHours(Mockito.anyLong());
229+
Mockito.when(configuration.getPerBuildMetricsMaxAgeInHours()).thenCallRealMethod();
230+
231+
// when
232+
configuration.setPerBuildMetricsMaxAgeInHours(24L);
233+
234+
// then
235+
assertEquals(24L, configuration.getPerBuildMetricsMaxAgeInHours());
236+
}
237+
238+
@Test
239+
public void shouldSetPerBuildMetricsMaxAgeInHoursToZeroForNegativeValues() {
240+
// given
241+
Mockito.doCallRealMethod().when(configuration).setPerBuildMetricsMaxAgeInHours(Mockito.anyLong());
242+
Mockito.when(configuration.getPerBuildMetricsMaxAgeInHours()).thenCallRealMethod();
243+
244+
// when
245+
configuration.setPerBuildMetricsMaxAgeInHours(-1L);
246+
247+
// then
248+
assertEquals(0L, configuration.getPerBuildMetricsMaxAgeInHours());
249+
}
250+
251+
@Test
252+
public void shouldSetPerBuildMetricsMaxBuilds() {
253+
// given
254+
Mockito.doCallRealMethod().when(configuration).setPerBuildMetricsMaxBuilds(Mockito.anyInt());
255+
Mockito.when(configuration.getPerBuildMetricsMaxBuilds()).thenCallRealMethod();
256+
257+
// when
258+
configuration.setPerBuildMetricsMaxBuilds(10);
259+
260+
// then
261+
assertEquals(10, configuration.getPerBuildMetricsMaxBuilds());
262+
}
263+
264+
@Test
265+
public void shouldSetPerBuildMetricsMaxBuildsToZeroForNegativeValues() {
266+
// given
267+
Mockito.doCallRealMethod().when(configuration).setPerBuildMetricsMaxBuilds(Mockito.anyInt());
268+
Mockito.when(configuration.getPerBuildMetricsMaxBuilds()).thenCallRealMethod();
269+
270+
// when
271+
configuration.setPerBuildMetricsMaxBuilds(-5);
272+
273+
// then
274+
assertEquals(0, configuration.getPerBuildMetricsMaxBuilds());
275+
}
276+
223277
}

0 commit comments

Comments
 (0)