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