1 package org.apache.maven.cli.event;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static org.apache.maven.cli.CLIReportingUtils.formatDuration;
23 import static org.apache.maven.cli.CLIReportingUtils.formatTimestamp;
24 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
25
26 import java.util.List;
27
28 import org.apache.commons.lang3.Validate;
29 import org.apache.maven.execution.AbstractExecutionListener;
30 import org.apache.maven.execution.BuildFailure;
31 import org.apache.maven.execution.BuildSuccess;
32 import org.apache.maven.execution.BuildSummary;
33 import org.apache.maven.execution.ExecutionEvent;
34 import org.apache.maven.execution.MavenExecutionResult;
35 import org.apache.maven.execution.MavenSession;
36 import org.apache.maven.plugin.MojoExecution;
37 import org.apache.maven.plugin.descriptor.MojoDescriptor;
38 import org.apache.maven.project.MavenProject;
39 import org.apache.maven.shared.utils.logging.MessageBuilder;
40 import org.codehaus.plexus.util.StringUtils;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45
46
47
48
49 public class ExecutionEventLogger
50 extends AbstractExecutionListener
51 {
52 private final Logger logger;
53
54 private static final int LINE_LENGTH = 72;
55 private static final int MAX_PADDED_BUILD_TIME_DURATION_LENGTH = 9;
56 private static final int MAX_PROJECT_NAME_LENGTH = 52;
57
58 private int totalProjects;
59 private volatile int currentVisitedProjectCount;
60
61 public ExecutionEventLogger()
62 {
63 logger = LoggerFactory.getLogger( ExecutionEventLogger.class );
64 }
65
66
67 public ExecutionEventLogger( Logger logger )
68 {
69 this.logger = Validate.notNull( logger, "logger cannot be null" );
70 }
71
72 private static String chars( char c, int count )
73 {
74 StringBuilder buffer = new StringBuilder( count );
75
76 for ( int i = count; i > 0; i-- )
77 {
78 buffer.append( c );
79 }
80
81 return buffer.toString();
82 }
83
84 private void infoLine( char c )
85 {
86 infoMain( chars( c, LINE_LENGTH ) );
87 }
88
89 private void infoMain( String msg )
90 {
91 logger.info( buffer().strong( msg ).toString() );
92 }
93
94 @Override
95 public void projectDiscoveryStarted( ExecutionEvent event )
96 {
97 if ( logger.isInfoEnabled() )
98 {
99 logger.info( "Scanning for projects..." );
100 }
101 }
102
103 @Override
104 public void sessionStarted( ExecutionEvent event )
105 {
106 if ( logger.isInfoEnabled() && event.getSession().getProjects().size() > 1 )
107 {
108 infoLine( '-' );
109
110 infoMain( "Reactor Build Order:" );
111
112 logger.info( "" );
113
114 final List<MavenProject> projects = event.getSession().getProjects();
115 for ( MavenProject project : projects )
116 {
117 int len = LINE_LENGTH - project.getName().length() - project.getPackaging().length() - 2;
118 logger.info( project.getName() + chars( ' ', ( len > 0 ) ? len : 1 ) + '[' + project.getPackaging()
119 + ']' );
120 }
121
122 totalProjects = projects.size();
123 }
124 }
125
126 @Override
127 public void sessionEnded( ExecutionEvent event )
128 {
129 if ( logger.isInfoEnabled() )
130 {
131 if ( event.getSession().getProjects().size() > 1 )
132 {
133 logReactorSummary( event.getSession() );
134 }
135
136 logResult( event.getSession() );
137
138 logStats( event.getSession() );
139
140 infoLine( '-' );
141 }
142 }
143
144 private void logReactorSummary( MavenSession session )
145 {
146 infoLine( '-' );
147
148 infoMain( "Reactor Summary:" );
149
150 logger.info( "" );
151
152 MavenExecutionResult result = session.getResult();
153
154 List<MavenProject> projects = session.getProjects();
155 MavenProject lastProject = projects.get( projects.size() - 1 );
156 MavenProject topProject = session.getTopLevelProject();
157
158 for ( MavenProject project : projects )
159 {
160 StringBuilder buffer = new StringBuilder( 128 );
161
162 buffer.append( project.getName() );
163 buffer.append( ' ' );
164
165 if ( topProject.equals( project ) || lastProject.equals( project )
166 || !topProject.getVersion().equals( project.getVersion() ) )
167 {
168 buffer.append( project.getVersion() );
169 buffer.append( ' ' );
170 }
171
172 if ( buffer.length() <= MAX_PROJECT_NAME_LENGTH )
173 {
174 while ( buffer.length() < MAX_PROJECT_NAME_LENGTH )
175 {
176 buffer.append( '.' );
177 }
178 buffer.append( ' ' );
179 }
180
181 BuildSummary buildSummary = result.getBuildSummary( project );
182
183 if ( buildSummary == null )
184 {
185 buffer.append( buffer().warning( "SKIPPED" ) );
186 }
187 else if ( buildSummary instanceof BuildSuccess )
188 {
189 buffer.append( buffer().success( "SUCCESS" ) );
190 buffer.append( " [" );
191 String buildTimeDuration = formatDuration( buildSummary.getTime() );
192 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - buildTimeDuration.length();
193 if ( padSize > 0 )
194 {
195 buffer.append( chars( ' ', padSize ) );
196 }
197 buffer.append( buildTimeDuration );
198 buffer.append( ']' );
199 }
200 else if ( buildSummary instanceof BuildFailure )
201 {
202 buffer.append( buffer().failure( "FAILURE" ) );
203 buffer.append( " [" );
204 String buildTimeDuration = formatDuration( buildSummary.getTime() );
205 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - buildTimeDuration.length();
206 if ( padSize > 0 )
207 {
208 buffer.append( chars( ' ', padSize ) );
209 }
210 buffer.append( buildTimeDuration );
211 buffer.append( ']' );
212 }
213
214 logger.info( buffer.toString() );
215 }
216 }
217
218 private void logResult( MavenSession session )
219 {
220 infoLine( '-' );
221 MessageBuilder buffer = buffer();
222
223 if ( session.getResult().hasExceptions() )
224 {
225 buffer.failure( "BUILD FAILURE" );
226 }
227 else
228 {
229 buffer.success( "BUILD SUCCESS" );
230 }
231 logger.info( buffer.toString() );
232 }
233
234 private void logStats( MavenSession session )
235 {
236 infoLine( '-' );
237
238 long finish = System.currentTimeMillis();
239
240 long time = finish - session.getRequest().getStartTime().getTime();
241
242 String wallClock = session.getRequest().getDegreeOfConcurrency() > 1 ? " (Wall Clock)" : "";
243
244 logger.info( "Total time: " + formatDuration( time ) + wallClock );
245
246 logger.info( "Finished at: " + formatTimestamp( finish ) );
247 }
248
249 @Override
250 public void projectSkipped( ExecutionEvent event )
251 {
252 if ( logger.isInfoEnabled() )
253 {
254 logger.info( "" );
255 infoLine( '-' );
256
257 infoMain( "Skipping " + event.getProject().getName() );
258 logger.info( "This project has been banned from the build due to previous failures." );
259
260 infoLine( '-' );
261 }
262 }
263
264 @Override
265 public void projectStarted( ExecutionEvent event )
266 {
267 if ( logger.isInfoEnabled() )
268 {
269 MavenProject project = event.getProject();
270
271 logger.info( "" );
272
273
274 String projectKey = project.getGroupId() + ':' + project.getArtifactId();
275
276 final String preHeader = "--< ";
277 final String postHeader = " >--";
278
279 final int headerLen = preHeader.length() + projectKey.length() + postHeader.length();
280
281 String prefix = chars( '-', Math.max( 0, ( LINE_LENGTH - headerLen ) / 2 ) ) + preHeader;
282
283 String suffix = postHeader
284 + chars( '-', Math.max( 0, LINE_LENGTH - headerLen - prefix.length() + preHeader.length() ) );
285
286 logger.info( buffer().strong( prefix ).project( projectKey ).strong( suffix ).toString() );
287
288
289 String building = "Building " + event.getProject().getName() + " " + event.getProject().getVersion();
290
291 if ( totalProjects <= 1 )
292 {
293 infoMain( building );
294 }
295 else
296 {
297
298 int number;
299 synchronized ( this )
300 {
301 number = ++currentVisitedProjectCount;
302 }
303 String progress = " [" + number + '/' + totalProjects + ']';
304
305 int pad = LINE_LENGTH - building.length() - progress.length();
306
307 infoMain( building + ( ( pad > 0 ) ? chars( ' ', pad ) : "" ) + progress );
308 }
309
310
311 prefix = chars( '-', Math.max( 0, ( LINE_LENGTH - project.getPackaging().length() - 4 ) / 2 ) );
312 suffix = chars( '-', Math.max( 0, LINE_LENGTH - project.getPackaging().length() - 4 - prefix.length() ) );
313 infoMain( prefix + "[ " + project.getPackaging() + " ]" + suffix );
314 }
315 }
316
317 @Override
318 public void mojoSkipped( ExecutionEvent event )
319 {
320 if ( logger.isWarnEnabled() )
321 {
322 logger.warn( "Goal " + event.getMojoExecution().getGoal()
323 + " requires online mode for execution but Maven is currently offline, skipping" );
324 }
325 }
326
327
328
329
330 @Override
331 public void mojoStarted( ExecutionEvent event )
332 {
333 if ( logger.isInfoEnabled() )
334 {
335 logger.info( "" );
336
337 MessageBuilder buffer = buffer().strong( "--- " );
338 append( buffer, event.getMojoExecution() );
339 append( buffer, event.getProject() );
340 buffer.strong( " ---" );
341
342 logger.info( buffer.toString() );
343 }
344 }
345
346
347
348
349
350
351
352 @Override
353 public void forkStarted( ExecutionEvent event )
354 {
355 if ( logger.isInfoEnabled() )
356 {
357 logger.info( "" );
358
359 MessageBuilder buffer = buffer().strong( ">>> " );
360 append( buffer, event.getMojoExecution() );
361 buffer.strong( " > " );
362 appendForkInfo( buffer, event.getMojoExecution().getMojoDescriptor() );
363 append( buffer, event.getProject() );
364 buffer.strong( " >>>" );
365
366 logger.info( buffer.toString() );
367 }
368 }
369
370
371
372
373
374
375
376 @Override
377 public void forkSucceeded( ExecutionEvent event )
378 {
379 if ( logger.isInfoEnabled() )
380 {
381 logger.info( "" );
382
383 MessageBuilder buffer = buffer().strong( "<<< " );
384 append( buffer, event.getMojoExecution() );
385 buffer.strong( " < " );
386 appendForkInfo( buffer, event.getMojoExecution().getMojoDescriptor() );
387 append( buffer, event.getProject() );
388 buffer.strong( " <<<" );
389
390 logger.info( buffer.toString() );
391
392 logger.info( "" );
393 }
394 }
395
396 private void append( MessageBuilder buffer, MojoExecution me )
397 {
398 buffer.mojo( me.getArtifactId() + ':' + me.getVersion() + ':' + me.getGoal() );
399 if ( me.getExecutionId() != null )
400 {
401 buffer.a( ' ' ).strong( '(' + me.getExecutionId() + ')' );
402 }
403 }
404
405 private void appendForkInfo( MessageBuilder buffer, MojoDescriptor md )
406 {
407 StringBuilder buff = new StringBuilder();
408 if ( StringUtils.isNotEmpty( md.getExecutePhase() ) )
409 {
410
411 if ( StringUtils.isNotEmpty( md.getExecuteLifecycle() ) )
412 {
413 buff.append( '[' );
414 buff.append( md.getExecuteLifecycle() );
415 buff.append( ']' );
416 }
417 buff.append( md.getExecutePhase() );
418 }
419 else
420 {
421
422 buff.append( ':' );
423 buff.append( md.getExecuteGoal() );
424 }
425 buffer.strong( buff.toString() );
426 }
427
428 private void append( MessageBuilder buffer, MavenProject project )
429 {
430 buffer.a( " @ " ).project( project.getArtifactId() );
431 }
432
433 @Override
434 public void forkedProjectStarted( ExecutionEvent event )
435 {
436 if ( logger.isInfoEnabled() && event.getMojoExecution().getForkedExecutions().size() > 1 )
437 {
438 logger.info( "" );
439 infoLine( '>' );
440
441 infoMain( "Forking " + event.getProject().getName() + " " + event.getProject().getVersion() );
442
443 infoLine( '>' );
444 }
445 }
446 }