You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2019/12/18 22:15:55 UTC

[maven] 01/12: Log fail-level option. Submitted by: Luc Klaassen and Martin Kanters.

This is an automated email from the ASF dual-hosted git repository.

rfscholte pushed a commit to branch MNG-6065
in repository https://gitbox.apache.org/repos/asf/maven.git

commit dbd651c358f1f52804e3e21f6fe36e5b248bbaa0
Author: Martin Kanters <mk...@gmail.com>
AuthorDate: Wed Oct 2 20:55:21 2019 +0200

    Log fail-level option. Submitted by: Luc Klaassen and Martin Kanters.
    
    Squashed work from Luc Klaassen and me.
---
 .../org/slf4j/impl/MavenLoggerFactoryTest.java     |  99 +++++++++++++++
 maven-embedder/pom.xml                             |   4 +
 .../main/java/org/apache/maven/cli/CLIManager.java |   3 +
 .../main/java/org/apache/maven/cli/MavenCli.java   |  16 ++-
 .../maven/cli/event/ExecutionEventLogger.java      |  25 +++-
 .../META-INF/maven/slf4j-configuration.properties  |   2 +-
 maven-slf4j-provider/pom.xml                       |   4 +
 .../java/org/slf4j/impl/MavenFailLevelLogger.java  | 141 +++++++++++++++++++++
 ...Factory.java => MavenFailLevelLoggerState.java} |  37 +++---
 .../java/org/slf4j/impl/MavenLoggerFactory.java    |  91 +++++++++++++
 .../java/org/slf4j/impl/MavenSimpleLogger.java     |  14 +-
 .../java/org/slf4j/impl/StaticLoggerBinder.java    |   4 +-
 maven-slf4j-wrapper/pom.xml                        |  45 +++++++
 .../maven/logwrapper/MavenSlf4jWrapperFactory.java |  29 +----
 maven-slf4j-wrapper/src/site/site.xml              |  38 ++++++
 pom.xml                                            |  11 +-
 16 files changed, 506 insertions(+), 57 deletions(-)

diff --git a/apache-maven/src/test/java/org/slf4j/impl/MavenLoggerFactoryTest.java b/apache-maven/src/test/java/org/slf4j/impl/MavenLoggerFactoryTest.java
new file mode 100644
index 0000000..cc42172
--- /dev/null
+++ b/apache-maven/src/test/java/org/slf4j/impl/MavenLoggerFactoryTest.java
@@ -0,0 +1,99 @@
+package org.slf4j.impl;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.junit.Test;
+import org.slf4j.Logger;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.*;
+
+public class MavenLoggerFactoryTest
+{
+    @Test
+    public void createsSimpleLogger()
+    {
+        MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
+
+        Logger logger = mavenLoggerFactory.getLogger( "Test" );
+
+        assertThat( logger, instanceOf( MavenSimpleLogger.class ) );
+    }
+
+    @Test
+    public void loggerCachingWorks()
+    {
+        MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
+
+        Logger logger = mavenLoggerFactory.getLogger( "Test" );
+        Logger logger2 = mavenLoggerFactory.getLogger( "Test" );
+        Logger differentLogger = mavenLoggerFactory.getLogger( "TestWithDifferentName" );
+
+        assertNotNull( logger );
+        assertNotNull( differentLogger );
+        assertSame( logger, logger2 );
+        assertNotSame( logger, differentLogger );
+    }
+
+    @Test
+    public void createsFailLevelLogger()
+    {
+        MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
+        mavenLoggerFactory.breakOnLogsOfLevel( "WARN" );
+
+        Logger logger = mavenLoggerFactory.getLogger( "Test" );
+
+        assertThat( logger, instanceOf( MavenFailLevelLogger.class ) );
+    }
+
+    @Test
+    public void reportsWhenFailLevelHasBeenHit()
+    {
+        MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
+        mavenLoggerFactory.breakOnLogsOfLevel( "ERROR" );
+
+        MavenFailLevelLogger logger = ( MavenFailLevelLogger ) mavenLoggerFactory.getLogger( "Test" );
+        assertFalse( mavenLoggerFactory.threwLogsOfBreakingLevel() );
+
+        logger.warn( "This should not hit the fail level" );
+        assertFalse( mavenLoggerFactory.threwLogsOfBreakingLevel() );
+
+        logger.error( "This should hit the fail level" );
+        assertTrue( mavenLoggerFactory.threwLogsOfBreakingLevel() );
+
+        logger.warn( "This should not reset the fail level" );
+        assertTrue( mavenLoggerFactory.threwLogsOfBreakingLevel() );
+    }
+
+    @Test( expected = IllegalStateException.class )
+    public void failLevelThresholdCanOnlyBeSetOnce()
+    {
+        MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
+        mavenLoggerFactory.breakOnLogsOfLevel( "WARN" );
+        mavenLoggerFactory.breakOnLogsOfLevel( "ERROR" );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void onlyWarningOrHigherFailLevelsCanBeSet()
+    {
+        MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
+        mavenLoggerFactory.breakOnLogsOfLevel( "INFO" );
+    }
+}
\ No newline at end of file
diff --git a/maven-embedder/pom.xml b/maven-embedder/pom.xml
index 235ae62..1d42f14 100644
--- a/maven-embedder/pom.xml
+++ b/maven-embedder/pom.xml
@@ -75,6 +75,10 @@ under the License.
       <artifactId>maven-shared-utils</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-slf4j-wrapper</artifactId>
+    </dependency>
+    <dependency>
       <groupId>com.google.inject</groupId>
       <artifactId>guice</artifactId>
       <classifier>no_aop</classifier>
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
index 1e95010..ea67fde 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
@@ -77,6 +77,8 @@ public class CLIManager
 
     public static final String FAIL_FAST = "ff";
 
+    public static final String FAIL_LEVEL = "fl";
+
     public static final String FAIL_AT_END = "fae";
 
     public static final String FAIL_NEVER = "fn";
@@ -128,6 +130,7 @@ public class CLIManager
         options.addOption( Option.builder( ALTERNATE_GLOBAL_SETTINGS ).longOpt( "global-settings" ).desc( "Alternate path for the global settings file" ).hasArg().build() );
         options.addOption( Option.builder( Character.toString( ALTERNATE_USER_TOOLCHAINS ) ).longOpt( "toolchains" ).desc( "Alternate path for the user toolchains file" ).hasArg().build() );
         options.addOption( Option.builder( ALTERNATE_GLOBAL_TOOLCHAINS ).longOpt( "global-toolchains" ).desc( "Alternate path for the global toolchains file" ).hasArg().build() );
+        options.addOption( Option.builder( FAIL_LEVEL ).longOpt( "fail-level" ).desc( "Configure which level of logging should cause the build to fail" ).hasArgs().build() );
         options.addOption( Option.builder( FAIL_FAST ).longOpt( "fail-fast" ).desc( "Stop at first failure in reactorized builds" ).build() );
         options.addOption( Option.builder( FAIL_AT_END ).longOpt( "fail-at-end" ).desc( "Only fail the build afterwards; allow all non-impacted builds to continue" ).build() );
         options.addOption( Option.builder( FAIL_NEVER ).longOpt( "fail-never" ).desc( "NEVER fail the build, regardless of project result" ).build() );
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
index 7d17e18..de8a725 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
@@ -58,6 +58,7 @@ import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
 import org.apache.maven.extension.internal.CoreExports;
 import org.apache.maven.extension.internal.CoreExtensionEntry;
 import org.apache.maven.lifecycle.LifecycleExecutionException;
+import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
 import org.apache.maven.model.building.ModelProcessor;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.properties.internal.EnvironmentUtils;
@@ -542,6 +543,17 @@ public class MavenCli
 
         plexusLoggerManager = new Slf4jLoggerManager();
         slf4jLogger = slf4jLoggerFactory.getLogger( this.getClass().getName() );
+
+        if ( cliRequest.commandLine.hasOption( CLIManager.FAIL_LEVEL ) )
+        {
+            String logLevelToBreakOn = cliRequest.commandLine.getOptionValue( CLIManager.FAIL_LEVEL );
+
+            if ( slf4jLoggerFactory instanceof MavenSlf4jWrapperFactory )
+            {
+                ( (MavenSlf4jWrapperFactory) slf4jLoggerFactory ).breakOnLogsOfLevel( logLevelToBreakOn );
+                slf4jLogger.info( "Enabled to break the build on log level {}.", logLevelToBreakOn );
+            }
+        }
     }
 
     private void version( CliRequest cliRequest )
@@ -1343,6 +1355,8 @@ public class MavenCli
         // this is the default behavior.
         String reactorFailureBehaviour = MavenExecutionRequest.REACTOR_FAIL_FAST;
 
+        slf4jLoggerFactory = LoggerFactory.getILoggerFactory();
+
         if ( commandLine.hasOption( CLIManager.NON_RECURSIVE ) )
         {
             recursive = false;
@@ -1630,7 +1644,7 @@ public class MavenCli
         if ( commandLine.hasOption( CLIManager.SET_SYSTEM_PROPERTY ) )
         {
             String[] defStrs = commandLine.getOptionValues( CLIManager.SET_SYSTEM_PROPERTY );
-
+            
             if ( defStrs != null )
             {
                 for ( String defStr : defStrs )
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
index 17da655..f7088a7 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
@@ -33,11 +33,13 @@ import org.apache.maven.execution.BuildSummary;
 import org.apache.maven.execution.ExecutionEvent;
 import org.apache.maven.execution.MavenExecutionResult;
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.shared.utils.logging.MessageBuilder;
 import org.codehaus.plexus.util.StringUtils;
+import org.slf4j.ILoggerFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,8 +48,7 @@ import org.slf4j.LoggerFactory;
  *
  * @author Benjamin Bentmann
  */
-public class ExecutionEventLogger
-    extends AbstractExecutionListener
+public class ExecutionEventLogger extends AbstractExecutionListener
 {
     private final Logger logger;
 
@@ -133,6 +134,18 @@ public class ExecutionEventLogger
                 logReactorSummary( event.getSession() );
             }
 
+            ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
+
+            if ( iLoggerFactory instanceof MavenSlf4jWrapperFactory )
+            {
+                if ( ( (MavenSlf4jWrapperFactory) iLoggerFactory ).threwLogsOfBreakingLevel() )
+                {
+                    event.getSession().getResult().addException( new Exception(
+                            "Build failed due to log statements above WARN. "
+                                    + "Fix the logged issues or remove flag --fail-level (-fl)." ) );
+                }
+            }
+
             logResult( event.getSession() );
 
             logStats( event.getSession() );
@@ -298,16 +311,16 @@ public class ExecutionEventLogger
 
             // -------< groupId:artifactId >-------
             String projectKey = project.getGroupId() + ':' + project.getArtifactId();
-            
-            final String preHeader  = "--< ";
+
+            final String preHeader = "--< ";
             final String postHeader = " >--";
 
             final int headerLen = preHeader.length() + projectKey.length() + postHeader.length();
 
             String prefix = chars( '-', Math.max( 0, ( LINE_LENGTH - headerLen ) / 2 ) ) + preHeader;
 
-            String suffix = postHeader
-                + chars( '-', Math.max( 0, LINE_LENGTH - headerLen - prefix.length() + preHeader.length() ) );
+            String suffix = postHeader + chars( '-',
+                    Math.max( 0, LINE_LENGTH - headerLen - prefix.length() + preHeader.length() ) );
 
             logger.info( buffer().strong( prefix ).project( projectKey ).strong( suffix ).toString() );
 
diff --git a/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties b/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties
index ff865bc..23b18c0 100644
--- a/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties
+++ b/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties
@@ -18,6 +18,6 @@
 # key = Slf4j effective logger factory implementation
 # value = corresponding o.a.m.cli.logging.Slf4jConfiguration class
 org.slf4j.impl.SimpleLoggerFactory org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration
-org.slf4j.impl.MavenSimpleLoggerFactory org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration
+org.slf4j.impl.MavenLoggerFactory org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration
 org.apache.logging.slf4j.Log4jLoggerFactory org.apache.maven.cli.logging.impl.Log4j2Configuration
 ch.qos.logback.classic.LoggerContext org.apache.maven.cli.logging.impl.LogbackConfiguration
diff --git a/maven-slf4j-provider/pom.xml b/maven-slf4j-provider/pom.xml
index defe2dd..7af239c 100644
--- a/maven-slf4j-provider/pom.xml
+++ b/maven-slf4j-provider/pom.xml
@@ -45,6 +45,10 @@ under the License.
       <groupId>org.apache.maven.shared</groupId>
       <artifactId>maven-shared-utils</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-slf4j-wrapper</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenFailLevelLogger.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenFailLevelLogger.java
new file mode 100644
index 0000000..7fde35a
--- /dev/null
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenFailLevelLogger.java
@@ -0,0 +1,141 @@
+package org.slf4j.impl;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.slf4j.event.Level;
+
+/**
+ * A proxy which enhances the MavenSimpleLogger with functionality to track whether a logging threshold is hit.
+ * Currently only support WARN and ERROR states, since it's been used for the --fail-level flag.
+ */
+public class MavenFailLevelLogger extends MavenSimpleLogger
+{
+    private final MavenFailLevelLoggerState failLevelLoggerState;
+
+    MavenFailLevelLogger( String name, MavenFailLevelLoggerState failLevelLoggerState )
+    {
+        super( name );
+        this.failLevelLoggerState = failLevelLoggerState;
+    }
+
+    /**
+     * A simple implementation which always logs messages of level WARN
+     * according to the format outlined above.
+     */
+    @Override
+    public void warn( String msg )
+    {
+        super.warn( msg );
+        failLevelLoggerState.recordLogLevel( Level.WARN );
+    }
+
+    /**
+     * Perform single parameter substitution before logging the message of level
+     * WARN according to the format outlined above.
+     */
+    @Override
+    public void warn( String format, Object arg )
+    {
+        super.warn( format, arg );
+        failLevelLoggerState.recordLogLevel( Level.WARN );
+    }
+
+    /**
+     * Perform double parameter substitution before logging the message of level
+     * WARN according to the format outlined above.
+     */
+    @Override
+    public void warn( String format, Object arg1, Object arg2 )
+    {
+        super.warn( format, arg1, arg2 );
+        failLevelLoggerState.recordLogLevel( Level.WARN );
+    }
+
+    /**
+     * Perform double parameter substitution before logging the message of level
+     * WARN according to the format outlined above.
+     */
+    @Override
+    public void warn( String format, Object... argArray )
+    {
+        super.warn( format, argArray );
+        failLevelLoggerState.recordLogLevel( Level.WARN );
+    }
+
+    /** Log a message of level WARN, including an exception. */
+    @Override
+    public void warn( String msg, Throwable t )
+    {
+        super.warn( msg, t );
+        failLevelLoggerState.recordLogLevel( Level.WARN );
+    }
+
+    /**
+     * A simple implementation which always logs messages of level ERROR
+     * according to the format outlined above.
+     */
+    @Override
+    public void error( String msg )
+    {
+        super.error( msg );
+        failLevelLoggerState.recordLogLevel( Level.ERROR );
+    }
+
+    /**
+     * Perform single parameter substitution before logging the message of level
+     * ERROR according to the format outlined above.
+     */
+    @Override
+    public void error( String format, Object arg )
+    {
+        super.error( format, arg );
+        failLevelLoggerState.recordLogLevel( Level.ERROR );
+    }
+
+    /**
+     * Perform double parameter substitution before logging the message of level
+     * ERROR according to the format outlined above.
+     */
+    @Override
+    public void error( String format, Object arg1, Object arg2 )
+    {
+        super.error( format, arg1, arg2 );
+        failLevelLoggerState.recordLogLevel( Level.ERROR );
+    }
+
+    /**
+     * Perform double parameter substitution before logging the message of level
+     * ERROR according to the format outlined above.
+     */
+    @Override
+    public void error( String format, Object... argArray )
+    {
+        super.error( format, argArray );
+        failLevelLoggerState.recordLogLevel( Level.ERROR );
+    }
+
+    /** Log a message of level ERROR, including an exception. */
+    @Override
+    public void error( String msg, Throwable t )
+    {
+        super.error( msg, t );
+        failLevelLoggerState.recordLogLevel( Level.ERROR );
+    }
+}
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLoggerFactory.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenFailLevelLoggerState.java
similarity index 58%
copy from maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLoggerFactory.java
copy to maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenFailLevelLoggerState.java
index 4adfdbe..ba6ecdb 100644
--- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLoggerFactory.java
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenFailLevelLoggerState.java
@@ -19,29 +19,32 @@ package org.slf4j.impl;
  * under the License.
  */
 
-import org.slf4j.Logger;
+import org.slf4j.event.Level;
 
 /**
- * MavenSimpleLoggerFactory
+ * Responsible for keeping state of whether the threshold of the --fail-level flag has been hit.
  */
-public class MavenSimpleLoggerFactory
-    extends SimpleLoggerFactory
+class MavenFailLevelLoggerState
 {
-    /**
-     * Return an appropriate {@link MavenSimpleLogger} instance by name.
-     */
-    public Logger getLogger( String name )
+    private final Level logThreshold;
+    private boolean thresholdHit = false;
+
+    MavenFailLevelLoggerState( Level logLevel )
     {
-        Logger simpleLogger = loggerMap.get( name );
-        if ( simpleLogger != null )
-        {
-            return simpleLogger;
-        }
-        else
+        assert logLevel != null;
+        this.logThreshold = logLevel;
+    }
+
+    void recordLogLevel( Level logLevel )
+    {
+        if ( !thresholdHit && logLevel.toInt() >= logThreshold.toInt() )
         {
-            Logger newInstance = new MavenSimpleLogger( name );
-            Logger oldInstance = loggerMap.putIfAbsent( name, newInstance );
-            return oldInstance == null ? newInstance : oldInstance;
+            thresholdHit = true;
         }
     }
+
+    boolean threwLogsOfBreakingLevel()
+    {
+        return thresholdHit;
+    }
 }
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenLoggerFactory.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenLoggerFactory.java
new file mode 100644
index 0000000..612a11f
--- /dev/null
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenLoggerFactory.java
@@ -0,0 +1,91 @@
+package org.slf4j.impl;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
+import org.slf4j.Logger;
+import org.slf4j.event.Level;
+
+/**
+ * LogFactory for Maven which can create a simple logger or a one which, if set, fails the build on a threshold.
+ */
+public class MavenLoggerFactory extends SimpleLoggerFactory implements MavenSlf4jWrapperFactory
+{
+    private MavenFailLevelLoggerState failLevelLoggerState = null;
+
+    @Override
+    public void breakOnLogsOfLevel( String logLevelToBreakOn )
+    {
+        if ( failLevelLoggerState != null )
+        {
+            throw new IllegalStateException( "Maven logger fail level has already been set." );
+        }
+
+        Level level = Level.valueOf( logLevelToBreakOn );
+        if ( level.toInt() < Level.WARN.toInt() )
+        {
+            throw new IllegalArgumentException( "Logging level thresholds can only be set to WARN or ERROR" );
+        }
+
+        failLevelLoggerState = new MavenFailLevelLoggerState( level );
+    }
+
+    @Override
+    public boolean threwLogsOfBreakingLevel()
+    {
+        if ( failLevelLoggerState != null )
+        {
+            return failLevelLoggerState.threwLogsOfBreakingLevel();
+        }
+
+        return false;
+    }
+
+    /**
+     * Return an appropriate {@link MavenSimpleLogger} instance by name.
+     */
+    @Override
+    public Logger getLogger( String name )
+    {
+        Logger simpleLogger = loggerMap.get( name );
+        if ( simpleLogger != null )
+        {
+            return simpleLogger;
+        }
+        else
+        {
+            Logger newInstance = getNewLoggingInstance( name );
+            Logger oldInstance = loggerMap.putIfAbsent( name, newInstance );
+            return oldInstance == null ? newInstance : oldInstance;
+        }
+    }
+
+    private Logger getNewLoggingInstance( String name )
+    {
+        if ( failLevelLoggerState == null )
+        {
+            return new MavenSimpleLogger( name );
+        }
+        else
+        {
+            return new MavenFailLevelLogger( name, failLevelLoggerState );
+        }
+    }
+}
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
index 9366687..767220d 100644
--- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
@@ -19,18 +19,18 @@ package org.slf4j.impl;
  * under the License.
  */
 
-import static org.apache.maven.shared.utils.logging.MessageUtils.level;
-import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
-
 import java.io.PrintStream;
 
+import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.apache.maven.shared.utils.logging.MessageUtils.level;
+
 /**
- * Logger for Maven, that support colorization of levels and stacktraces.
- * This class implements 2 methods introduced in slf4j-simple provider local copy.
+ * Logger for Maven, that support colorization of levels and stacktraces. This class implements 2 methods introduced in
+ * slf4j-simple provider local copy.
+ *
  * @since 3.5.0
  */
-public class MavenSimpleLogger
-    extends SimpleLogger
+public class MavenSimpleLogger extends SimpleLogger
 {
     MavenSimpleLogger( String name )
     {
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/StaticLoggerBinder.java
index ba01d83..ed94c70 100644
--- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/StaticLoggerBinder.java
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/StaticLoggerBinder.java
@@ -40,7 +40,7 @@ public final class StaticLoggerBinder
     @SuppressWarnings( { "checkstyle:staticvariablename", "checkstyle:visibilitymodifier" } )
     public static String REQUESTED_API_VERSION = "1.7.25"; // !final
 
-    private static final String LOGGER_FACTORY_CLASS_STR = MavenSimpleLoggerFactory.class.getName();
+    private static final String LOGGER_FACTORY_CLASS_STR = MavenLoggerFactory.class.getName();
 
     /**
      * The unique instance of this class.
@@ -58,7 +58,7 @@ public final class StaticLoggerBinder
      */
     private StaticLoggerBinder()
     {
-        loggerFactory = new MavenSimpleLoggerFactory();
+        loggerFactory = new MavenLoggerFactory();
     }
 
     /**
diff --git a/maven-slf4j-wrapper/pom.xml b/maven-slf4j-wrapper/pom.xml
new file mode 100644
index 0000000..02481a3
--- /dev/null
+++ b/maven-slf4j-wrapper/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven</groupId>
+    <artifactId>maven</artifactId>
+    <version>3.6.3-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>maven-slf4j-wrapper</artifactId>
+
+  <name>Maven SLF4J Wrapper</name>
+  <!-- TODO: Fill in a more detailed description.. -->
+  <description>
+    Maven SLF4J Wrapper.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLoggerFactory.java b/maven-slf4j-wrapper/src/main/java/org/apache/maven/logwrapper/MavenSlf4jWrapperFactory.java
similarity index 54%
rename from maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLoggerFactory.java
rename to maven-slf4j-wrapper/src/main/java/org/apache/maven/logwrapper/MavenSlf4jWrapperFactory.java
index 4adfdbe..92e6b0c 100644
--- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLoggerFactory.java
+++ b/maven-slf4j-wrapper/src/main/java/org/apache/maven/logwrapper/MavenSlf4jWrapperFactory.java
@@ -1,4 +1,4 @@
-package org.slf4j.impl;
+package org.apache.maven.logwrapper;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,29 +19,14 @@ package org.slf4j.impl;
  * under the License.
  */
 
-import org.slf4j.Logger;
+import org.slf4j.ILoggerFactory;
 
 /**
- * MavenSimpleLoggerFactory
+ * Wrapper for creating loggers which can have a log level threshold.
  */
-public class MavenSimpleLoggerFactory
-    extends SimpleLoggerFactory
+public interface MavenSlf4jWrapperFactory extends ILoggerFactory
 {
-    /**
-     * Return an appropriate {@link MavenSimpleLogger} instance by name.
-     */
-    public Logger getLogger( String name )
-    {
-        Logger simpleLogger = loggerMap.get( name );
-        if ( simpleLogger != null )
-        {
-            return simpleLogger;
-        }
-        else
-        {
-            Logger newInstance = new MavenSimpleLogger( name );
-            Logger oldInstance = loggerMap.putIfAbsent( name, newInstance );
-            return oldInstance == null ? newInstance : oldInstance;
-        }
-    }
+    boolean threwLogsOfBreakingLevel();
+
+    void breakOnLogsOfLevel( String logLevelToBreakOn );
 }
diff --git a/maven-slf4j-wrapper/src/site/site.xml b/maven-slf4j-wrapper/src/site/site.xml
new file mode 100644
index 0000000..e475330
--- /dev/null
+++ b/maven-slf4j-wrapper/src/site/site.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.8.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/DECORATION/1.8.0 http://maven.apache.org/xsd/decoration-1.8.0.xsd">
+
+  <edit>${project.scm.url}</edit>
+
+  <body>
+    <menu name="Overview">
+      <item name="Introduction" href="index.html"/>
+      <item name="JavaDocs" href="apidocs/index.html"/>
+      <item name="Source Xref" href="xref/index.html"/>
+      <!--item name="FAQ" href="faq.html"/-->
+    </menu>
+
+    <menu ref="parent"/>
+    <menu ref="reports"/>
+  </body>
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0a1c92f..64c98c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,6 +92,7 @@ under the License.
     <module>maven-resolver-provider</module>
     <module>maven-repository-metadata</module>
     <module>maven-slf4j-provider</module>
+    <module>maven-slf4j-wrapper</module>
     <module>maven-embedder</module>
     <module>maven-compat</module>
     <module>apache-maven</module>
@@ -157,7 +158,10 @@ under the License.
       <name>Mike Mol (MNG-6665)</name>
     </contributor>
     <contributor>
-      <name>Martin Kanters (MNG-6665)</name>
+      <name>Martin Kanters (MNG-6665, MNG-6065)</name>
+    </contributor>
+    <contributor>
+      <name>Luc Klaassen (MNG-6065)</name>
     </contributor>
   </contributors>
 
@@ -232,6 +236,11 @@ under the License.
         <artifactId>maven-slf4j-provider</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.maven</groupId>
+        <artifactId>maven-slf4j-wrapper</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <!--bootstrap-end-comment-->
       <!--  Plexus -->
       <dependency>