001 package org.apache.maven.cli.event;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.util.Date;
023
024 import org.apache.maven.execution.AbstractExecutionListener;
025 import org.apache.maven.execution.BuildFailure;
026 import org.apache.maven.execution.BuildSuccess;
027 import org.apache.maven.execution.BuildSummary;
028 import org.apache.maven.execution.ExecutionEvent;
029 import org.apache.maven.execution.MavenExecutionResult;
030 import org.apache.maven.execution.MavenSession;
031 import org.apache.maven.plugin.MojoExecution;
032 import org.apache.maven.project.MavenProject;
033 import org.slf4j.Logger;
034 import org.slf4j.LoggerFactory;
035
036 /**
037 * Logs execution events to logger, eventually user-supplied.
038 *
039 * @author Benjamin Bentmann
040 */
041 public class ExecutionEventLogger
042 extends AbstractExecutionListener
043 {
044 private final Logger logger;
045
046 private static final int LINE_LENGTH = 72;
047
048 public ExecutionEventLogger()
049 {
050 logger = LoggerFactory.getLogger( ExecutionEventLogger.class );
051 }
052
053 // TODO should we deprecate?
054 public ExecutionEventLogger( Logger logger )
055 {
056 if ( logger == null )
057 {
058 throw new IllegalArgumentException( "logger missing" );
059 }
060
061 this.logger = logger;
062 }
063
064 private static String chars( char c, int count )
065 {
066 StringBuilder buffer = new StringBuilder( count );
067
068 for ( int i = count; i > 0; i-- )
069 {
070 buffer.append( c );
071 }
072
073 return buffer.toString();
074 }
075
076 private static String getFormattedTime( long time )
077 {
078 // NOTE: DateFormat is not suitable to format timespans of 24h+
079
080 long h = time / ( 60 * 60 * 1000 );
081 long m = ( time - h * 60 * 60 * 1000 ) / ( 60 * 1000 );
082 long s = ( time - h * 60 * 60 * 1000 - m * 60 * 1000 ) / 1000;
083 long ms = time % 1000;
084
085 String format;
086 if ( h > 0 )
087 {
088 format = "%1$d:%2$02d:%3$02d.%4$03ds";
089 }
090 else if ( m > 0 )
091 {
092 format = "%2$d:%3$02d.%4$03ds";
093 }
094 else
095 {
096 format = "%3$d.%4$03ds";
097 }
098
099 return String.format( format, h, m, s, ms );
100 }
101
102 @Override
103 public void projectDiscoveryStarted( ExecutionEvent event )
104 {
105 if ( logger.isInfoEnabled() )
106 {
107 logger.info( "Scanning for projects..." );
108 }
109 }
110
111 @Override
112 public void sessionStarted( ExecutionEvent event )
113 {
114 if ( logger.isInfoEnabled() && event.getSession().getProjects().size() > 1 )
115 {
116 logger.info( chars( '-', LINE_LENGTH ) );
117
118 logger.info( "Reactor Build Order:" );
119
120 logger.info( "" );
121
122 for ( MavenProject project : event.getSession().getProjects() )
123 {
124 logger.info( project.getName() );
125 }
126 }
127 }
128
129 @Override
130 public void sessionEnded( ExecutionEvent event )
131 {
132 if ( logger.isInfoEnabled() )
133 {
134 if ( event.getSession().getProjects().size() > 1 )
135 {
136 logReactorSummary( event.getSession() );
137 }
138
139 logResult( event.getSession() );
140
141 logStats( event.getSession() );
142
143 logger.info( chars( '-', LINE_LENGTH ) );
144 }
145 }
146
147 private void logReactorSummary( MavenSession session )
148 {
149 logger.info( chars( '-', LINE_LENGTH ) );
150
151 logger.info( "Reactor Summary:" );
152
153 logger.info( "" );
154
155 MavenExecutionResult result = session.getResult();
156
157 for ( MavenProject project : session.getProjects() )
158 {
159 StringBuilder buffer = new StringBuilder( 128 );
160
161 buffer.append( project.getName() );
162
163 buffer.append( ' ' );
164 while ( buffer.length() < LINE_LENGTH - 21 )
165 {
166 buffer.append( '.' );
167 }
168 buffer.append( ' ' );
169
170 BuildSummary buildSummary = result.getBuildSummary( project );
171
172 if ( buildSummary == null )
173 {
174 buffer.append( "SKIPPED" );
175 }
176 else if ( buildSummary instanceof BuildSuccess )
177 {
178 buffer.append( "SUCCESS [" );
179 buffer.append( getFormattedTime( buildSummary.getTime() ) );
180 buffer.append( "]" );
181 }
182 else if ( buildSummary instanceof BuildFailure )
183 {
184 buffer.append( "FAILURE [" );
185 buffer.append( getFormattedTime( buildSummary.getTime() ) );
186 buffer.append( "]" );
187 }
188
189 logger.info( buffer.toString() );
190 }
191 }
192
193 private void logResult( MavenSession session )
194 {
195 logger.info( chars( '-', LINE_LENGTH ) );
196
197 if ( session.getResult().hasExceptions() )
198 {
199 logger.info( "BUILD FAILURE" );
200 }
201 else
202 {
203 logger.info( "BUILD SUCCESS" );
204 }
205 }
206
207 private void logStats( MavenSession session )
208 {
209 logger.info( chars( '-', LINE_LENGTH ) );
210
211 Date finish = new Date();
212
213 long time = finish.getTime() - session.getRequest().getStartTime().getTime();
214
215 String wallClock = session.getRequest().isThreadConfigurationPresent() ? " (Wall Clock)" : "";
216
217 logger.info( "Total time: " + getFormattedTime( time ) + wallClock );
218
219 logger.info( "Finished at: " + finish );
220
221 System.gc();
222
223 Runtime r = Runtime.getRuntime();
224
225 long MB = 1024 * 1024;
226
227 logger.info( "Final Memory: " + ( r.totalMemory() - r.freeMemory() ) / MB + "M/" + r.totalMemory() / MB + "M" );
228 }
229
230 @Override
231 public void projectSkipped( ExecutionEvent event )
232 {
233 if ( logger.isInfoEnabled() )
234 {
235 logger.info( chars( ' ', LINE_LENGTH ) );
236 logger.info( chars( '-', LINE_LENGTH ) );
237
238 logger.info( "Skipping " + event.getProject().getName() );
239 logger.info( "This project has been banned from the build due to previous failures." );
240
241 logger.info( chars( '-', LINE_LENGTH ) );
242 }
243 }
244
245 @Override
246 public void projectStarted( ExecutionEvent event )
247 {
248 if ( logger.isInfoEnabled() )
249 {
250 logger.info( chars( ' ', LINE_LENGTH ) );
251 logger.info( chars( '-', LINE_LENGTH ) );
252
253 logger.info( "Building " + event.getProject().getName() + " " + event.getProject().getVersion() );
254
255 logger.info( chars( '-', LINE_LENGTH ) );
256 }
257 }
258
259 @Override
260 public void mojoSkipped( ExecutionEvent event )
261 {
262 if ( logger.isWarnEnabled() )
263 {
264 logger.warn( "Goal " + event.getMojoExecution().getGoal()
265 + " requires online mode for execution but Maven is currently offline, skipping" );
266 }
267 }
268
269 /**
270 * <pre>--- mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId ---</pre>
271 */
272 @Override
273 public void mojoStarted( ExecutionEvent event )
274 {
275 if ( logger.isInfoEnabled() )
276 {
277 StringBuilder buffer = new StringBuilder( 128 );
278
279 buffer.append( "--- " );
280 append( buffer, event.getMojoExecution() );
281 append( buffer, event.getProject() );
282 buffer.append( " ---" );
283
284 logger.info( "" );
285 logger.info( buffer.toString() );
286 }
287 }
288
289 /**
290 * <pre>>>> mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId >>></pre>
291 */
292 @Override
293 public void forkStarted( ExecutionEvent event )
294 {
295 if ( logger.isInfoEnabled() )
296 {
297 StringBuilder buffer = new StringBuilder( 128 );
298
299 buffer.append( ">>> " );
300 append( buffer, event.getMojoExecution() );
301 append( buffer, event.getProject() );
302 buffer.append( " >>>" );
303
304 logger.info( "" );
305 logger.info( buffer.toString() );
306 }
307 }
308
309 /**
310 * <pre><<< mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId <<<</pre>
311 */
312 @Override
313 public void forkSucceeded( ExecutionEvent event )
314 {
315 if ( logger.isInfoEnabled() )
316 {
317 StringBuilder buffer = new StringBuilder( 128 );
318
319 buffer.append( "<<< " );
320 append( buffer, event.getMojoExecution() );
321 append( buffer, event.getProject() );
322 buffer.append( " <<<" );
323
324 logger.info( "" );
325 logger.info( buffer.toString() );
326 }
327 }
328
329 private void append( StringBuilder buffer, MojoExecution me )
330 {
331 buffer.append( me.getArtifactId() ).append( ':' ).append( me.getVersion() );
332 buffer.append( ':' ).append( me.getGoal() );
333 if ( me.getExecutionId() != null )
334 {
335 buffer.append( " (" ).append( me.getExecutionId() ).append( ')' );
336 }
337 }
338
339 private void append( StringBuilder buffer, MavenProject project )
340 {
341 buffer.append( " @ " ).append( project.getArtifactId() );
342 }
343
344 @Override
345 public void forkedProjectStarted( ExecutionEvent event )
346 {
347 if ( logger.isInfoEnabled() && event.getMojoExecution().getForkedExecutions().size() > 1 )
348 {
349 logger.info( chars( ' ', LINE_LENGTH ) );
350 logger.info( chars( '>', LINE_LENGTH ) );
351
352 logger.info( "Forking " + event.getProject().getName() + " " + event.getProject().getVersion() );
353
354 logger.info( chars( '>', LINE_LENGTH ) );
355 }
356 }
357
358 }