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>&lt;&lt;&lt; mojo-artifactId:version:goal (mojo-executionId) @ project-artifactId &lt;&lt;&lt;</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}