001package 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 022import static org.apache.maven.cli.CLIReportingUtils.formatDuration; 023import static org.apache.maven.cli.CLIReportingUtils.formatTimestamp; 024 025import org.apache.commons.lang3.Validate; 026import org.apache.maven.execution.AbstractExecutionListener; 027import org.apache.maven.execution.BuildFailure; 028import org.apache.maven.execution.BuildSuccess; 029import org.apache.maven.execution.BuildSummary; 030import org.apache.maven.execution.ExecutionEvent; 031import org.apache.maven.execution.MavenExecutionResult; 032import org.apache.maven.execution.MavenSession; 033import org.apache.maven.plugin.MojoExecution; 034import org.apache.maven.plugin.descriptor.MojoDescriptor; 035import org.apache.maven.project.MavenProject; 036import org.codehaus.plexus.util.StringUtils; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040/** 041 * Logs execution events to logger, eventually user-supplied. 042 * 043 * @author Benjamin Bentmann 044 */ 045public class ExecutionEventLogger 046 extends AbstractExecutionListener 047{ 048 private final Logger logger; 049 050 private static final int LINE_LENGTH = 72; 051 private static final int MAX_PADDED_BUILD_TIME_DURATION_LENGTH = 9; 052 private static final int MAX_PROJECT_NAME_LENGTH = 52; 053 054 public ExecutionEventLogger() 055 { 056 logger = LoggerFactory.getLogger( ExecutionEventLogger.class ); 057 } 058 059 // TODO should we deprecate? 060 public ExecutionEventLogger( Logger logger ) 061 { 062 this.logger = Validate.notNull( logger, "logger cannot be null" ); 063 } 064 065 private static String chars( char c, int count ) 066 { 067 StringBuilder buffer = new StringBuilder( count ); 068 069 for ( int i = count; i > 0; i-- ) 070 { 071 buffer.append( c ); 072 } 073 074 return buffer.toString(); 075 } 076 077 @Override 078 public void projectDiscoveryStarted( ExecutionEvent event ) 079 { 080 if ( logger.isInfoEnabled() ) 081 { 082 logger.info( "Scanning for projects..." ); 083 } 084 } 085 086 @Override 087 public void sessionStarted( ExecutionEvent event ) 088 { 089 if ( logger.isInfoEnabled() && event.getSession().getProjects().size() > 1 ) 090 { 091 logger.info( chars( '-', LINE_LENGTH ) ); 092 093 logger.info( "Reactor Build Order:" ); 094 095 logger.info( "" ); 096 097 for ( MavenProject project : event.getSession().getProjects() ) 098 { 099 logger.info( project.getName() ); 100 } 101 } 102 } 103 104 @Override 105 public void sessionEnded( ExecutionEvent event ) 106 { 107 if ( logger.isInfoEnabled() ) 108 { 109 if ( event.getSession().getProjects().size() > 1 ) 110 { 111 logReactorSummary( event.getSession() ); 112 } 113 114 logResult( event.getSession() ); 115 116 logStats( event.getSession() ); 117 118 logger.info( chars( '-', LINE_LENGTH ) ); 119 } 120 } 121 122 private void logReactorSummary( MavenSession session ) 123 { 124 logger.info( chars( '-', LINE_LENGTH ) ); 125 126 logger.info( "Reactor Summary:" ); 127 128 logger.info( "" ); 129 130 MavenExecutionResult result = session.getResult(); 131 132 for ( MavenProject project : session.getProjects() ) 133 { 134 StringBuilder buffer = new StringBuilder( 128 ); 135 136 buffer.append( project.getName() ); 137 buffer.append( ' ' ); 138 139 if ( buffer.length() <= MAX_PROJECT_NAME_LENGTH ) 140 { 141 while ( buffer.length() < MAX_PROJECT_NAME_LENGTH ) 142 { 143 buffer.append( '.' ); 144 } 145 buffer.append( ' ' ); 146 } 147 148 BuildSummary buildSummary = result.getBuildSummary( project ); 149 150 if ( buildSummary == null ) 151 { 152 buffer.append( "SKIPPED" ); 153 } 154 else if ( buildSummary instanceof BuildSuccess ) 155 { 156 buffer.append( "SUCCESS [" ); 157 String buildTimeDuration = formatDuration( buildSummary.getTime() ); 158 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - buildTimeDuration.length(); 159 if ( padSize > 0 ) 160 { 161 buffer.append( chars( ' ', padSize ) ); 162 } 163 buffer.append( buildTimeDuration ); 164 buffer.append( "]" ); 165 } 166 else if ( buildSummary instanceof BuildFailure ) 167 { 168 buffer.append( "FAILURE [" ); 169 String buildTimeDuration = formatDuration( buildSummary.getTime() ); 170 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - buildTimeDuration.length(); 171 if ( padSize > 0 ) 172 { 173 buffer.append( chars( ' ', padSize ) ); 174 } 175 buffer.append( buildTimeDuration ); 176 buffer.append( "]" ); 177 } 178 179 logger.info( buffer.toString() ); 180 } 181 } 182 183 private void logResult( MavenSession session ) 184 { 185 logger.info( chars( '-', LINE_LENGTH ) ); 186 187 if ( session.getResult().hasExceptions() ) 188 { 189 logger.info( "BUILD FAILURE" ); 190 } 191 else 192 { 193 logger.info( "BUILD SUCCESS" ); 194 } 195 } 196 197 private void logStats( MavenSession session ) 198 { 199 logger.info( chars( '-', LINE_LENGTH ) ); 200 201 long finish = System.currentTimeMillis(); 202 203 long time = finish - session.getRequest().getStartTime().getTime(); 204 205 String wallClock = session.getRequest().getDegreeOfConcurrency() > 1 ? " (Wall Clock)" : ""; 206 207 logger.info( "Total time: " + formatDuration( time ) + wallClock ); 208 209 logger.info( "Finished at: " + formatTimestamp( finish ) ); 210 211 System.gc(); 212 213 Runtime r = Runtime.getRuntime(); 214 215 long mb = 1024 * 1024; 216 217 logger.info( "Final Memory: " + ( r.totalMemory() - r.freeMemory() ) / mb + "M/" + r.totalMemory() / mb + "M" ); 218 } 219 220 @Override 221 public void projectSkipped( ExecutionEvent event ) 222 { 223 if ( logger.isInfoEnabled() ) 224 { 225 logger.info( chars( ' ', LINE_LENGTH ) ); 226 logger.info( chars( '-', LINE_LENGTH ) ); 227 228 logger.info( "Skipping " + event.getProject().getName() ); 229 logger.info( "This project has been banned from the build due to previous failures." ); 230 231 logger.info( chars( '-', LINE_LENGTH ) ); 232 } 233 } 234 235 @Override 236 public void projectStarted( ExecutionEvent event ) 237 { 238 if ( logger.isInfoEnabled() ) 239 { 240 logger.info( chars( ' ', LINE_LENGTH ) ); 241 logger.info( chars( '-', LINE_LENGTH ) ); 242 243 logger.info( "Building " + event.getProject().getName() + " " + event.getProject().getVersion() ); 244 245 logger.info( chars( '-', LINE_LENGTH ) ); 246 } 247 } 248 249 @Override 250 public void mojoSkipped( ExecutionEvent event ) 251 { 252 if ( logger.isWarnEnabled() ) 253 { 254 logger.warn( "Goal " + event.getMojoExecution().getGoal() 255 + " requires online mode for execution but Maven is currently offline, skipping" ); 256 } 257 } 258 259 /** 260 * <pre>--- mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId ---</pre> 261 */ 262 @Override 263 public void mojoStarted( ExecutionEvent event ) 264 { 265 if ( logger.isInfoEnabled() ) 266 { 267 StringBuilder buffer = new StringBuilder( 128 ); 268 269 buffer.append( "--- " ); 270 append( buffer, event.getMojoExecution() ); 271 append( buffer, event.getProject() ); 272 buffer.append( " ---" ); 273 274 logger.info( "" ); 275 logger.info( buffer.toString() ); 276 } 277 } 278 279 /** 280 * <pre>>>> mojo-artifactId:version:goal (mojo-executionId) > :forked-goal @ project-artifactId >>></pre> 281 * <pre>>>> mojo-artifactId:version:goal (mojo-executionId) > [lifecycle]phase @ project-artifactId >>></pre> 282 */ 283 @Override 284 public void forkStarted( ExecutionEvent event ) 285 { 286 if ( logger.isInfoEnabled() ) 287 { 288 StringBuilder buffer = new StringBuilder( 128 ); 289 290 buffer.append( ">>> " ); 291 append( buffer, event.getMojoExecution() ); 292 buffer.append( " > " ); 293 appendForkInfo( buffer, event.getMojoExecution().getMojoDescriptor() ); 294 append( buffer, event.getProject() ); 295 buffer.append( " >>>" ); 296 297 logger.info( "" ); 298 logger.info( buffer.toString() ); 299 } 300 } 301 302 // CHECKSTYLE_OFF: LineLength 303 /** 304 * <pre><<< mojo-artifactId:version:goal (mojo-executionId) < :forked-goal @ project-artifactId <<<</pre> 305 * <pre><<< mojo-artifactId:version:goal (mojo-executionId) < [lifecycle]phase @ project-artifactId <<<</pre> 306 */ 307 // CHECKSTYLE_ON: LineLength 308 @Override 309 public void forkSucceeded( ExecutionEvent event ) 310 { 311 if ( logger.isInfoEnabled() ) 312 { 313 StringBuilder buffer = new StringBuilder( 128 ); 314 315 buffer.append( "<<< " ); 316 append( buffer, event.getMojoExecution() ); 317 buffer.append( " < " ); 318 appendForkInfo( buffer, event.getMojoExecution().getMojoDescriptor() ); 319 append( buffer, event.getProject() ); 320 buffer.append( " <<<" ); 321 322 logger.info( "" ); 323 logger.info( buffer.toString() ); 324 } 325 } 326 327 private void append( StringBuilder buffer, MojoExecution me ) 328 { 329 buffer.append( me.getArtifactId() ).append( ':' ).append( me.getVersion() ); 330 buffer.append( ':' ).append( me.getGoal() ); 331 if ( me.getExecutionId() != null ) 332 { 333 buffer.append( " (" ).append( me.getExecutionId() ).append( ')' ); 334 } 335 } 336 337 private void appendForkInfo( StringBuilder buffer, MojoDescriptor md ) 338 { 339 if ( StringUtils.isNotEmpty( md.getExecutePhase() ) ) 340 { 341 // forked phase 342 if ( StringUtils.isNotEmpty( md.getExecuteLifecycle() ) ) 343 { 344 buffer.append( '[' ); 345 buffer.append( md.getExecuteLifecycle() ); 346 buffer.append( ']' ); 347 } 348 buffer.append( md.getExecutePhase() ); 349 } 350 else 351 { 352 // forked goal 353 buffer.append( ':' ); 354 buffer.append( md.getExecuteGoal() ); 355 } 356 } 357 358 private void append( StringBuilder buffer, MavenProject project ) 359 { 360 buffer.append( " @ " ).append( project.getArtifactId() ); 361 } 362 363 @Override 364 public void forkedProjectStarted( ExecutionEvent event ) 365 { 366 if ( logger.isInfoEnabled() && event.getMojoExecution().getForkedExecutions().size() > 1 ) 367 { 368 logger.info( chars( ' ', LINE_LENGTH ) ); 369 logger.info( chars( '>', LINE_LENGTH ) ); 370 371 logger.info( "Forking " + event.getProject().getName() + " " + event.getProject().getVersion() ); 372 373 logger.info( chars( '>', LINE_LENGTH ) ); 374 } 375 } 376 377}