BuildTimeProfiler.java

  1. package com.soebes.maven.extensions;

  2. /*
  3.  * Licensed to the Apache Software Foundation (ASF) under one
  4.  * or more contributor license agreements.  See the NOTICE file
  5.  * distributed with this work for additional information
  6.  * regarding copyright ownership.  The ASF licenses this file
  7.  * to you under the Apache License, Version 2.0 (the
  8.  * "License"); you may not use this file except in compliance
  9.  * with the License.  You may obtain a copy of the License at
  10.  *
  11.  *   http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing,
  14.  * software distributed under the License is distributed on an
  15.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16.  * KIND, either express or implied.  See the License for the
  17.  * specific language governing permissions and limitations
  18.  * under the License.
  19.  */

  20. import com.soebes.maven.extensions.artifact.DeployTimer;
  21. import com.soebes.maven.extensions.artifact.DownloadTimer;
  22. import com.soebes.maven.extensions.artifact.InstallTimer;
  23. import com.soebes.maven.extensions.metadata.MetadataDeploymentTimer;
  24. import com.soebes.maven.extensions.metadata.MetadataDownloadTimer;
  25. import com.soebes.maven.extensions.metadata.MetadataInstallTimer;
  26. import org.apache.maven.execution.ExecutionEvent;
  27. import org.apache.maven.execution.MavenExecutionRequest;
  28. import org.apache.maven.execution.MavenExecutionResult;
  29. import org.apache.maven.project.DependencyResolutionRequest;
  30. import org.apache.maven.project.DependencyResolutionResult;
  31. import org.apache.maven.project.MavenProject;
  32. import org.eclipse.aether.RepositoryEvent;
  33. import org.slf4j.Logger;
  34. import org.slf4j.LoggerFactory;

  35. import javax.inject.Named;
  36. import javax.inject.Singleton;
  37. import java.util.Collections;
  38. import java.util.LinkedList;
  39. import java.util.List;
  40. import java.util.Map;
  41. import java.util.Map.Entry;

  42. /**
  43.  * @author Karl Heinz Marbaise <a href="mailto:kama@soebes.de">kama@soebes.de</a>
  44.  */
  45. @Named
  46. @Singleton
  47. public class BuildTimeProfiler
  48.     extends LifeCycleOrdering
  49. {
  50.     private static final String SEPARATION_LINE = "------------------------------------------------------------------------";

  51.     private static final Logger LOGGER = LoggerFactory.getLogger(BuildTimeProfiler.class);

  52.     private final List<String> lifeCyclePhases;

  53.     private final DiscoveryTimer discoveryTimer;

  54.     private final GoalTimer goalTimer;

  55.     private final MojoTimer mojoTimer;

  56.     private final ProjectTimer projectTimer;

  57.     private final SessionTimer sessionTimer;

  58.     private final InstallTimer installTimer;

  59.     private final DeployTimer deployTimer;

  60.     private final DownloadTimer downloadTimer;

  61.     private final MetadataDownloadTimer metadataDownloadTimer;

  62.     private final MetadataDeploymentTimer metadataDeploymentTimer;

  63.     private final MetadataInstallTimer metadataInstallTimer;

  64.     private final ForkTimer forkTimer;

  65.     private final ProjectTimer forkProject;

  66.     private boolean activated;

  67.     public BuildTimeProfiler()
  68.     {
  69.         LOGGER.debug( "LifeCycleProfiler ctor called." );
  70.         this.lifeCyclePhases = Collections.synchronizedList(new LinkedList<>() );
  71.         this.discoveryTimer = new DiscoveryTimer();
  72.         this.goalTimer = new GoalTimer();
  73.         this.mojoTimer = new MojoTimer();
  74.         this.projectTimer = new ProjectTimer();
  75.         this.sessionTimer = new SessionTimer();
  76.         this.installTimer = new InstallTimer();
  77.         this.deployTimer = new DeployTimer();
  78.         this.downloadTimer = new DownloadTimer();

  79.         this.metadataDownloadTimer = new MetadataDownloadTimer();
  80.         this.metadataDeploymentTimer = new MetadataDeploymentTimer();
  81.         this.metadataInstallTimer = new MetadataInstallTimer();
  82.         this.forkTimer = new ForkTimer();
  83.         this.forkProject = new ProjectTimer();

  84.         String disabled = System.getProperty("maven-build-time-profiler.disabled", "unknown");
  85.         LOGGER.debug("MBTP: maven-build-time-profiler.disabled {}", disabled);
  86.         this.activated = !disabled.equals("true");
  87.     }

  88.     @Override
  89.     public void init( Context context )
  90.         throws Exception
  91.     {
  92.         super.init( context );

  93.         LOGGER.info( "Maven Build Time Profiler started. (Version {})", BuildTimeProfilerVersion.getVersion() );
  94.         if ( !activated ) {
  95.             LOGGER.info("Maven Build Time Profiler deactivated.");
  96.             return;
  97.         }

  98.         // Is this always in the context? Based on Maven Core yes.
  99.         String workingDirectory = (String) context.getData().get( "workingDirectory" );
  100.         LOGGER.debug( "MBTP: workingDirectory: {}", workingDirectory );

  101.         String multiModuleProjectDirectory = (String) context.getData().get( "multiModuleProjectDirectory" );
  102.         LOGGER.debug( "MBTP: multiModuleProjectDirectory: {}", multiModuleProjectDirectory );
  103.     }

  104.     @Override
  105.     public void onEvent( Object event )
  106.         throws Exception
  107.     {
  108.         try
  109.         {
  110.             if ( !activated ) {
  111.                 return;
  112.             }

  113.             if ( event instanceof ExecutionEvent)
  114.             {
  115.                 executionEventHandler( (ExecutionEvent) event );
  116.             }
  117.             else if ( event instanceof org.eclipse.aether.RepositoryEvent )
  118.             {
  119.                 repositoryEventHandler( (RepositoryEvent) event );
  120.             }
  121.             else if ( event instanceof MavenExecutionRequest)
  122.             {
  123.                 executionRequestEventHandler( (MavenExecutionRequest) event );
  124.             }
  125.             else if ( event instanceof MavenExecutionResult)
  126.             {
  127.                 executionResultEventHandler( (MavenExecutionResult) event );
  128.             }
  129.             else if ( event instanceof DependencyResolutionRequest)
  130.             {
  131.                 dependencyResolutionRequest( (DependencyResolutionRequest) event );
  132.             }
  133.             else if ( event instanceof DependencyResolutionResult)
  134.             {
  135.                 dependencyResolutionResult( (DependencyResolutionResult) event );
  136.             }
  137.             // The following event type is available since Maven 3.3.1+
  138.             // else if ( event instanceof DefaultSettingsBuildingRequest) {
  139.             // DefaultSettingsBuildingRequest r = null;
  140.             // r.getGlobalSettingsFile();
  141.             // r.getGlobalSettingsSource();
  142.             // r.getSystemProperties();
  143.             // r.getUserSettingsFile();
  144.             // r.getUserSettingsSource();
  145.             //
  146.             // r.setGlobalSettingsFile( globalSettingsFile );
  147.             // r.setGlobalSettingsSource( globalSettingsSource );
  148.             // r.setSystemProperties( systemProperties );
  149.             // r.setUserProperties( userProperties );
  150.             // r.setUserSettingsFile( userSettingsFile );
  151.             // r.setUserSettingsSource( userSettingsSource );
  152.             // }
  153.             // The following event type is available since Maven 3.3.1+
  154.             // else if (event instanceof DefaultSettingsBuildingRequest) {
  155.             //
  156.             // DefaultSettingsBuildingRequest r = null;
  157.             // r.getGlobalSettingsSource().getLocation()
  158.             // }
  159.             // The following event type is available since Maven 3.3.1+
  160.             // else if (event instanceof DefaultToolchainsBuildingRequest) {
  161.             // DefaultToolchainsBuildingRequest r = null;
  162.             // r.getGlobalToolchainsSource().
  163.             // }
  164.             // The following event type is available since Maven 3.3.1+
  165.             // else if (event instanceof DefaultToolchainsBuildingResult) {
  166.             // DefaultToolchainsBuildingResult r = null;
  167.             // r.getEffectiveToolchains();
  168.             // r.getProblems();
  169.             // }
  170.             else
  171.             {
  172.                 // TODO: What kind of event we haven't considered?
  173.                 LOGGER.debug( "MBTP: Event {}", event.getClass().getCanonicalName() );
  174.             }
  175.         }
  176.         catch ( Exception e )
  177.         {
  178.             LOGGER.error( "MBTP: Exception", e );
  179.         }
  180.     }

  181.     @Override
  182.     public void close()
  183.     {
  184.         if ( !activated ) {
  185.             return;
  186.         }
  187.         LOGGER.debug( "MBTP: done." );
  188.     }

  189.     private void dependencyResolutionResult( DependencyResolutionResult event )
  190.     {
  191.         LOGGER.debug( "MBTP: dependencyResolutionResult() {}", event.getResolvedDependencies().size() );
  192.     }

  193.     private void dependencyResolutionRequest( DependencyResolutionRequest event )
  194.     {
  195.         LOGGER.debug( "MBTP: dependencyResolutionRequest()" );
  196.     }

  197.     private void repositoryEventHandler( org.eclipse.aether.RepositoryEvent repositoryEvent )
  198.     {
  199.         RepositoryEvent.EventType type = repositoryEvent.getType();
  200.         switch ( type )
  201.         {
  202.             case ARTIFACT_DOWNLOADING:
  203.                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
  204.                 downloadTimer.start( repositoryEvent );
  205.                 break;
  206.             case ARTIFACT_DOWNLOADED:
  207.                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
  208.                 downloadTimer.stop( repositoryEvent );
  209.                 break;

  210.             case ARTIFACT_DEPLOYING:
  211.                 deployTimer.start( repositoryEvent );
  212.                 break;
  213.             case ARTIFACT_DEPLOYED:
  214.                 deployTimer.stop( repositoryEvent );
  215.                 break;

  216.             case ARTIFACT_INSTALLING:
  217.                 installTimer.start( repositoryEvent );
  218.                 break;
  219.             case ARTIFACT_INSTALLED:
  220.                 installTimer.stop( repositoryEvent );
  221.                 break;

  222.             case METADATA_DEPLOYING:
  223.                 metadataDeploymentTimer.start( repositoryEvent );
  224.                 break;
  225.             case METADATA_DEPLOYED:
  226.                 metadataDeploymentTimer.stop( repositoryEvent );
  227.                 break;

  228.             case METADATA_DOWNLOADING:
  229.                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
  230.                 metadataDownloadTimer.start( repositoryEvent );
  231.                 break;
  232.             case METADATA_DOWNLOADED:
  233.                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
  234.                 metadataDownloadTimer.stop( repositoryEvent );
  235.                 break;

  236.             case METADATA_INSTALLING:
  237.                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
  238.                 metadataInstallTimer.start( repositoryEvent );
  239.                 break;
  240.             case METADATA_INSTALLED:
  241.                 LOGGER.debug( "MBTP: repositoryEventHandler {}", type );
  242.                 metadataInstallTimer.stop( repositoryEvent );
  243.                 break;

  244.             case ARTIFACT_RESOLVING:
  245.             case ARTIFACT_RESOLVED:
  246.             case ARTIFACT_DESCRIPTOR_INVALID:
  247.             case ARTIFACT_DESCRIPTOR_MISSING:
  248.             case METADATA_RESOLVED:
  249.             case METADATA_RESOLVING:
  250.             case METADATA_INVALID:
  251.                 // Those events are not recorded.
  252.                 break;

  253.             default:
  254.                 LOGGER.error( "MBTP: repositoryEventHandler {}", type );
  255.                 break;
  256.         }
  257.     }

  258.     private void executionEventHandler( ExecutionEvent executionEvent )
  259.     {
  260.         if ( !activated ) {
  261.             return;
  262.         }
  263.         LOGGER.debug( "executionEventHandler: {}", executionEvent.getType() );
  264.         ExecutionEvent.Type type = executionEvent.getType();
  265.         switch ( type )
  266.         {
  267.             case ProjectDiscoveryStarted:
  268.                 // Start reading the pom files..
  269.                 discoveryTimer.discoveryStart();
  270.                 break;
  271.             case SessionStarted:
  272.                 // Reading of pom files done and structure now there.
  273.                 discoveryTimer.discoveryStop();
  274.                 sessionTimer.sessionStart();
  275.                 break;
  276.             case SessionEnded:
  277.                 // Everything is done.
  278.                 LOGGER.info( "MBTP: Session Ended");
  279.                 sessionTimer.sessionStop();
  280.                 break;

  281.             case ForkStarted:
  282.                 forkTimer.start();
  283.                 break;
  284.             case ForkFailed:
  285.             case ForkSucceeded:
  286.                 forkTimer.stop();
  287.                 break;

  288.             case ForkedProjectStarted:
  289.                 forkProject.projectStart( executionEvent );
  290.                 break;
  291.             case ForkedProjectFailed:
  292.             case ForkedProjectSucceeded:
  293.                 forkProject.projectStop( executionEvent );
  294.                 break;

  295.             case MojoStarted:
  296.                 String phaseStart = executionEvent.getMojoExecution().getLifecyclePhase();
  297.                 // Key: phase, project, mojo
  298.                 if ( phaseStart == null )
  299.                 {
  300.                     goalTimer.mojoStart( executionEvent );
  301.                 }
  302.                 else
  303.                 {
  304.                     collectAllLifeCyclePhases( phaseStart );
  305.                     mojoTimer.mojoStart( executionEvent );
  306.                 }
  307.                 break;

  308.             case MojoFailed:
  309.             case MojoSucceeded:
  310.             case MojoSkipped:
  311.                 String phaseStop = executionEvent.getMojoExecution().getLifecyclePhase();
  312.                 if ( phaseStop == null )
  313.                 {
  314.                     goalTimer.mojoStop( executionEvent );
  315.                 }
  316.                 else
  317.                 {
  318.                     mojoTimer.mojoStop( executionEvent );
  319.                 }
  320.                 break;

  321.             case ProjectStarted:
  322.                 projectTimer.projectStart( executionEvent );
  323.                 break;

  324.             case ProjectFailed:
  325.             case ProjectSucceeded:
  326.             case ProjectSkipped:
  327.                 projectTimer.projectStop( executionEvent );
  328.                 break;

  329.             default:
  330.                 LOGGER.error( "MBTP: executionEventHandler: {}", type );
  331.                 break;
  332.         }

  333.     }

  334.     private void executionRequestEventHandler( MavenExecutionRequest event )
  335.     {
  336.         LOGGER.debug( "MBTP: executionRequestEventHandler: {}", event.getExecutionListener() );
  337.     }

  338.     private void executionResultEventHandler( MavenExecutionResult event )
  339.     {
  340.         LOGGER.debug( "MBTP: executionResultEventHandler: {}", event.getProject() );
  341.         LOGGER.info( "--             Maven Build Time Profiler Summary                      --" );
  342.         LOGGER.info(SEPARATION_LINE);

  343.         discoveryTimer.report();

  344.         if ( mojoTimer.hasEvents() )
  345.         {
  346.             LOGGER.info( "Project Build Time (reactor order):" );
  347.             LOGGER.info( "" );
  348.             for ( MavenProject mavenProject : event.getTopologicallySortedProjects() )
  349.             {
  350.                 LOGGER.info( "{}:", mavenProject.getName() );

  351.                 for ( String phase : lifeCyclePhases )
  352.                 {
  353.                     ProjectKey projectKey = mavenProjectToProjectKey( mavenProject );

  354.                     if ( !mojoTimer.hasTimeForProjectAndPhase( projectKey, phase ) )
  355.                     {
  356.                         continue;
  357.                     }

  358.                     long timeForPhaseAndProjectInMillis =
  359.                         mojoTimer.getTimeForProjectAndPhaseInMillis( projectKey, phase );
  360.                     String formattedTime = String.format("%8d", timeForPhaseAndProjectInMillis);
  361.                     LOGGER.info( "    {} ms : {}", formattedTime, phase );

  362.                 }

  363.             }
  364.             LOGGER.info(SEPARATION_LINE);
  365.             LOGGER.info( "Lifecycle Phase summary:" );
  366.             LOGGER.info( "" );
  367.             for ( String phase : lifeCyclePhases )
  368.             {
  369.                 long timeForPhaseInMillis = mojoTimer.getTimeForPhaseInMillis( phase );
  370.                 LOGGER.info( "{} ms : {}", String.format( "%8d", timeForPhaseInMillis ), phase );
  371.             }

  372.             // List all plugins per phase
  373.             LOGGER.info(SEPARATION_LINE);
  374.             LOGGER.info( "Plugins in lifecycle Phases:" );
  375.             LOGGER.info( "" );
  376.             for ( String phase : lifeCyclePhases )
  377.             {
  378.                 LOGGER.info( "{}:", phase );
  379.                 Map<ProjectMojo, SystemTime> plugisInPhase = mojoTimer.getPluginsInPhase( phase );
  380.                 for ( Entry<ProjectMojo, SystemTime> pluginInPhase : plugisInPhase.entrySet() )
  381.                 {
  382.                     LOGGER.info( "{} ms: {}", String.format( "%8d", pluginInPhase.getValue().getElapsedTime() ),
  383.                                  pluginInPhase.getKey().getMojo().getFullId() );
  384.                 }

  385.             }

  386.             LOGGER.info( "" );
  387.             long timeForPlugins = mojoTimer.getTimeForPlugins();
  388.             String formattedTime = String.format("%8d", timeForPlugins);
  389.             LOGGER.info("{} ms plugins execution summary.", formattedTime);
  390.             LOGGER.info(SEPARATION_LINE);
  391.         }

  392.         if ( goalTimer.hasEvents() )
  393.         {
  394.             LOGGER.info( "Plugins directly called via goals:" );
  395.             LOGGER.info( "" );
  396.             goalTimer.report();
  397.             LOGGER.info(SEPARATION_LINE);
  398.         }

  399.         installTimer.report();
  400.         downloadTimer.report();
  401.         deployTimer.report();
  402.         metadataInstallTimer.report();
  403.         metadataDownloadTimer.report();
  404.         metadataDeploymentTimer.report();

  405.         forkTimer.report();
  406.         forkProject.report();
  407.     }

  408.     private ProjectKey mavenProjectToProjectKey( MavenProject project )
  409.     {
  410.         return new ProjectKey( project.getGroupId(), project.getArtifactId(), project.getVersion() );
  411.     }

  412.     private void collectAllLifeCyclePhases(String phase )
  413.     {
  414.         // Phase can be null if you call maven via:
  415.         // mvn site:stage (no life cycle started.)
  416.         if ( phase == null )
  417.         {
  418.             return;
  419.         }
  420.         LOGGER.debug( "collectAllLifeCyclePhases({})", phase );
  421.         if ( !lifeCyclePhases.contains( phase ) )
  422.         {
  423.             lifeCyclePhases.add( phase );
  424.         }
  425.     }

  426. }