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; 024import org.apache.maven.execution.AbstractExecutionListener; 025import org.apache.maven.execution.BuildFailure; 026import org.apache.maven.execution.BuildSuccess; 027import org.apache.maven.execution.BuildSummary; 028import org.apache.maven.execution.ExecutionEvent; 029import org.apache.maven.execution.MavenExecutionResult; 030import org.apache.maven.execution.MavenSession; 031import org.apache.maven.plugin.MojoExecution; 032import org.apache.maven.project.MavenProject; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * Logs execution events to logger, eventually user-supplied. 038 * 039 * @author Benjamin Bentmann 040 */ 041public class ExecutionEventLogger 042 extends AbstractExecutionListener 043{ 044 private final Logger logger; 045 046 private static final int LINE_LENGTH = 72; 047 private static final int BUILD_TIME_DURATION_LENGTH = 9; 048 049 public ExecutionEventLogger() 050 { 051 logger = LoggerFactory.getLogger( ExecutionEventLogger.class ); 052 } 053 054 // TODO should we deprecate? 055 public ExecutionEventLogger( Logger logger ) 056 { 057 if ( logger == null ) 058 { 059 throw new IllegalArgumentException( "logger missing" ); 060 } 061 062 this.logger = logger; 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 138 buffer.append( ' ' ); 139 while ( buffer.length() < LINE_LENGTH - 21 ) 140 { 141 buffer.append( '.' ); 142 } 143 buffer.append( ' ' ); 144 145 BuildSummary buildSummary = result.getBuildSummary( project ); 146 147 if ( buildSummary == null ) 148 { 149 buffer.append( "SKIPPED" ); 150 } 151 else if ( buildSummary instanceof BuildSuccess ) 152 { 153 buffer.append( "SUCCESS [" ); 154 String buildTimeDuration = formatDuration( buildSummary.getTime() ); 155 buffer.append( chars( ' ', BUILD_TIME_DURATION_LENGTH - buildTimeDuration.length() ) ); 156 buffer.append( buildTimeDuration ); 157 buffer.append( "]" ); 158 } 159 else if ( buildSummary instanceof BuildFailure ) 160 { 161 buffer.append( "FAILURE [" ); 162 String buildTimeDuration = formatDuration( buildSummary.getTime() ); 163 buffer.append( chars( ' ', BUILD_TIME_DURATION_LENGTH - buildTimeDuration.length() ) ); 164 buffer.append( buildTimeDuration ); 165 buffer.append( "]" ); 166 } 167 168 logger.info( buffer.toString() ); 169 } 170 } 171 172 private void logResult( MavenSession session ) 173 { 174 logger.info( chars( '-', LINE_LENGTH ) ); 175 176 if ( session.getResult().hasExceptions() ) 177 { 178 logger.info( "BUILD FAILURE" ); 179 } 180 else 181 { 182 logger.info( "BUILD SUCCESS" ); 183 } 184 } 185 186 private void logStats( MavenSession session ) 187 { 188 logger.info( chars( '-', LINE_LENGTH ) ); 189 190 long finish = System.currentTimeMillis(); 191 192 long time = finish - session.getRequest().getStartTime().getTime(); 193 194 String wallClock = session.getRequest().getDegreeOfConcurrency() > 1 ? " (Wall Clock)" : ""; 195 196 logger.info( "Total time: " + formatDuration( time ) + wallClock ); 197 198 logger.info( "Finished at: " + formatTimestamp( finish ) ); 199 200 System.gc(); 201 202 Runtime r = Runtime.getRuntime(); 203 204 long MB = 1024 * 1024; 205 206 logger.info( "Final Memory: " + ( r.totalMemory() - r.freeMemory() ) / MB + "M/" + r.totalMemory() / MB + "M" ); 207 } 208 209 @Override 210 public void projectSkipped( ExecutionEvent event ) 211 { 212 if ( logger.isInfoEnabled() ) 213 { 214 logger.info( chars( ' ', LINE_LENGTH ) ); 215 logger.info( chars( '-', LINE_LENGTH ) ); 216 217 logger.info( "Skipping " + event.getProject().getName() ); 218 logger.info( "This project has been banned from the build due to previous failures." ); 219 220 logger.info( chars( '-', LINE_LENGTH ) ); 221 } 222 } 223 224 @Override 225 public void projectStarted( ExecutionEvent event ) 226 { 227 if ( logger.isInfoEnabled() ) 228 { 229 logger.info( chars( ' ', LINE_LENGTH ) ); 230 logger.info( chars( '-', LINE_LENGTH ) ); 231 232 logger.info( "Building " + event.getProject().getName() + " " + event.getProject().getVersion() ); 233 234 logger.info( chars( '-', LINE_LENGTH ) ); 235 } 236 } 237 238 @Override 239 public void mojoSkipped( ExecutionEvent event ) 240 { 241 if ( logger.isWarnEnabled() ) 242 { 243 logger.warn( "Goal " + event.getMojoExecution().getGoal() 244 + " requires online mode for execution but Maven is currently offline, skipping" ); 245 } 246 } 247 248 /** 249 * <pre>--- mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId ---</pre> 250 */ 251 @Override 252 public void mojoStarted( ExecutionEvent event ) 253 { 254 if ( logger.isInfoEnabled() ) 255 { 256 StringBuilder buffer = new StringBuilder( 128 ); 257 258 buffer.append( "--- " ); 259 append( buffer, event.getMojoExecution() ); 260 append( buffer, event.getProject() ); 261 buffer.append( " ---" ); 262 263 logger.info( "" ); 264 logger.info( buffer.toString() ); 265 } 266 } 267 268 /** 269 * <pre>>>> mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId >>></pre> 270 */ 271 @Override 272 public void forkStarted( ExecutionEvent event ) 273 { 274 if ( logger.isInfoEnabled() ) 275 { 276 StringBuilder buffer = new StringBuilder( 128 ); 277 278 buffer.append( ">>> " ); 279 append( buffer, event.getMojoExecution() ); 280 append( buffer, event.getProject() ); 281 buffer.append( " >>>" ); 282 283 logger.info( "" ); 284 logger.info( buffer.toString() ); 285 } 286 } 287 288 /** 289 * <pre><<< mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId <<<</pre> 290 */ 291 @Override 292 public void forkSucceeded( ExecutionEvent event ) 293 { 294 if ( logger.isInfoEnabled() ) 295 { 296 StringBuilder buffer = new StringBuilder( 128 ); 297 298 buffer.append( "<<< " ); 299 append( buffer, event.getMojoExecution() ); 300 append( buffer, event.getProject() ); 301 buffer.append( " <<<" ); 302 303 logger.info( "" ); 304 logger.info( buffer.toString() ); 305 } 306 } 307 308 private void append( StringBuilder buffer, MojoExecution me ) 309 { 310 buffer.append( me.getArtifactId() ).append( ':' ).append( me.getVersion() ); 311 buffer.append( ':' ).append( me.getGoal() ); 312 if ( me.getExecutionId() != null ) 313 { 314 buffer.append( " (" ).append( me.getExecutionId() ).append( ')' ); 315 } 316 } 317 318 private void append( StringBuilder buffer, MavenProject project ) 319 { 320 buffer.append( " @ " ).append( project.getArtifactId() ); 321 } 322 323 @Override 324 public void forkedProjectStarted( ExecutionEvent event ) 325 { 326 if ( logger.isInfoEnabled() && event.getMojoExecution().getForkedExecutions().size() > 1 ) 327 { 328 logger.info( chars( ' ', LINE_LENGTH ) ); 329 logger.info( chars( '>', LINE_LENGTH ) ); 330 331 logger.info( "Forking " + event.getProject().getName() + " " + event.getProject().getVersion() ); 332 333 logger.info( chars( '>', LINE_LENGTH ) ); 334 } 335 } 336 337}