You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by tj...@apache.org on 2020/08/12 14:57:16 UTC

[felix-dev] 01/03: [scr] Logging Extension

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

tjwatson pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git

commit 18d37af0cc3b6a1392dc2053ae97a5577adfa0e5
Author: Peter Kriens <Pe...@aqute.biz>
AuthorDate: Thu Jul 23 16:39:40 2020 +0200

    [scr] Logging Extension
    
    The OSGi specifications mandates component relative
    logging on the component's bundle and using the
    implementation class as logger name.
    
    This can be quite inconvenient since SCR is rather
    enthusiastic logger. If the component's bundle has
    DEBUG logging enabled, the customer's logging messages
    tend to get overshadowed.
    
    Also, the component logger could be disabled, but
    then the bundle logger was used, and if the bundle
    logger was disabled, the SCR logger was used. This
    is quite inconvenient.
    
    The code was not properly handling the case when
    a LoggerFactory would disappear or a bundle would
    stop.
    
    Last, the code looked like it had grown a lot over time.
    
    This patch will provide an (almost) source compatible
    version of the SCR logging class ScrLogger, ComponentLogger,
    and BundleLogger, only their creation differs.
    
    The default behavior is (or should be) as today. There
    is one exception, we will not delegate to the parent
    since this seems a bug. The logger hierarchy is much better
    handled by the Logger Admin.
    
    The extension has a new configuration property:
    
    	ds.log.extension ::= 'true' | 'false'
    
    If set to true, the logging is not required to conform
    to the OSGi specification.
    
    ScrLogManager – Access point for SCR logging. It provides
    a static method to create a ScrLogger. The ScrLogger gives
    access to a BundleLogger, and this gives access to a
    ComponentLogger.
    
    The stdout logger has been removed and the fallback to
    System.out/err is now handled in the ScrLogManager.
    
    ExtLogManager – Implements the extension
    
    LogManager – However, the internals are quite different. First
    there is a LogManager that handles the interaction
    between the OSGi service registry and the LoggerFactory,
    cleaning up when the factory goes or the bundle goes.
    This class has no _policy_. It uses a _facade_ to handle the
    life cycle issues.
    
    
    The InternalLogger had a Level that was used by all the code but the
    API of the logger objects was defined by the AbstractLogger. Since
    the InternalLogger had no meaning anymore, it is used as the
    base interface for the logger _interfaces_. The scr, bundle, and
    component logger objects are now almost empty interfaces.
    
    Package looks busier than I like, and many classes are almost empty
    but this way there is almost no change in the rest of the code base.
    
    ## Testing
    
    An extensive JUnit test was added in LoggerTest that has almost full
    code coverage.
---
 scr/pom.xml                                        |  13 +-
 .../java/org/apache/felix/scr/impl/Activator.java  |   5 +-
 .../felix/scr/impl/BundleComponentActivator.java   |   4 +-
 .../scr/impl/config/ScrConfigurationImpl.java      |  18 +-
 .../felix/scr/impl/logger/AbstractLogger.java      | 152 -------
 .../apache/felix/scr/impl/logger/BundleLogger.java |  73 +---
 .../felix/scr/impl/logger/ComponentLogger.java     |  91 +---
 .../felix/scr/impl/logger/ExtLogManager.java       |  79 ++++
 .../felix/scr/impl/logger/InternalLogger.java      |  98 ++++-
 .../apache/felix/scr/impl/logger/LogManager.java   | 203 +++++++++
 .../scr/impl/logger/LogServiceEnabledLogger.java   |  74 ----
 .../apache/felix/scr/impl/logger/OSGiLogger.java   |  76 ----
 .../felix/scr/impl/logger/ScrLogManager.java       | 293 +++++++++++++
 .../apache/felix/scr/impl/logger/ScrLogger.java    |  46 +-
 .../apache/felix/scr/impl/logger/StdOutLogger.java |  99 -----
 .../felix/scr/impl/manager/ScrConfiguration.java   |  12 +
 .../apache/felix/scr/impl/logger/LogService.java   | 336 +++++++++++++++
 .../apache/felix/scr/impl/logger/LoggerTest.java   | 463 +++++++++++++++++++++
 .../felix/scr/impl/logger/MockBundleLogger.java    |  23 +-
 .../felix/scr/impl/logger/MockComponentLogger.java |  38 +-
 .../felix/scr/impl/logger/MockScrLogger.java       |  81 +---
 21 files changed, 1568 insertions(+), 709 deletions(-)

diff --git a/scr/pom.xml b/scr/pom.xml
index 88a1347..9caf4a1 100644
--- a/scr/pom.xml
+++ b/scr/pom.xml
@@ -144,7 +144,18 @@
             <version>2.28.2</version>
             <scope>test</scope>
         </dependency>
-
+		<dependency>
+			<groupId>org.assertj</groupId>
+			<artifactId>assertj-core</artifactId>
+			<version>2.9.1</version>
+            <scope>test</scope>
+		</dependency>
+         <dependency>
+            <groupId>biz.aQute.bnd</groupId>
+            <artifactId>biz.aQute.bndlib</artifactId>
+            <version>5.1.1</version>
+            <scope>test</scope>
+        </dependency>
         <!-- Integration Testing with Pax Exam -->
         <dependency>
             <groupId>org.ops4j.pax.exam</groupId>
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/Activator.java b/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
index 5235f04..491cdee 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
@@ -43,6 +43,7 @@ import java.util.concurrent.locks.ReentrantLock;
 import org.apache.felix.scr.impl.config.ScrConfigurationImpl;
 import org.apache.felix.scr.impl.inject.internal.ClassUtils;
 import org.apache.felix.scr.impl.logger.InternalLogger.Level;
+import org.apache.felix.scr.impl.logger.ScrLogManager;
 import org.apache.felix.scr.impl.logger.ScrLogger;
 import org.apache.felix.scr.impl.manager.ComponentHolder;
 import org.apache.felix.scr.impl.metadata.ComponentMetadata;
@@ -115,7 +116,7 @@ public class Activator extends AbstractExtender
         m_context = context;
         m_bundle = context.getBundle();
         // require the log service
-        logger = new ScrLogger(m_configuration, m_context);
+        logger = ScrLogManager.scr(context, m_configuration);
         // set bundle context for PackageAdmin tracker
         ClassUtils.setBundleContext( context );
         // get the configuration
@@ -208,7 +209,7 @@ public class Activator extends AbstractExtender
         super.stop( context );
         m_configuration.stop();
         store(m_componentMetadataStore, context, logger, m_configuration.cacheMetadata());
-        logger.closeTracker();
+        logger.close();
     }
 
     @Override
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java b/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
index 1e880a3..1bf5108 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
@@ -199,7 +199,7 @@ public class BundleComponentActivator implements ComponentActivator
     throws ComponentException
     {
         // create a logger on behalf of the bundle
-        this.logger = new BundleLogger(context.getBundle(), scrLogger);
+        this.logger = scrLogger.bundle(context.getBundle());
         // keep the parameters for later
         m_componentRegistry = componentRegistry;
         m_componentActor = componentActor;
@@ -432,7 +432,7 @@ public class BundleComponentActivator implements ComponentActivator
 
     void validateAndRegister(ComponentMetadata metadata)
     {
-        final ComponentLogger componentLogger = new ComponentLogger(metadata, logger);
+        final ComponentLogger componentLogger = logger.component(m_bundle, metadata.getImplementationClassName(), metadata.getName());
         ComponentRegistryKey key = null;
         try
         {
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
index f504238..bf2778b 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
@@ -82,6 +82,7 @@ public class ScrConfigurationImpl implements ScrConfiguration
     private boolean infoAsService;
 
     private boolean cacheMetadata;
+    private boolean logExtension;
 
     private long lockTimeout = DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
 
@@ -178,6 +179,7 @@ public class ScrConfigurationImpl implements ScrConfiguration
                         serviceChangecountTimeout = DEFAULT_SERVICE_CHANGECOUNT_TIMEOUT_MILLISECONDS;
                         newGlobalExtender = false;
                         cacheMetadata = false;
+                        logExtension = false;
                     }
                     else
                     {
@@ -190,6 +192,7 @@ public class ScrConfigurationImpl implements ScrConfiguration
                         serviceChangecountTimeout = getServiceChangecountTimeout();
                         newGlobalExtender = getDefaultGlobalExtender();
                         cacheMetadata = getDefaultCacheMetadata();
+                        logExtension = getDefaultLogExtension();
                     }
                 }
                 else
@@ -224,7 +227,7 @@ public class ScrConfigurationImpl implements ScrConfiguration
         }
     }
 
-    /**
+	/**
      * Returns the current log level.
      * Note that this log level is not used with an R7 LogService implementation.
      * @return
@@ -249,7 +252,8 @@ public class ScrConfigurationImpl implements ScrConfiguration
         return keepInstances;
     }
 
-    @Override
+    @SuppressWarnings("deprecation")
+	@Override
     public boolean infoAsService()
     {
         return infoAsService;
@@ -402,4 +406,14 @@ public class ScrConfigurationImpl implements ScrConfiguration
         // default log level (errors only)
         return Level.ERROR;
     }
+
+	@Override
+	public boolean isLogExtension() {
+		return logExtension;
+	}
+
+    private boolean getDefaultLogExtension() {
+        return VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty( PROP_LOG_EXTENSION) );
+	}
+
 }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/AbstractLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/AbstractLogger.java
deleted file mode 100644
index 87236c6..0000000
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/AbstractLogger.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.
- */
-package org.apache.felix.scr.impl.logger;
-
-import java.text.MessageFormat;
-
-import org.apache.felix.scr.impl.logger.InternalLogger.Level;
-import org.osgi.framework.Bundle;
-
-/**
- * This is a common base for all loggers
- *
- */
-public abstract class AbstractLogger
-{
-
-    /**
-     * The prefix put for each log message
-     */
-    private volatile String prefix;
-
-    AbstractLogger(final String prefix)
-    {
-        this.prefix = prefix;
-    }
-
-    void setPrefix(final String value)
-    {
-        this.prefix = value;
-    }
-
-    String getPrefix()
-    {
-        return this.prefix;
-    }
-
-    /**
-     * Get the internal logger
-     * @return The internal logger
-     */
-    abstract InternalLogger getLogger();
-
-    /**
-     * Returns {@code true} if logging for the given level is enabled.
-     */
-    public boolean isLogEnabled(final Level level)
-    {
-        final InternalLogger l = getLogger();
-        return l.isLogEnabled(level);
-    }
-
-    /**
-     * Method to actually emit the log message. If the LogService is available,
-     * the message will be logged through the LogService. Otherwise the message
-     * is logged to stdout (or stderr in case of LOG_ERROR level messages),
-     *
-     * @param level The log level to log the message at
-     * @param pattern The {@code java.text.MessageFormat} message format
-     *      string for preparing the message
-     * @param ex An optional <code>Throwable</code> whose stack trace is written,
-     * @param arguments The format arguments for the <code>pattern</code>
-     *      string.
-     */
-    public boolean log(final Level level, final String pattern, final Throwable ex,
-        final Object... arguments)
-    {
-        if ( isLogEnabled( level ) )
-        {
-            getLogger().log(level, format(pattern, arguments), ex);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Method to actually emit the log message. If the LogService is available,
-     * the message will be logged through the LogService. Otherwise the message
-     * is logged to stdout (or stderr in case of LOG_ERROR level messages),
-     *
-     * @param level The log level of the messages. This corresponds to the log
-     *          levels defined by the OSGi LogService.
-     * @param message The message to print
-     * @param ex The <code>Throwable</code> causing the message to be logged.
-     */
-    public boolean log(final Level level, final String message, final Throwable ex)
-    {
-        if ( isLogEnabled( level ) )
-        {
-            getLogger().log(level, prefix.concat(" ").concat(message), ex);
-            return true;
-        }
-        return false;
-    }
-
-    static String getBundleIdentifier(final Bundle bundle)
-    {
-        final StringBuilder sb = new StringBuilder("bundle ");
-        // symbolic name might be null
-        if ( bundle.getSymbolicName() != null )
-        {
-            sb.append(bundle.getSymbolicName());
-            sb.append(':');
-            sb.append(bundle.getVersion());
-            sb.append( " (" );
-            sb.append( bundle.getBundleId() );
-            sb.append( ")" );
-        }
-        else
-        {
-            sb.append( bundle.getBundleId() );
-        }
-
-        return sb.toString();
-    }
-
-    private String format( final String pattern, final Object... arguments )
-    {
-        final String message;
-        if ( arguments == null || arguments.length == 0 )
-        {
-            message = pattern;
-        }
-        else
-        {
-            for(int i=0;i<arguments.length;i++)
-            {
-                if ( arguments[i] instanceof Bundle )
-                {
-                    arguments[i] = getBundleIdentifier((Bundle)arguments[i]);
-                }
-            }
-            message = MessageFormat.format( pattern, arguments );
-        }
-        return prefix.concat(message);
-    }
-}
\ No newline at end of file
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/BundleLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/BundleLogger.java
index 241b9c8..6f9c6c2 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/BundleLogger.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/BundleLogger.java
@@ -18,83 +18,14 @@
  */
 package org.apache.felix.scr.impl.logger;
 
-import org.apache.felix.scr.impl.logger.InternalLogger.Level;
 import org.osgi.framework.Bundle;
-import org.osgi.service.log.Logger;
-import org.osgi.service.log.LoggerFactory;
 
 /**
  * The {@code BundleLogger} defines a simple API to enable some logging on behalf of
  * an extended bundle. This avoids that all clients doing logging on behalf of
  * a component bundle need to pass in things like {@code BundleContext}.
  */
-public class BundleLogger extends LogServiceEnabledLogger
-{
-    private final ScrLogger parent;
+public interface BundleLogger  extends InternalLogger  {
 
-    public BundleLogger(final Bundle bundle, final ScrLogger parent)
-    {
-        super(bundle, parent.getLoggerFactoryTracker());
-        this.parent = parent;
-    }
-
-    @Override
-    InternalLogger getDefaultLogger()
-    {
-        return new InternalLogger()
-        {
-
-            @Override
-            public void log(final Level level, final String message,
-                final Throwable ex)
-            {
-                parent.getLogger().log(level, message, ex);
-            }
-
-            @Override
-            public boolean isLogEnabled(final Level level)
-            {
-                return parent.getLogger().isLogEnabled(level);
-            }
-        };
-    }
-
-    int getTrackingCount()
-    {
-        return trackingCount;
-    }
-
-    InternalLogger getLogger(final String className)
-    {
-        if ( className != null )
-        {
-            final LoggerFactory factory = this.loggingFactoryTracker.getService();
-            if (factory != null)
-            {
-                return new OSGiLogger(factory.getLogger(bundle, className, Logger.class));
-            }
-        }
-        return this.getLogger();
-    }
-
-    @Override
-    public boolean log(final Level level, final String pattern, final Throwable ex,
-        final Object... arguments)
-    {
-        // delegate to parent if not logging
-        if ( !super.log(level, pattern, ex, arguments) ) {
-            return this.parent.log(level, pattern, ex, arguments);
-        }
-        return false;
-    }
-
-    @Override
-    public boolean log(final Level level, final String message, final Throwable ex)
-    {
-        // delegate to parent if not logging
-        if ( !super.log(level, message, ex) ) {
-            return this.parent.log(level, message, ex);
-        }
-        return false;
-    }
+	ComponentLogger component(Bundle m_bundle, String implementationClassName, String name);
 }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/ComponentLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/ComponentLogger.java
index 13252c5..facf595 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/ComponentLogger.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/ComponentLogger.java
@@ -18,98 +18,11 @@
  */
 package org.apache.felix.scr.impl.logger;
 
-import org.apache.felix.scr.impl.logger.InternalLogger.Level;
-import org.apache.felix.scr.impl.metadata.ComponentMetadata;
-
 /**
  * The {@code ComponentLogger} is the logger to be used to log on behalf of a component.
  * This avoids avoids that all clients doing logging on behalf of a component need to
  * pass in things like {@code ComponentMetadata} or the component Id.
  */
-public class ComponentLogger extends AbstractLogger
-{
-    private final String name;
-
-    private final String className;
-
-    private final BundleLogger parent;
-
-    private volatile int trackingCount = -3;
-
-    private volatile InternalLogger currentLogger;
-
-    public ComponentLogger(final ComponentMetadata metadata, final BundleLogger parent)
-    {
-        super(""); // we set the prefix later
-        this.parent = parent;
-        if ( metadata.getName() != null )
-        {
-            this.name = metadata.getName();
-        }
-        else if ( metadata.getImplementationClassName() != null )
-        {
-            this.name = "implementation class " + metadata.getImplementationClassName();
-        }
-        else
-        {
-            this.name = "UNKNOWN";
-        }
-        if ( metadata.getImplementationClassName() != null )
-        {
-            this.className = metadata.getImplementationClassName();
-        }
-        else
-        {
-            this.className = null;
-        }
-        this.setComponentId(-1);
-    }
-
-    /**
-     * Update the logger with the correct component id.
-     * @param id The component id
-     */
-    public void setComponentId(final long id)
-    {
-        if ( id > -1 )
-        {
-            this.setPrefix(this.parent.getPrefix() + "[" + name + "(" + id + ")] : ");
-        }
-        else
-        {
-            this.setPrefix(this.parent.getPrefix() + "[" + name + "] : ");
-        }
-    }
-
-    @Override
-    InternalLogger getLogger()
-    {
-        if ( this.trackingCount < this.parent.getTrackingCount() )
-        {
-            this.currentLogger = this.parent.getLogger(this.className);
-            this.trackingCount = this.parent.getTrackingCount();
-        }
-        return currentLogger;
-    }
-
-    @Override
-    public boolean log(final Level level, final String pattern, final Throwable ex,
-        final Object... arguments)
-    {
-        // delegate to parent if not logging
-        if ( !super.log(level, pattern, ex, arguments) ) {
-            return this.parent.log(level, pattern, ex, arguments);
-        }
-        return false;
-    }
-
-    @Override
-    public boolean log(final Level level, final String message, final Throwable ex)
-    {
-        // delegate to parent if not logging
-        if ( !super.log(level, message, ex) ) {
-            return this.parent.log(level, message, ex);
-        }
-        return false;
-    }
+public interface ComponentLogger extends InternalLogger {
+	void setComponentId(long m_componentId);
 }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/ExtLogManager.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/ExtLogManager.java
new file mode 100644
index 0000000..3c99e5f
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/ExtLogManager.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.felix.scr.impl.logger;
+
+import org.apache.felix.scr.impl.manager.ScrConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Implements an extension to the SCR log manager that uses logger names to
+ * create a hierarchy of loggers. All messages will be logged via the SCR
+ * logger's bundle unlike the classic scr log manager that used the bundle's logger.
+ * 
+ * <ul>
+ * <li>An ScrLogger will log with the name {@value #SCR_LOGGER_NAME}
+ * <li>A BundleLogger will log with the name {@value #SCR_LOGGER_PREFIX} + the
+ * bundle symbolic name
+ * <li>A ComponentLogger will log with the name {@value #SCR_LOGGER_PREFIX} +
+ * the bundle symbolic name + "." + component name
+ * </ul>
+ */
+class ExtLogManager extends ScrLogManager {
+	public static String	SCR_LOGGER_NAME		= "org.apache.felix.scr.impl";
+	public static String	SCR_LOGGER_PREFIX	= "org.apache.felix.scr.";
+	private final Bundle	bundle;
+
+	ExtLogManager(BundleContext context, ScrConfiguration config) {
+		super(context, config);
+		this.bundle = context.getBundle();
+	}
+
+	@Override
+	public ScrLogger scr() {
+		return getLogger(bundle, SCR_LOGGER_NAME, ScrLoggerFacade.class);
+	}
+
+	@Override
+	public BundleLogger bundle(Bundle bundle) {
+		return getLogger(this.bundle, SCR_LOGGER_PREFIX.concat(bundle.getSymbolicName()), ScrLoggerFacade.class);
+	}
+
+	@Override
+	public ComponentLogger component(Bundle bundle, String implementationClass, String componentName) {
+
+		assert bundle != null;
+		assert bundle.getSymbolicName() != null : "scr requires recent bundles";
+		assert implementationClass != null;
+		assert componentName != null;
+
+		String loggerName = SCR_LOGGER_PREFIX.concat(bundle.getSymbolicName()).concat(".").concat(componentName);
+		ScrLoggerFacade logger = getLogger(this.bundle, loggerName, ScrLoggerFacade.class);
+		logger.setPrefix("["+componentName+"]");
+		return logger;
+	}
+
+	String componentPrefix(ScrLoggerFacade slf, long id) {
+		assert slf.prefix != null; 
+		if ( slf.prefix.indexOf(')')<0)
+			return slf.prefix.replace("]", "(" + id + ")]");
+		else
+			return slf.prefix;
+	}
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/InternalLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/InternalLogger.java
index 2295dc9..4eca75f 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/InternalLogger.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/InternalLogger.java
@@ -18,19 +18,93 @@
  */
 package org.apache.felix.scr.impl.logger;
 
-public interface InternalLogger
-{
-    public enum Level
-    {
-        AUDIT, ERROR, WARN, INFO, DEBUG, TRACE;
+import java.text.MessageFormat;
 
-        public boolean implies(Level other)
-        {
-            return ordinal() >= other.ordinal();
-        }
-    }
+import org.apache.felix.scr.impl.manager.ScrConfiguration;
+import org.osgi.service.log.admin.LoggerAdmin;
 
-    void log(Level level, String message, Throwable ex);
+/**
+ * Base interface for the different SCR Loggers. Since this is not used outside
+ * this package, it could be private. However, then Level should be standalone,
+ * which would change most files. So minimize the code change, it is kept public.
+ */
+public interface InternalLogger {
+	/**
+	 * The level to log. This is aligned with the OSGi LogLevel
+	 */
+	public enum Level {
+		AUDIT {
+			boolean err() {
+				return true;
+			}
+		},
+		ERROR {
+			boolean err() {
+				return true;
+			}
+		},
+		WARN, INFO, DEBUG, TRACE;
+
+		/**
+		 * Check if this log level is higher or the same of the other level.
+		 * 
+		 * @param other
+		 *                  the other level
+		 * @return true if the level other should be logged
+		 */
+		boolean implies(Level other) {
+			return ordinal() >= other.ordinal();
+		}
+
+		boolean err() {
+			return false;
+		}
+
+	}
+
+	/**
+	 * Logs the message to an appropriate OSGi logger. If not such logger can be
+	 * found then it will log to stderr for ERROR & AUDIT messages and stdout
+	 * for other messages
+	 * 
+	 * @param level
+	 *                    only log when this level is implied by the current log
+	 *                    level
+	 * @param message
+	 *                    the message to log
+	 * @param ex
+	 *                    a Throwable or null
+	 */
+	void log(Level level, String message, Throwable ex);
+
+	/**
+	 * Formats the message using the {@link MessageFormat} class, i.e. with {}
+	 * place holders for the args. It then calls
+	 * {@link #log(Level, String, Throwable)}.
+	 * 
+	 * @param level
+	 *                    only log when this level is implied by the current log
+	 *                    level
+	 * @param message
+	 *                    the message to log
+	 * @param ex
+	 *                    a Throwable or null
+	 * @param args
+	 *                    the arguments to the {@link MessageFormat} formatting
+	 */
+	void log(Level level, String message, Throwable ex, Object... args);
+
+	/**
+	 * Answer true if the current logging level is enabled for the given level.
+	 * For stdout/stderr fallback the logging level is defined by the
+	 * {@link ScrConfiguration#getLogLevel()}. If there is an OSGi logger
+	 * available then the logger name will define the log level via
+	 * {@link LoggerAdmin}.
+	 *
+	 * @param level
+	 *                  the level to check
+	 * @return true if the given log level is enabled
+	 */
+	boolean isLogEnabled(Level level);
 
-    boolean isLogEnabled(Level level);
 }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/LogManager.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/LogManager.java
new file mode 100644
index 0000000..15a2ea4
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/LogManager.java
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+package org.apache.felix.scr.impl.logger;
+
+import java.io.Closeable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.Logger;
+import org.osgi.service.log.LoggerFactory;
+import org.osgi.util.tracker.ServiceTracker;
+
+/*
+ * A class that tracks the LoggerFactory of a bundle and its associated Logger objects. 
+ * When a bundle is stopped and/or the service is unregistered, the logger objects are cleaned.
+ * 
+ * The basic technique is to use a facade. Instead of returning a log object, we return a facade. The 
+ * methods delegate to the actual logger object. If there is no logger object, we create one.
+ * 
+ * The LogDomain represents every bundle. Per LogDomain, we keep the facades. If the factory goes,
+ * we reset the facades.
+ */
+class LogManager extends ServiceTracker<LoggerFactory, LoggerFactory> {
+
+	// Guard is itself. Don't replace it with a ConcurrentHashMap since the
+	// LogDomain value is not stateless.
+
+	final Map<Bundle, LogDomain>	domains	= new HashMap<>();
+	final BundleContext				scrContext;
+	final AtomicBoolean				closed	= new AtomicBoolean(false);
+	volatile int					trackingCount;
+	volatile LoggerFactory			factory;
+
+	LogManager(BundleContext context) {
+		super(context, LoggerFactory.class, null);
+		this.scrContext = context;
+	}
+
+	private LogDomain getLogDomain(Bundle bundle) {
+		synchronized (domains) {
+			LogDomain domain = domains.get(bundle);
+			if (domain == null) {
+				domain = new LogDomain(bundle);
+				domains.put(bundle, domain);
+			}
+			return domain;
+		}
+	}
+
+	@Override
+	public void removedService(ServiceReference<LoggerFactory> reference, LoggerFactory service) {
+		if (!closed.get()) {
+			reset();
+		}
+	}
+
+	private void reset() {
+		for (LogDomain domain : domains.values()) {
+			domain.reset();
+		}
+	}
+
+	<T> T getLogger(Bundle bundle, String name, Class<T> type) {
+		return type.cast(getLogDomain(bundle).getLogger(name));
+	}
+
+	LoggerFactory getLoggerFactory() {
+		int trackingCount = getTrackingCount();
+		if (this.trackingCount < trackingCount) {
+			this.trackingCount = trackingCount;
+			factory = getService();
+		}
+		return factory;
+	}
+
+	/*
+	 * Tracks a bundle's LoggerFactory service
+	 */
+	class LogDomain
+			implements Closeable, BundleListener {
+
+		private final Bundle			bundle;
+		private final Set<LoggerFacade>	facades	= new HashSet<>();
+
+		LogDomain(Bundle bundle) {
+			this.bundle = bundle;
+			scrContext.addBundleListener(this);
+			open();
+		}
+
+		void reset() {
+			for (LoggerFacade facade : facades) {
+				facade.reset();
+			}
+		}
+
+		@SuppressWarnings("resource")
+		@Override
+		public void bundleChanged(BundleEvent event) {
+			if (event.getBundle() == bundle && event.getType() == BundleEvent.STOPPED && !closed.get()) {
+				scrContext.removeBundleListener(this);
+				LogDomain remove;
+				synchronized (domains) {
+					remove = domains.remove(bundle);
+				}
+				if (remove != null)
+					remove.close();
+			}
+		}
+
+		LoggerFacade getLogger(String name) {
+			LoggerFacade facade = createLoggerFacade(this, name);
+			facades.add(facade);
+			return facade;
+		}
+
+		@Override
+		public void close() {
+			reset();
+		}
+
+	}
+
+	class LoggerFacade {
+		private final String	name;
+		private final LogDomain	domain;
+		volatile Logger			logger;
+		volatile String			prefix;
+
+		LoggerFacade(LogDomain logDomain, String name) {
+			this.domain = logDomain;
+			this.name = name;
+		}
+
+		void reset() {
+			logger = null;
+		}
+
+		Logger getLogger() {
+			Logger l = this.logger;
+			if (l == null) {
+				LoggerFactory lf = getLoggerFactory();
+				if (lf == null)
+					return null;
+
+				l = this.logger = lf.getLogger(domain.bundle, name, Logger.class);
+			}
+			return l;
+		}
+
+		Bundle getBundle() {
+			return domain.bundle;
+		}
+
+		String getName() {
+			return name;
+		}
+
+	}
+
+	public void close() {
+		if (closed.compareAndSet(false, true)) {
+			Set<LogDomain> domains;
+			synchronized (this.domains) {
+				domains = new HashSet<>(this.domains.values());
+				this.domains.clear();
+			}
+			for (LogDomain domain : domains) {
+				domain.close();
+			}
+		}
+	}
+
+	LoggerFacade createLoggerFacade(LogDomain logDomain, String name) {
+		assert !closed.get();
+		return new LoggerFacade(logDomain, name);
+	}
+
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/LogServiceEnabledLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/LogServiceEnabledLogger.java
deleted file mode 100644
index 933b3e5..0000000
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/LogServiceEnabledLogger.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.
- */
-package org.apache.felix.scr.impl.logger;
-
-import org.osgi.framework.Bundle;
-import org.osgi.service.log.Logger;
-import org.osgi.service.log.LoggerFactory;
-import org.osgi.util.tracker.ServiceTracker;
-
-/**
- * This abstract class adds support for using a LogService
- * (or LoggerFactory for R7+).
- */
-abstract class LogServiceEnabledLogger extends AbstractLogger
-{
-    // the log service to log messages to
-    protected final ServiceTracker<LoggerFactory, LoggerFactory> loggingFactoryTracker;
-
-    protected final Bundle bundle;
-
-    private volatile InternalLogger currentLogger;
-
-    protected volatile int trackingCount = -2;
-
-    public LogServiceEnabledLogger(final Bundle bundle, ServiceTracker<LoggerFactory, LoggerFactory> loggingFactoryTracker)
-    {
-        super(getBundleIdentifier(bundle));
-        this.bundle = bundle;
-        this.loggingFactoryTracker = loggingFactoryTracker;
-    }
-
-    @Override
-    InternalLogger getLogger()
-    {
-        if (this.trackingCount < this.loggingFactoryTracker.getTrackingCount())
-        {
-            final LoggerFactory factory = this.loggingFactoryTracker.getService();
-            if (factory == null)
-            {
-                this.currentLogger = this.getDefaultLogger();
-            }
-            else
-            {
-                this.currentLogger = new OSGiLogger(
-                    factory.getLogger(bundle, Logger.ROOT_LOGGER_NAME, Logger.class));
-            }
-            this.trackingCount = this.loggingFactoryTracker.getTrackingCount();
-        }
-        return currentLogger;
-    }
-
-    abstract InternalLogger getDefaultLogger();
-
-    ServiceTracker<LoggerFactory, LoggerFactory> getLoggerFactoryTracker()
-    {
-        return loggingFactoryTracker;
-    }
-}
\ No newline at end of file
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/OSGiLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/OSGiLogger.java
deleted file mode 100644
index 1a9fc07..0000000
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/OSGiLogger.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-package org.apache.felix.scr.impl.logger;
-
-import org.osgi.service.log.Logger;
-
-final class OSGiLogger implements InternalLogger
-{
-
-    private final Logger logger;
-
-    public OSGiLogger(Logger logger)
-    {
-        this.logger = logger;
-    }
-
-    public void log(Level level, String message, Throwable ex)
-    {
-        if ( ex == null )
-        {
-            switch ( level )
-            {
-                case AUDIT : logger.audit(message); break;
-                case ERROR : logger.error(message); break;
-                case WARN : logger.warn(message); break;
-                case INFO : logger.info(message); break;
-                case DEBUG : logger.debug(message); break;
-                case TRACE : logger.trace(message); break;
-                default : logger.debug(message);
-            }
-        }
-        else
-        {
-            switch ( level )
-            {
-                case AUDIT : logger.audit(message, ex); break;
-                case ERROR : logger.error(message, ex); break;
-                case WARN : logger.warn(message, ex); break;
-                case INFO : logger.info(message, ex); break;
-                case DEBUG : logger.debug(message, ex); break;
-                case TRACE : logger.trace(message, ex); break;
-                default : logger.debug(message, ex);
-            }
-        }
-    }
-
-    public boolean isLogEnabled(Level level)
-    {
-        switch (level)
-        {
-            case AUDIT: return true;
-            case ERROR: return logger.isErrorEnabled();
-            case WARN: return logger.isWarnEnabled();
-            case INFO: return logger.isInfoEnabled();
-            case DEBUG: return logger.isDebugEnabled();
-            case TRACE: return logger.isTraceEnabled();
-            default: return logger.isDebugEnabled();
-        }
-    }
-}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogManager.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogManager.java
new file mode 100644
index 0000000..3fc2d8c
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogManager.java
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+package org.apache.felix.scr.impl.logger;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+
+import org.apache.felix.scr.impl.logger.InternalLogger.Level;
+import org.apache.felix.scr.impl.manager.ScrConfiguration;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.Logger;
+
+/**
+ * Implements a SCR based log manager. This class was needed to not change the
+ * whole codebase. It looks very similar to the old logging but leverages the
+ * {@link LogManager}. This class implements the existing behavior and the
+ * {@link ExtLogManager} implements the extension behavior. The {@link #scr()}
+ * method makes this distinction based on the configuration.
+ */
+public class ScrLogManager extends LogManager {
+
+	private final Bundle			bundle;
+	private final ScrConfiguration	config;
+
+	/**
+	 * Get a new log manager based on the configuration.
+	 * 
+	 * @param context
+	 *                    the bundle context of the SCR bundle
+	 * @param config
+	 *                    the SCR configuration
+	 * @return a proper ScrLogManager
+	 */
+
+	public static ScrLogger scr(BundleContext context, ScrConfiguration config) {
+		if (config.isLogExtension())
+			return new ExtLogManager(context, config).scr();
+		else
+			return new ScrLogManager(context, config).scr();
+	}
+
+	ScrLogManager(BundleContext context, ScrConfiguration config) {
+		super(context);
+		this.config = config;
+		this.bundle = context.getBundle();
+	}
+
+	/**
+	 * This logger is used for the main code of SCR. This will use the SCR
+	 * bundle & the {@link Logger#ROOT_LOGGER_NAME}
+	 * 
+	 * @return an Scr Logger.
+	 */
+	public ScrLogger scr() {
+		ScrLoggerFacade scrl = super.getLogger(bundle, Logger.ROOT_LOGGER_NAME, ScrLoggerFacade.class);
+		scrl.setPrefix(getBundleIdentifier(bundle));
+		return scrl;
+	}
+
+	/**
+	 * This logger is used for the logging on a per bundle basis. This will use
+	 * the target bundle & the {@link Logger#ROOT_LOGGER_NAME}
+	 * 
+	 * @param bundle
+	 *                   the target bundle
+	 * @return a logger suitable to log bundle entries
+	 */
+	public BundleLogger bundle(Bundle bundle) {
+		ScrLoggerFacade logger = getLogger(bundle, Logger.ROOT_LOGGER_NAME, ScrLoggerFacade.class);
+		logger.setPrefix(getBundleIdentifier(bundle));
+		return logger;
+	}
+
+	/**
+	 * This logger is used for the logging on a per bundle basis. This will use
+	 * the target bundle & the implementation class as logger name.
+	 * 
+	 * @param bundle
+	 *                   the target bundle
+	 * @return a logger suitable to log bundle entries
+	 */
+	public ComponentLogger component(Bundle bundle, String implementationClass, String name) {
+
+		// assert bundle != null;
+		// assert bundle.getSymbolicName() != null : "scr requires recent bundles";
+		// assert implementationClass != null;
+		// assert name != null;
+
+		ScrLoggerFacade facade = getLogger(bundle, implementationClass, ScrLoggerFacade.class);
+		facade.setComponentId(-1);
+		return (ComponentLogger) facade;
+	}
+
+	class ScrLoggerFacade extends LoggerFacade implements InternalLogger, ScrLogger, BundleLogger, ComponentLogger {
+		ScrLoggerFacade(LogDomain logDomain, String name) {
+			super(logDomain, name);
+		}
+
+		@Override
+		public void setComponentId(long id) {
+			setPrefix(componentPrefix(this, id));
+		}
+
+		public boolean isLogEnabled(Level level) {
+
+			// assert !closed.get();
+
+			Logger logger = getLogger();
+			if (logger != null) {
+				switch (level) {
+				case AUDIT:
+					return true;
+				case ERROR:
+					return logger.isErrorEnabled();
+				case WARN:
+					return logger.isWarnEnabled();
+				case INFO:
+					return logger.isInfoEnabled();
+				case TRACE:
+					return logger.isTraceEnabled();
+				case DEBUG:
+				default:
+					return logger.isDebugEnabled();
+				}
+			} else {
+				return getLogLevel().implies(level);
+			}
+		}
+
+		@Override
+		public void log(Level level, String format, Throwable ex, Object... arguments) {
+			log(level, format(format, arguments), ex);
+		}
+
+		@Override
+		public void log(Level level, String message, Throwable ex) {
+			if (prefix != null && prefix.length() > 0) {
+				message = prefix.concat(" ").concat(message);
+			}
+			if (isLogEnabled(level)) {
+				Logger logger = getLogger();
+				if (logger != null) {
+					if (ex == null) {
+						switch (level) {
+						case AUDIT:
+							logger.audit(message);
+							break;
+						case ERROR:
+							logger.error(message);
+							break;
+						case WARN:
+							logger.warn(message);
+							break;
+						case INFO:
+							logger.info(message);
+							break;
+						case TRACE:
+							logger.trace(message);
+							break;
+						case DEBUG:
+						default:
+							logger.debug(message);
+						}
+					} else {
+						switch (level) {
+						case AUDIT:
+							logger.audit(message, ex);
+							break;
+						case ERROR:
+							logger.error(message, ex);
+							break;
+						case WARN:
+							logger.warn(message, ex);
+							break;
+						case INFO:
+							logger.info(message, ex);
+							break;
+						case TRACE:
+							logger.trace(message, ex);
+							break;
+						case DEBUG:
+						default:
+							logger.debug(message, ex);
+						}
+					}
+				} else {
+					StringWriter buf = new StringWriter();
+					String l = String.format("%-5s", level);
+					buf.append(l).append(" : ").append(message);
+					if (ex != null) {
+						try (PrintWriter pw = new PrintWriter(buf)) {
+							pw.println();
+							ex.printStackTrace(pw);
+						}
+					}
+
+					@SuppressWarnings("resource")
+					PrintStream out = level.err() ? System.err : System.out;
+					out.println(buf);
+				}
+			}
+		}
+
+		void setPrefix(String prefix) {
+			this.prefix = prefix;
+		}
+
+		@Override
+		public ComponentLogger component(Bundle m_bundle, String implementationClassName, String name) {
+			// assert !closed.get();
+			return ScrLogManager.this.component(bundle, implementationClassName, name);
+		}
+
+		@Override
+		public BundleLogger bundle(Bundle bundle) {
+			// assert !closed.get();
+			return ScrLogManager.this.bundle(bundle);
+		}
+
+		@Override
+		public void close() {
+			// assert !closed.get();
+			ScrLogManager.this.close();
+		}
+	};
+
+	LoggerFacade createLoggerFacade(LogDomain logDomain, String name) {
+		// assert !closed.get();
+		return new ScrLoggerFacade(logDomain, name);
+	}
+
+	Level getLogLevel() {
+		return config.getLogLevel();
+	}
+
+	String getBundleIdentifier(final Bundle bundle) {
+		final StringBuilder sb = new StringBuilder("bundle ");
+
+		if (bundle.getSymbolicName() != null) {
+			sb.append(bundle.getSymbolicName());
+			sb.append(':');
+			sb.append(bundle.getVersion());
+			sb.append(" (");
+			sb.append(bundle.getBundleId());
+			sb.append(")");
+		} else {
+			sb.append(bundle.getBundleId());
+		}
+
+		return sb.toString();
+	}
+
+	String componentPrefix(ScrLoggerFacade slf, long id) {
+		if (id >= 0) {
+			return getBundleIdentifier(slf.getBundle()) + "[" + slf.getName() + "(" + id + ")] :";
+		} else {
+			return getBundleIdentifier(slf.getBundle()) + "[" + slf.getName() + "] :";
+		}
+	}
+
+	String format(final String pattern, final Object... arguments) {
+		if (arguments == null || arguments.length == 0) {
+			return pattern;
+		} else {
+			for (int i = 0; i < arguments.length; i++) {
+				if (arguments[i] instanceof Bundle) {
+					arguments[i] = getBundleIdentifier((Bundle) arguments[i]);
+				}
+			}
+			return MessageFormat.format(pattern, arguments);
+		}
+	}
+
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogger.java
index 9ef2562..cbf8a42 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogger.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/logger/ScrLogger.java
@@ -16,43 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.felix.scr.impl.logger;
 
-import org.apache.felix.scr.impl.manager.ScrConfiguration;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.log.LoggerFactory;
-import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.framework.Bundle;
 
 /**
- * This is the "global" logger used by the implementation for all logging
- * not directly related to an extended bundle (and its components)
+ * Logger used by SCR main code
  */
-public class ScrLogger extends LogServiceEnabledLogger
-{
-    private final ScrConfiguration config;
-    public ScrLogger(final ScrConfiguration config, BundleContext context)
-    {
-        super(context.getBundle(), getTracker(context));
-        this.config = config;
-    }
-
-    @Override
-    InternalLogger getDefaultLogger()
-    {
-        return new StdOutLogger(config);
-    }
+public interface ScrLogger extends InternalLogger {
 
-    private static ServiceTracker<LoggerFactory, LoggerFactory> getTracker(
-        BundleContext context)
-    {
-        ServiceTracker<LoggerFactory, LoggerFactory> tracker = new ServiceTracker<>(
-            context, "org.osgi.service.log.LoggerFactory", null);
-        tracker.open();
-        return tracker;
-    }
+	/**
+	 * Create a bundle logger
+	 */
+	BundleLogger bundle(Bundle bundle);
 
-    public void closeTracker()
-    {
-        getLoggerFactoryTracker().close();
-    }
-}
\ No newline at end of file
+	/**
+	 * Close the log manager
+	 */
+	void close();
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/logger/StdOutLogger.java b/scr/src/main/java/org/apache/felix/scr/impl/logger/StdOutLogger.java
deleted file mode 100644
index 627b812..0000000
--- a/scr/src/main/java/org/apache/felix/scr/impl/logger/StdOutLogger.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.
- */
-package org.apache.felix.scr.impl.logger;
-
-import java.io.PrintStream;
-
-import org.apache.felix.scr.impl.manager.ScrConfiguration;
-
-/**
- * This logger logs to std out / err
- */
-class StdOutLogger implements InternalLogger
-{
-
-    private final ScrConfiguration config;
-
-    public StdOutLogger(ScrConfiguration config)
-    {
-        this.config = config;
-    }
-
-    @Override
-    public void log(final Level level, final String message, final Throwable ex)
-    {
-        if ( isLogEnabled(level) )
-        {
-            // output depending on level
-            final PrintStream out = (level == Level.ERROR) ? System.err : System.out;
-
-            // level as a string
-            final StringBuilder buf = new StringBuilder();
-            switch (level)
-            {
-                case AUDIT:
-                    buf.append("AUDIT");
-                    break;
-                case TRACE:
-                    buf.append("TRACE");
-                    break;
-                case DEBUG:
-                    buf.append( "DEBUG: " );
-                    break;
-                case INFO:
-                    buf.append( "INFO : " );
-                    break;
-                case WARN:
-                    buf.append( "WARN : " );
-                    break;
-                case ERROR:
-                    buf.append( "ERROR: " );
-                    break;
-                default:
-                    buf.append( "UNK  : " );
-                    break;
-            }
-
-            buf.append(message);
-
-            final String msg = buf.toString();
-
-            if ( ex == null )
-            {
-                out.println(msg);
-            }
-            else
-            {
-                // keep the message and the stacktrace together
-                synchronized ( out )
-                {
-                    out.println( msg );
-                    ex.printStackTrace( out );
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean isLogEnabled(Level level)
-    {
-        return config.getLogLevel().implies(level);
-
-    }
-}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
index 77881e0..65a5254 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
@@ -64,12 +64,17 @@ public interface ScrConfiguration
     long DEFAULT_STOP_TIMEOUT_MILLISECONDS = 60000;
 
     String PROP_LOGLEVEL = "ds.loglevel";
+    /**
+     * See {@link #isLogExtension()}
+     */
+    String PROP_LOG_EXTENSION = "ds.log.extension";
 
     String PROP_GLOBAL_EXTENDER="ds.global.extender";
 
     String PROP_SERVICE_CHANGECOUNT_TIMEOUT = "ds.service.changecount.timeout";
 
     String PROP_CACHE_METADATA = "ds.cache.metadata";
+    
 
     /**
      * Returns the current log level.
@@ -102,4 +107,11 @@ public interface ScrConfiguration
 
     boolean cacheMetadata();
 
+
+    /**
+     * If true, use a logging extension. The extension can be incompatible with the OSGi specification.
+     * @return true if extension allowed
+     */
+	boolean isLogExtension();
+
 }
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/logger/LogService.java b/scr/src/test/java/org/apache/felix/scr/impl/logger/LogService.java
new file mode 100644
index 0000000..0106161
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/logger/LogService.java
@@ -0,0 +1,336 @@
+/*
+ * 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.
+ */
+package org.apache.felix.scr.impl.logger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogLevel;
+import org.osgi.service.log.Logger;
+import org.osgi.service.log.LoggerConsumer;
+import org.osgi.service.log.LoggerFactory;
+
+import aQute.lib.collections.MultiMap;
+
+public class LogService implements ServiceFactory<LoggerFactory> {
+	public BundleContext						context;
+	public final List<LogEntry>					entries			= new ArrayList<>();
+	public final Map<String, LogLevel>			levels			= new HashMap<>();
+	public LogLevel								defaultLogLevel	= LogLevel.DEBUG;
+	public List<LoggerFactory>					factories		= new ArrayList<>();
+	public MultiMap<Bundle, Logger>				loggers			= new MultiMap<>();
+
+	public ServiceRegistration<LoggerFactory>	registration;
+
+	public static class LogEntry {
+
+		@Override
+		public String toString() {
+			return "LogEntry [level=" + level + ", format=" + format + ", args=" + Arrays.toString(args) + ", bundle="
+					+ bundle + ", loggername=" + loggername + "]";
+		}
+
+		public LogLevel	level;
+		public String	format;
+		public Object[]	args;
+		public Bundle	bundle;
+		public String	loggername;
+
+		public LogEntry(Bundle bundle, String loggername, LogLevel level, String format, Object[] args) {
+			this.bundle = bundle;
+			this.loggername = loggername;
+			this.level = level;
+			this.format = format;
+			this.args = args;
+		}
+
+	}
+
+	public void register() {
+		registration = context.registerService(LoggerFactory.class, this, null);
+	}
+
+	public void unregister() {
+		assert registration != null;
+		registration.unregister();
+		registration = null;
+	}
+
+	public LogService(BundleContext context) {
+		this.context = context;
+	}
+
+	@Override
+	public LoggerFactory getService(final Bundle bundle, ServiceRegistration<LoggerFactory> registration) {
+		LoggerFactory factory = new LoggerFactory() {
+
+			@Override
+			public Logger getLogger(String name) {
+				return getLogger(bundle, name, Logger.class);
+			}
+
+			@Override
+			public Logger getLogger(Class<?> clazz) {
+				return getLogger(bundle, clazz.getName(), Logger.class);
+			}
+
+			@Override
+			public <L extends Logger> L getLogger(String name, Class<L> loggerType) {
+				return getLogger(bundle, name, loggerType);
+			}
+
+			@Override
+			public <L extends Logger> L getLogger(Class<?> clazz, Class<L> loggerType) {
+				return getLogger(bundle, clazz.getName(), loggerType);
+			}
+
+			@Override
+			public <L extends Logger> L getLogger(final Bundle bundle, final String name, Class<L> loggerType) {
+				Logger logger = new Logger() {
+
+					@Override
+					public String getName() {
+						return name;
+					}
+
+					@Override
+					public boolean isTraceEnabled() {
+						return getLevel().implies(LogLevel.TRACE);
+					}
+
+					@Override
+					public void trace(String message) {
+						log(LogLevel.TRACE, message);
+					}
+
+					@Override
+					public void trace(String format, Object arg) {
+						log(LogLevel.TRACE, format, arg);
+					}
+
+					@Override
+					public void trace(String format, Object arg1, Object arg2) {
+						log(LogLevel.TRACE, format, arg1, arg2);
+					}
+
+					@Override
+					public void trace(String format, Object... arguments) {
+						log(LogLevel.TRACE, format, arguments);
+					}
+
+					@Override
+					public <E extends Exception> void trace(LoggerConsumer<E> consumer) throws E {
+						consumer.accept(this);
+					}
+
+					@Override
+					public boolean isDebugEnabled() {
+						return getLevel().implies(LogLevel.DEBUG);
+					}
+
+					@Override
+					public void debug(String message) {
+						log(LogLevel.DEBUG, message);
+					}
+
+					@Override
+					public void debug(String format, Object arg) {
+						log(LogLevel.DEBUG, format, arg);
+					}
+
+					@Override
+					public void debug(String format, Object arg1, Object arg2) {
+						log(LogLevel.DEBUG, format, arg1, arg2);
+					}
+
+					@Override
+					public void debug(String format, Object... arguments) {
+						log(LogLevel.DEBUG, format, arguments);
+					}
+
+					@Override
+					public <E extends Exception> void debug(LoggerConsumer<E> consumer) throws E {
+						consumer.accept(this);
+					}
+
+					@Override
+					public boolean isInfoEnabled() {
+						return getLevel().implies(LogLevel.INFO);
+					}
+
+					@Override
+					public void info(String message) {
+						log(LogLevel.INFO, message);
+					}
+
+					@Override
+					public void info(String format, Object arg) {
+						log(LogLevel.INFO, format, arg);
+					}
+
+					@Override
+					public void info(String format, Object arg1, Object arg2) {
+						log(LogLevel.INFO, format, arg1, arg2);
+					}
+
+					@Override
+					public void info(String format, Object... arguments) {
+						log(LogLevel.INFO, format, arguments);
+					}
+
+					@Override
+					public <E extends Exception> void info(LoggerConsumer<E> consumer) throws E {
+						consumer.accept(this);
+					}
+
+					@Override
+					public boolean isWarnEnabled() {
+						return getLevel().implies(LogLevel.WARN);
+					}
+
+					@Override
+					public void warn(String message) {
+						log(LogLevel.WARN, message);
+					}
+
+					@Override
+					public void warn(String format, Object arg) {
+						log(LogLevel.WARN, format, arg);
+					}
+
+					@Override
+					public void warn(String format, Object arg1, Object arg2) {
+						log(LogLevel.WARN, format, arg1, arg2);
+					}
+
+					@Override
+					public void warn(String format, Object... arguments) {
+						log(LogLevel.WARN, format, arguments);
+					}
+
+					@Override
+					public <E extends Exception> void warn(LoggerConsumer<E> consumer) throws E {
+						consumer.accept(this);
+					}
+
+					@Override
+					public boolean isErrorEnabled() {
+						return getLevel().implies(LogLevel.ERROR);
+					}
+
+					@Override
+					public void error(String message) {
+						log(LogLevel.ERROR, message);
+					}
+
+					@Override
+					public void error(String format, Object arg) {
+						log(LogLevel.ERROR, format, arg);
+					}
+
+					@Override
+					public void error(String format, Object arg1, Object arg2) {
+						log(LogLevel.ERROR, format, arg1, arg2);
+					}
+
+					@Override
+					public void error(String format, Object... arguments) {
+						log(LogLevel.ERROR, format, arguments);
+					}
+
+					@Override
+					public <E extends Exception> void error(LoggerConsumer<E> consumer) throws E {
+						consumer.accept(this);
+					}
+
+					@Override
+					public void audit(String message) {
+						log(LogLevel.AUDIT, message);
+					}
+
+					@Override
+					public void audit(String format, Object arg) {
+						log(LogLevel.AUDIT, format, arg);
+					}
+
+					@Override
+					public void audit(String format, Object arg1, Object arg2) {
+						log(LogLevel.AUDIT, format, arg1, arg2);
+					}
+
+					@Override
+					public void audit(String format, Object... arguments) {
+						log(LogLevel.AUDIT, format, arguments);
+					}
+
+					public void log(LogLevel level, String format, Object... args) {
+						if (getLevel().implies(level)) {
+							entries.add(new LogEntry(bundle, name, level, format, args));
+						}
+					}
+
+					public LogLevel getLevel() {
+						String ancestor = name;
+						while (true) {
+							LogLevel logLevel = levels.get(ancestor);
+							if (logLevel != null) {
+								return logLevel;
+							}
+
+							int n = ancestor.lastIndexOf('.');
+							if (n < 0) {
+								LogLevel logLevel2 = levels.get(Logger.ROOT_LOGGER_NAME);
+								if ( logLevel2 != null)
+									return logLevel2;
+								
+								return defaultLogLevel;
+							}
+
+							ancestor = ancestor.substring(0, n);
+						}
+					}
+
+				};
+				loggers.add(bundle, logger);
+				return loggerType.cast(logger);
+			}
+		};
+		factories.add(factory);
+		return factory;
+	}
+
+	@Override
+	public void ungetService(Bundle bundle, ServiceRegistration<LoggerFactory> registration, LoggerFactory service) {
+		factories.add(service);
+	}
+
+	public void clear() {
+		loggers.clear();
+		entries.clear();
+		factories.clear();
+		levels.clear();
+	}
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/logger/LoggerTest.java b/scr/src/test/java/org/apache/felix/scr/impl/logger/LoggerTest.java
new file mode 100644
index 0000000..383832e
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/logger/LoggerTest.java
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ */
+package org.apache.felix.scr.impl.logger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+import org.apache.felix.scr.impl.logger.InternalLogger.Level;
+import org.apache.felix.scr.impl.logger.LogManager.LoggerFacade;
+import org.apache.felix.scr.impl.logger.LogService.LogEntry;
+import org.apache.felix.scr.impl.manager.ScrConfiguration;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.log.LogLevel;
+import org.osgi.service.log.Logger;
+
+import aQute.bnd.osgi.Builder;
+import aQute.bnd.osgi.Jar;
+import aQute.bnd.osgi.JarResource;
+import aQute.lib.io.IO;
+
+public class LoggerTest {
+
+	private Framework	framework;
+	private Bundle		scr;
+	private Bundle		component;
+	private Bundle		log;
+	private File		tmp	= IO.getFile("target/tmp/loggertest");
+
+	@Before
+	public void setup() throws Exception {
+		Map<String, String> configuration = new HashMap<>();
+		configuration.put(Constants.FRAMEWORK_STORAGE, tmp.getAbsolutePath());
+		configuration.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
+		framework = ServiceLoader.load(org.osgi.framework.launch.FrameworkFactory.class).iterator().next()
+				.newFramework(configuration);
+		framework.init();
+		framework.start();
+
+		scr = framework.getBundleContext().installBundle("scr", makeBundle("scr").openInputStream());
+		component = framework.getBundleContext().installBundle("component",
+				makeBundle("component").openInputStream());
+		log = framework.getBundleContext().installBundle("component", makeBundle("log").openInputStream());
+		scr.start();
+		component.start();
+		log.start();
+	}
+
+	@After
+	public void after() throws Exception {
+		framework.stop();
+		framework.waitForStop(100000);
+	}
+
+	static class Buf extends FilterOutputStream {
+		StringWriter		buffer	= new StringWriter();
+		final PrintStream	stream;
+
+		public Buf(PrintStream out) {
+			super(out);
+			this.stream = out;
+		}
+
+		@Override
+		public void write(int b) throws IOException {
+			stream.write(b);
+			buffer.write(b);
+		}
+
+		PrintStream out() {
+			return new PrintStream(this);
+		}
+
+		public void reset() {
+			buffer = new StringWriter();
+		}
+	}
+
+	@Test
+	public void formatTest() {
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.getLogLevel()).thenReturn(Level.DEBUG);
+		ExtLogManager elm = new ExtLogManager(scr.getBundleContext(), config);
+
+		ComponentLogger clog = elm.component(component, "i.c", "c");
+
+		clog.log(Level.ERROR, "error {0} {1} {2}", null, "a", 1, scr);
+		LogEntry le = l.entries.get(0);
+		assertThat(le.format).isEqualTo("[c] error a 1 bundle scr:0.0.0 (1)");
+	}
+
+	@Test
+	public void testStandardOutput() throws IOException {
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.getLogLevel()).thenReturn(Level.DEBUG);
+		ExtLogManager elm = new ExtLogManager(scr.getBundleContext(), config);
+		try (Buf out = new Buf(System.out);
+				Buf err = new Buf(System.err);) {
+			try {
+				System.setOut(out.out());
+				System.setErr(err.out());
+				ScrLogger scr = elm.scr();
+				{
+					scr.log(Level.AUDIT, "audit ", null);
+					scr.log(Level.AUDIT, "audit", new Exception("FOOBAR"));
+					scr.log(Level.TRACE, "audit", new Exception("FOOBAR"));
+
+					String logged = (Level.AUDIT.err() ? err : out).buffer.toString();
+					assertThat(logged).startsWith("AUDIT : audit").contains("FOOBAR");
+				}
+
+				{
+					int n = 0;
+					reset(config);
+
+					for (Level configLevel : Level.values()) {
+
+						for (Level requestedLevel : Level.values()) {
+							out.reset();
+							err.reset();
+							when(config.getLogLevel()).thenReturn(configLevel);
+
+							String message = configLevel + ":" + requestedLevel;
+							scr.log(requestedLevel, message, null);
+							verify(config, times(++n)).getLogLevel();
+
+							String logged = (requestedLevel.err() ? err : out).buffer.toString();
+
+							if (configLevel.implies(requestedLevel)) {
+								assertThat(logged).contains(message);
+							} else {
+								assertThat(logged).isEmpty();
+							}
+						}
+					}
+				}
+			} finally {
+				System.setOut(out.stream);
+				System.setErr(err.stream);
+			}
+		}
+	}
+
+	@Test
+	public void testSelectionOfExtensionManager() {
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.isLogExtension()).thenReturn(true);
+		ScrLogger logger = ScrLogManager.scr(scr.getBundleContext(), config);
+		BundleLogger bundle = logger.bundle(component);
+		bundle.log(Level.ERROR, "Ext", null);
+		assertThat(l.entries).hasSize(1);
+		LogEntry le = l.entries.get(0);
+		assertThat(le.bundle).isEqualTo(scr);
+	}
+
+	@Test
+	public void testSelectionOfBaseManager() {
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.isLogExtension()).thenReturn(false);
+		ScrLogger logger = ScrLogManager.scr(scr.getBundleContext(), config);
+		BundleLogger bundle = logger.bundle(component);
+		bundle.log(Level.ERROR, "Ext", null);
+		assertThat(l.entries).hasSize(1);
+		LogEntry le = l.entries.get(0);
+		assertThat(le.bundle).isEqualTo(component);
+	}
+
+	@Test
+	public void testExtensionLogLevelNotLoggingWhenRootSetToInfoAndLevelIsDebug() {
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.isLogExtension()).thenReturn(true);
+
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+		l.levels.put(Logger.ROOT_LOGGER_NAME, LogLevel.INFO);
+		l.defaultLogLevel = LogLevel.TRACE;
+		
+		ScrLogger lscr = ScrLogManager.scr(scr.getBundleContext(), config);
+
+		assertThat(lscr.isLogEnabled(Level.DEBUG)).isFalse();
+		assertThat(lscr.isLogEnabled(Level.INFO)).isTrue();
+		
+		lscr.log(Level.DEBUG, "I should not be reported", null);
+
+		assertThat(l.entries).isEmpty();
+	}
+
+	@Test
+	public void testExtensionLogLevelNotLoggingWhenPartialNameSetToInfoAndLevelIsDebug() {
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.isLogExtension()).thenReturn(true);
+
+		LogService l = new LogService(log.getBundleContext());
+		l.defaultLogLevel = LogLevel.TRACE;
+		l.register();
+		l.levels.put("org.apache.felix.scr", LogLevel.INFO);
+
+		ScrLogger lscr = ScrLogManager.scr(scr.getBundleContext(), config);
+
+		assertThat(lscr.isLogEnabled(Level.DEBUG)).isFalse();
+		assertThat(lscr.isLogEnabled(Level.INFO)).isTrue();
+		
+		lscr.log(Level.DEBUG, "I should not be reported", null);
+
+		assertThat(l.entries).isEmpty();
+	}
+
+
+	@Test
+	public void testExtensionLogManager() {
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.isLogExtension()).thenReturn(true);
+
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+
+		ScrLogManager lm = new ExtLogManager(scr.getBundleContext(), config);
+
+		{
+			l.entries.clear();
+			ScrLogger scr = lm.scr();
+			scr.log(Level.ERROR, "Scr", null);
+			assertThat(l.entries).hasSize(1);
+			LogEntry le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("Scr");
+			assertThat(le.bundle).isEqualTo(this.scr);
+			assertThat(le.loggername).isEqualTo(ExtLogManager.SCR_LOGGER_NAME);
+		}
+
+		{
+			l.entries.clear();
+			BundleLogger blog = lm.bundle(component);
+			blog.log(Level.ERROR, "Bundle", null);
+			assertThat(l.entries).hasSize(1);
+			LogEntry le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("Bundle");
+			assertThat(le.bundle).isEqualTo(scr);
+			assertThat(le.loggername).isEqualTo(ExtLogManager.SCR_LOGGER_PREFIX + "component");
+		}
+
+		{
+			l.entries.clear();
+			ComponentLogger clog = lm.component(component, "implementation.class", "name");
+			clog.log(Level.ERROR, "Component", null);
+			assertThat(l.entries).hasSize(1);
+			LogEntry le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("[name] Component");
+			assertThat(le.bundle).isEqualTo(scr);
+			assertThat(le.loggername).isEqualTo(ExtLogManager.SCR_LOGGER_PREFIX + "component.name");
+
+			l.entries.clear();
+			clog.setComponentId(100);
+			clog.log(Level.ERROR, "Component", null);
+			le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("[name(100)] Component");
+		}
+
+		{
+			lm.scr().close();
+		}
+	}
+
+	@Test
+	public void testBackwardCompatibilityOutput() {
+		ScrConfiguration config = mock(ScrConfiguration.class);
+		when(config.isLogExtension()).thenReturn(false);
+
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+
+		ScrLogManager lm = new ScrLogManager(scr.getBundleContext(), config);
+
+		{
+			l.entries.clear();
+			ScrLogger scr = lm.scr();
+			scr.log(Level.ERROR, "Scr", null);
+			assertThat(l.entries).hasSize(1);
+			LogEntry le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("bundle scr:0.0.0 (1) Scr");
+			assertThat(le.bundle).isEqualTo(this.scr);
+			assertThat(le.loggername).isEqualTo(Logger.ROOT_LOGGER_NAME);
+		}
+
+		{
+			l.entries.clear();
+			BundleLogger blog = lm.bundle(component);
+			blog.log(Level.ERROR, "Bundle", null);
+			assertThat(l.entries).hasSize(1);
+			LogEntry le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("bundle component:0.0.0 (2) Bundle");
+			assertThat(le.bundle).isEqualTo(component);
+			assertThat(le.loggername).isEqualTo(Logger.ROOT_LOGGER_NAME);
+		}
+
+		{
+			l.entries.clear();
+			ComponentLogger clog = lm.component(component, "implementation.class", "name");
+			clog.log(Level.ERROR, "Component", null);
+			assertThat(l.entries).hasSize(1);
+			LogEntry le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("bundle component:0.0.0 (2)[implementation.class] : Component");
+			assertThat(le.bundle).isEqualTo(component);
+			assertThat(le.loggername).isEqualTo("implementation.class");
+
+			l.entries.clear();
+			clog.setComponentId(100);
+			clog.log(Level.ERROR, "Component", null);
+			le = l.entries.get(0);
+			assertThat(le.format).isEqualTo("bundle component:0.0.0 (2)[implementation.class(100)] : Component");
+		}
+	}
+
+	@Test
+	public void testLifeCycle() {
+
+		LogManager lm = new LogManager(scr.getBundleContext());
+		LoggerFacade facade = lm.getLogger(scr, "lifecycle", LoggerFacade.class);
+		assertThat(facade.logger).isNull();
+
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+		assertThat(l.loggers.get(scr)).isNull();
+
+		Logger logger = facade.getLogger();
+
+		assertThat(logger).isNotNull();
+		assertThat(facade.logger).isEqualTo(logger);
+		assertThat(l.loggers.get(scr)).hasSize(1);
+
+		assertThat(logger.getName()).isEqualTo("lifecycle");
+
+		Logger logger2 = facade.getLogger();
+		assertThat(logger2).isEqualTo(logger);
+		assertThat(l.loggers.get(scr)).hasSize(1);
+
+		l.unregister();
+		l.register();
+
+		assertThat(facade.logger).isNull();
+		logger = facade.getLogger();
+		assertThat(l.loggers.get(scr)).hasSize(2);
+
+		assertThat(facade.logger).isNotNull();
+		lm.close();
+		assertThat(facade.logger).isNull();
+	}
+
+	@Test
+	public void testLifeCycleOfComponentBundle() throws BundleException, InterruptedException {
+
+		ScrConfiguration config = mock(ScrConfiguration.class);
+
+		LogService l = new LogService(log.getBundleContext());
+		l.register();
+
+		ScrLogManager lm = new ScrLogManager(scr.getBundleContext(), config);
+		lm.component(component, "implementation.class", "component");
+
+		assertThat(lm.domains).hasSize(1);
+		component.stop();
+
+		for (int i = 0; i < 100; i++) {
+			if (lm.domains.isEmpty())
+				return;
+			Thread.sleep(10);
+		}
+		lm.close();
+		fail("domains not cleared within 1 sec");
+	}
+
+	@Test
+	public void testLogLevels() {
+
+		ScrConfiguration config = mock(ScrConfiguration.class);
+
+		when(config.getLogLevel()).thenReturn(Level.DEBUG);
+		when(config.isLogExtension()).thenReturn(false);
+
+		LogService l = new LogService(log.getBundleContext());
+
+		l.register();
+
+		ScrLogManager lm = new ScrLogManager(scr.getBundleContext(), config);
+		ScrLogger facade = lm.scr();
+
+		assert LogLevel.values().length == Level.values().length;
+		for (int i = 0; i < LogLevel.values().length; i++) {
+			assert LogLevel.values()[i].name().equals(Level.values()[i].name());
+		}
+
+		Exception ex = new Exception("exception");
+
+		for (LogLevel logLevel : LogLevel.values()) {
+
+			l.defaultLogLevel = logLevel;
+
+			for (Level level : Level.values()) {
+
+				if (logLevel.ordinal() >= level.ordinal())
+					assertThat(facade.isLogEnabled(level)).isTrue();
+				else
+					assertThat(facade.isLogEnabled(level)).isFalse();
+
+				facade.log(level, "level " + level, null);
+				facade.log(level, "level " + level, ex);
+			}
+		}
+		assertThat(l.entries).hasSize(42);
+	}
+
+	private JarResource makeBundle(String bsn) throws Exception {
+		@SuppressWarnings("resource")
+		Builder b = new Builder();
+		b.setBundleSymbolicName(bsn);
+		Jar jar = b.build();
+		b.removeClose(jar);
+		return new JarResource(jar);
+	}
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/logger/MockBundleLogger.java b/scr/src/test/java/org/apache/felix/scr/impl/logger/MockBundleLogger.java
index acbd8fb..3040101 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/logger/MockBundleLogger.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/logger/MockBundleLogger.java
@@ -21,18 +21,11 @@ package org.apache.felix.scr.impl.logger;
 
 import java.text.MessageFormat;
 
-import org.apache.felix.scr.impl.MockBundle;
-import org.apache.felix.scr.impl.logger.InternalLogger.Level;
+import org.osgi.framework.Bundle;
 
 
-public class MockBundleLogger extends BundleLogger
+public class MockBundleLogger implements BundleLogger
 {
-    public MockBundleLogger()
-    {
-        super(new MockBundle(), new MockScrLogger());
-    }
-
-
     private String lastMessage;
 
 
@@ -43,22 +36,20 @@ public class MockBundleLogger extends BundleLogger
     }
 
     @Override
-    public boolean log(final Level level, final String pattern, final Throwable ex,
+    public void log(final Level level, final String pattern, final Throwable ex,
         final Object... arguments)
     {
         if ( isLogEnabled( level ) )
         {
             log( level,  MessageFormat.format( pattern, arguments ), ex );
         }
-        return true;
     }
 
 
     @Override
-    public boolean log(final Level level, final String message, final Throwable ex)
+    public void log(final Level level, final String message, final Throwable ex)
     {
         lastMessage = message;
-        return true;
     }
 
 
@@ -66,4 +57,10 @@ public class MockBundleLogger extends BundleLogger
     {
         return lastMessage != null && lastMessage.indexOf( value ) >= 0;
     }
+
+	@Override
+	public ComponentLogger component(Bundle m_bundle, String implementationClassName, String name) {
+		return new MockComponentLogger();
+	}
+
 }
\ No newline at end of file
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/logger/MockComponentLogger.java b/scr/src/test/java/org/apache/felix/scr/impl/logger/MockComponentLogger.java
index 8194ebe..18f7575 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/logger/MockComponentLogger.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/logger/MockComponentLogger.java
@@ -18,29 +18,23 @@
  */
 package org.apache.felix.scr.impl.logger;
 
-import org.apache.felix.scr.impl.logger.InternalLogger.Level;
-import org.apache.felix.scr.impl.metadata.ComponentMetadata;
-import org.apache.felix.scr.impl.metadata.DSVersion;
-
-public class MockComponentLogger extends ComponentLogger
+public class MockComponentLogger implements ComponentLogger
 {
-    public MockComponentLogger()
-    {
-        super(new ComponentMetadata(DSVersion.DS14), new MockBundleLogger());
-    }
 
-    @Override
-    public boolean log(final Level level, final String pattern, final Throwable ex,
-        final Object... arguments)
-    {
-        // ignore
-        return true;
-    }
+	@Override
+	public void log(Level level, String message, Throwable ex) {
+	}
+
+	@Override
+	public void log(Level level, String message, Throwable ex, Object... args) {
+	}
+
+	@Override
+	public boolean isLogEnabled(Level level) {
+		return true;
+	}
 
-    @Override
-    public boolean log(final Level level, final String message, final Throwable ex)
-    {
-        // ignore
-        return true;
-    }
+	@Override
+	public void setComponentId(long m_componentId) {
+	}
 }
\ No newline at end of file
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java b/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
index f2f2f94..3634439 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
@@ -18,72 +18,31 @@
  */
 package org.apache.felix.scr.impl.logger;
 
+import org.osgi.framework.Bundle;
 
-import org.apache.felix.scr.impl.MockBundle;
-import org.apache.felix.scr.impl.MockBundleContext;
-import org.apache.felix.scr.impl.logger.InternalLogger.Level;
-import org.apache.felix.scr.impl.manager.ScrConfiguration;
-
-
-public class MockScrLogger extends ScrLogger
+public class MockScrLogger implements ScrLogger
 {
-    public MockScrLogger()
-    {
-        super(new ScrConfiguration()
-        {
-
-            @Override
-            public long stopTimeout()
-            {
-                return 0;
-            }
-
-            @Override
-            public long lockTimeout()
-            {
-                return 0;
-            }
-
-            @Override
-            public boolean keepInstances()
-            {
-                return false;
-            }
-
-            @Override
-            public boolean isFactoryEnabled()
-            {
-                return false;
-            }
+	@Override
+	public void log(Level level, String message, Throwable ex) {		
+	}
 
-            @Override
-            public boolean infoAsService()
-            {
-                return false;
-            }
+	@Override
+	public void log(Level level, String message, Throwable ex, Object... args) {
+		
+	}
 
-            @Override
-            public long serviceChangecountTimeout()
-            {
-                return 0;
-            }
+	@Override
+	public boolean isLogEnabled(Level level) {
+		return true;
+	}
 
-            @Override
-            public Level getLogLevel()
-            {
-                return Level.ERROR;
-            }
+	@Override
+	public BundleLogger bundle(Bundle bundle) {
+		return new MockBundleLogger();
+	}
 
-            @Override
-            public boolean globalExtender() {
-                return false;
-            }
+	@Override
+	public void close() {
+	}
 
-            @Override
-            public boolean cacheMetadata()
-            {
-                return false;
-            }
-        }, new MockBundleContext(new MockBundle()));
-    }
 }
\ No newline at end of file