Skip to content

Commit b26b759

Browse files
JENKINS-56448 Introduce a report of the time spent in each phase of the Maven build (compile, test...)
1 parent 638895d commit b26b759

File tree

20 files changed

+3042
-845
lines changed

20 files changed

+3042
-845
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
import java.io.Serializable;
4+
import java.time.ZonedDateTime;
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.SortedSet;
10+
import java.util.TreeSet;
11+
import java.util.stream.Collectors;
12+
13+
import javax.annotation.Nonnull;
14+
15+
/**
16+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
17+
*/
18+
public class MavenExecutionDetails implements Comparable<MavenExecutionDetails>, Serializable {
19+
20+
private static final long serialVersionUID = 1L;
21+
22+
private SortedSet<MavenProjectExecutionDetails> mavenProjectExecutionDetails = new TreeSet<>();
23+
24+
@Nonnull
25+
private final ZonedDateTime start;
26+
@Nonnull
27+
private ZonedDateTime stop;
28+
29+
public MavenExecutionDetails(@Nonnull ZonedDateTime start) {
30+
this.start = start;
31+
}
32+
33+
public SortedSet<MavenProjectExecutionDetails> getMavenProjectExecutionDetails() {
34+
return mavenProjectExecutionDetails;
35+
}
36+
37+
@Nonnull
38+
public ZonedDateTime getStart() {
39+
return start;
40+
}
41+
42+
@Nonnull
43+
public ZonedDateTime getStop() {
44+
return stop;
45+
}
46+
47+
public void setStop(@Nonnull ZonedDateTime stop) {
48+
this.stop = stop;
49+
}
50+
51+
@Override
52+
public int compareTo(MavenExecutionDetails o) {
53+
return this.start.compareTo(o.start);
54+
}
55+
56+
/**
57+
* it's a poor comparison but there is no risk of having 2 builds starting at the same time
58+
*
59+
* @param o
60+
* @return
61+
*/
62+
@Override
63+
public boolean equals(Object o) {
64+
if (this == o) return true;
65+
if (o == null || getClass() != o.getClass()) return false;
66+
67+
MavenExecutionDetails that = (MavenExecutionDetails) o;
68+
69+
return start.equals(that.start);
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
return start.hashCode();
75+
}
76+
77+
public String getExecutionDurationDetails() {
78+
79+
Map<String, MavenExecutionPhaseDetails> phaseDetailsByPhase = new HashMap<>();
80+
81+
for (MavenProjectExecutionDetails projectExecutionDetails :
82+
getMavenProjectExecutionDetails()) {
83+
for (MavenMojoExecutionDetails mojoExecutionDetails : projectExecutionDetails.getMojoExecutionDetails()) {
84+
85+
MavenExecutionPhaseDetails mavenExecutionPhaseDetails = phaseDetailsByPhase.computeIfAbsent(mojoExecutionDetails.getLifecyclePhase(), phase -> new MavenExecutionPhaseDetails(phase));
86+
mavenExecutionPhaseDetails.getMojoExecutionDetails().add(mojoExecutionDetails);
87+
}
88+
}
89+
90+
StringBuilder sb = new StringBuilder();
91+
92+
int phasesCounter = 0;
93+
int mojoExecutionCounter = 0;
94+
List<MavenExecutionPhaseDetails> mavenExecutionPhaseDetailsList = phaseDetailsByPhase.values().stream().sorted().collect(Collectors.toList());
95+
96+
for (MavenExecutionPhaseDetails phaseDetails : mavenExecutionPhaseDetailsList) {
97+
phasesCounter++;
98+
if (phaseDetails.getMojoExecutionDetails().isEmpty()) {
99+
100+
} else {
101+
sb.append(" * " + phaseDetails.getPhase() + " --- " + phaseDetails.getDuration() + "\n");
102+
for (MavenMojoExecutionDetails mojoExecutionDetails : phaseDetails.getMojoExecutionDetails().stream()/*.filter(m -> m.getDurationMillis() > 1000)*/.collect(Collectors.toList())) {
103+
mojoExecutionCounter++;
104+
sb.append(" * " + mojoExecutionDetails.getPlugin().getArtifactId() + ":" + mojoExecutionDetails.getGoal() + " (" + mojoExecutionDetails.getLifecyclePhase() + " - " + mojoExecutionDetails.getExecutionId() + ")" + " @ " + mojoExecutionDetails.getProject().getArtifactId() + " --- " + mojoExecutionDetails.getDuration() + "\n");
105+
}
106+
}
107+
}
108+
109+
sb.append("\n####\n");
110+
for (MavenExecutionPhaseDetails phaseDetails : mavenExecutionPhaseDetailsList) {
111+
if (phaseDetails.getMojoExecutionDetails().isEmpty()) {
112+
} else {
113+
sb.append(" * " + phaseDetails.getPhase() + " --- " + phaseDetails.getDuration() + "\n");
114+
}
115+
}
116+
117+
sb.append("phases:" + phasesCounter + ", mojoExecutions:" + mojoExecutionCounter);
118+
119+
return sb.toString();
120+
}
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
/**
4+
* See {@code org.apache.maven.execution.ExecutionEvent.Type}
5+
*/
6+
public enum MavenExecutionEventType {
7+
ProjectDiscoveryStarted,
8+
SessionStarted,
9+
SessionEnded,
10+
ProjectSkipped,
11+
ProjectStarted,
12+
ProjectSucceeded,
13+
ProjectFailed,
14+
MojoSkipped,
15+
MojoStarted,
16+
MojoSucceeded,
17+
MojoFailed,
18+
ForkStarted,
19+
ForkSucceeded,
20+
ForkFailed,
21+
ForkedProjectStarted,
22+
ForkedProjectSucceeded,
23+
ForkedProjectFailed
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
import edu.umd.cs.findbugs.annotations.NonNull;
4+
import org.apache.commons.lang3.builder.CompareToBuilder;
5+
6+
import java.time.ZonedDateTime;
7+
import java.util.SortedSet;
8+
import java.util.TreeSet;
9+
import java.util.concurrent.TimeUnit;
10+
11+
import javax.annotation.Nonnull;
12+
13+
/**
14+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
15+
*/
16+
public class MavenExecutionPhaseDetails implements Comparable<MavenExecutionPhaseDetails> {
17+
@NonNull
18+
private final String phase;
19+
@NonNull
20+
private SortedSet<MavenMojoExecutionDetails> mojoExecutionDetails = new TreeSet<>();
21+
22+
public MavenExecutionPhaseDetails(@NonNull String phase) {
23+
this.phase = phase;
24+
}
25+
26+
@Nonnull
27+
public ZonedDateTime getStart() {
28+
return mojoExecutionDetails.first().getStart();
29+
}
30+
31+
@Nonnull
32+
public ZonedDateTime getStop() {
33+
return mojoExecutionDetails.last().getStop();
34+
}
35+
36+
/**
37+
* Duration in seconds
38+
* @return duration (e.g. "12s")
39+
*/
40+
@Nonnull
41+
public String getDuration() {
42+
int durationInSecs = 0;
43+
for (MavenMojoExecutionDetails mojoExecutionDetails:getMojoExecutionDetails()) {
44+
durationInSecs += mojoExecutionDetails.getDurationMillis();
45+
}
46+
return TimeUnit.SECONDS.convert(durationInSecs, TimeUnit.MILLISECONDS) + "s";
47+
}
48+
49+
/**
50+
* Maven lifecycle phase
51+
* @return phase (e.g. "compile")
52+
*/
53+
@NonNull
54+
public String getPhase() {
55+
return phase;
56+
}
57+
58+
public SortedSet<MavenMojoExecutionDetails> getMojoExecutionDetails() {
59+
return mojoExecutionDetails;
60+
}
61+
62+
@Override
63+
public int compareTo(MavenExecutionPhaseDetails other) {
64+
return new CompareToBuilder()
65+
.append(this.getStart(), other.getStart())
66+
.append(this.getStop(), other.getStop())
67+
.toComparison();
68+
}
69+
70+
@Override
71+
public String toString() {
72+
return "MavenExecutionPhaseDetails{" +
73+
"phase=" + phase +
74+
", mojoExecutionDetails=" + mojoExecutionDetails +
75+
'}';
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
/**
4+
* See {@code org.apache.maven.execution.BuildSuccess} and {@code org.apache.maven.execution.BuildFailure}
5+
*/
6+
public enum MavenExecutionStatus {Success, Failure}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
import org.jenkinsci.plugins.pipeline.maven.MavenArtifact;
4+
5+
import java.io.Serializable;
6+
import java.time.Duration;
7+
import java.time.ZonedDateTime;
8+
import java.time.temporal.ChronoUnit;
9+
10+
import javax.annotation.Nonnull;
11+
12+
/**
13+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
14+
*/
15+
public class MavenMojoExecutionDetails implements Comparable<MavenMojoExecutionDetails>, Serializable {
16+
17+
private static final long serialVersionUID = 1L;
18+
19+
@Nonnull
20+
private final MavenArtifact project;
21+
@Nonnull
22+
private final MavenArtifact plugin;
23+
@Nonnull
24+
private final String executionId;
25+
@Nonnull
26+
private final String goal;
27+
@Nonnull
28+
private final String lifecyclePhase;
29+
@Nonnull
30+
private final ZonedDateTime start;
31+
@Nonnull
32+
private ZonedDateTime stop;
33+
@Nonnull
34+
private MavenExecutionEventType type;
35+
36+
public MavenMojoExecutionDetails(@Nonnull MavenArtifact project, @Nonnull MavenArtifact plugin, @Nonnull String executionId, @Nonnull String lifecyclePhase, @Nonnull String goal, @Nonnull ZonedDateTime start, @Nonnull MavenExecutionEventType type) {
37+
this.project = project;
38+
this.plugin = plugin;
39+
this.executionId = executionId;
40+
this.lifecyclePhase = lifecyclePhase;
41+
this.goal = goal;
42+
this.start = start;
43+
this.stop = start;
44+
this.type = type;
45+
}
46+
47+
/**
48+
* See {@code org.apache.maven.execution.ExecutionEvent.Type#MojoStarted}
49+
*/
50+
@Nonnull
51+
public ZonedDateTime getStart() {
52+
return start;
53+
}
54+
55+
/**
56+
* See {@code org.apache.maven.execution.ExecutionEvent.Type#MojoSucceeded} and {@code org.apache.maven.execution.ExecutionEvent.Type#MojoFailed}
57+
*/
58+
@Nonnull
59+
public ZonedDateTime getStop() {
60+
return stop;
61+
}
62+
63+
public void stop(@Nonnull ZonedDateTime stop, MavenExecutionEventType type) {
64+
this.stop = stop;
65+
this.type = type;
66+
}
67+
68+
@Nonnull
69+
public MavenArtifact getProject() {
70+
return project;
71+
}
72+
73+
@Nonnull
74+
public MavenArtifact getPlugin() {
75+
return plugin;
76+
}
77+
78+
@Nonnull
79+
public String getExecutionId() {
80+
return executionId;
81+
}
82+
83+
@Nonnull
84+
public String getLifecyclePhase() {
85+
return lifecyclePhase;
86+
}
87+
88+
@Nonnull
89+
public String getGoal() {
90+
return goal;
91+
}
92+
93+
@Override
94+
public String toString() {
95+
return "MavenMojoExecutionDetails{" +
96+
"project=" + project.getId() +
97+
", plugin=" + plugin.getId() +
98+
", executionId='" + executionId + '\'' +
99+
", lifecyclePhase='" + lifecyclePhase + '\'' +
100+
", goal='" + goal + '\'' +
101+
", start=" + start +
102+
", stop=" + stop +
103+
", type=" + type +
104+
'}';
105+
}
106+
107+
@Override
108+
public int compareTo(MavenMojoExecutionDetails other) {
109+
int comparison = this.getStart().compareTo(other.getStart());
110+
111+
if (comparison == 0) {
112+
comparison = this.getStop().compareTo(other.getStop());
113+
}
114+
return comparison;
115+
}
116+
117+
@Nonnull
118+
public String getDuration() {
119+
return Duration.between(start, stop).getSeconds() + "s";
120+
}
121+
122+
@Nonnull
123+
public long getDurationMillis() {
124+
return start.until(stop, ChronoUnit.MILLIS);
125+
}
126+
127+
@Override
128+
public boolean equals(Object o) {
129+
if (this == o) return true;
130+
if (o == null || getClass() != o.getClass()) return false;
131+
132+
MavenMojoExecutionDetails that = (MavenMojoExecutionDetails) o;
133+
134+
if (!project.equals(that.project)) return false;
135+
if (!plugin.equals(that.plugin)) return false;
136+
if (!executionId.equals(that.executionId)) return false;
137+
return goal.equals(that.goal);
138+
}
139+
140+
@Override
141+
public int hashCode() {
142+
int result = project.hashCode();
143+
result = 31 * result + plugin.hashCode();
144+
result = 31 * result + executionId.hashCode();
145+
result = 31 * result + goal.hashCode();
146+
return result;
147+
}
148+
}

0 commit comments

Comments
 (0)