You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2013/08/28 07:56:34 UTC

svn commit: r1518083 [2/3] - in /sling/trunk/bundles/commons/log: ./ src/main/appended-resources/ src/main/appended-resources/META-INF/ src/main/java/org/apache/sling/commons/log/internal/ src/main/java/org/apache/sling/commons/log/logback/ src/main/ja...

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,220 @@
+/*
+ * 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.sling.commons.log.logback.internal;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.OutputStreamAppender;
+import ch.qos.logback.core.encoder.Encoder;
+import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
+import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
+import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
+
+import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender;
+
+/**
+ * The <code>LogWriter</code> class encapsulates the OSGi configuration for a
+ * log writer and provides methods to access these to create an Appender.
+ */
+public class LogWriter {
+
+    /**
+     * Special fileName for which Console Appender would be created
+     */
+    public static final String FILE_NAME_CONSOLE = "CONSOLE";
+
+    private static final long FACTOR_KB = 1024;
+
+    private static final long FACTOR_MB = 1024 * FACTOR_KB;
+
+    private static final long FACTOR_GB = 1024 * FACTOR_MB;
+
+    /**
+     * Regular expression matching a maximum file size specification. This
+     * pattern case-insensitively matches a number and an optional factor
+     * specifier of the forms k, kb, m, mb, g, or gb.
+     */
+    private static final Pattern SIZE_SPEC = Pattern.compile("([\\d]+)([kmg]b?)?", Pattern.CASE_INSENSITIVE);
+
+    /**
+     * The PID of the configuration from which this instance has been
+     * configured. If this is <code>null</code> this instance is an implicitly
+     * created instance which is not tied to any configuration.
+     */
+    private final String configurationPID;
+
+    private final String fileName;
+
+    private final int logNumber;
+
+    private final String logRotation;
+
+    public LogWriter(String configurationPID, String fileName, int logNumber, String logRotation) {
+        if (fileName == null || fileName.length() == 0) {
+            fileName = FILE_NAME_CONSOLE;
+        }
+
+        if (logNumber < 0) {
+            logNumber = LogConfigManager.LOG_FILE_NUMBER_DEFAULT;
+        }
+
+        if (logRotation == null || logRotation.length() == 0) {
+            logRotation = LogConfigManager.LOG_FILE_SIZE_DEFAULT;
+        }
+
+        this.configurationPID = configurationPID;
+        this.fileName = fileName;
+        this.logNumber = logNumber;
+        this.logRotation = logRotation;
+    }
+
+    public LogWriter(String fileName, int logNumber, String logRotation) {
+        this(null, fileName, logNumber, logRotation);
+    }
+
+    public String getConfigurationPID() {
+        return configurationPID;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public int getLogNumber() {
+        return logNumber;
+    }
+
+    public String getLogRotation() {
+        return logRotation;
+    }
+
+    public boolean isImplicit() {
+        return configurationPID == null;
+    }
+
+    public Appender<ILoggingEvent> createAppender(final Context context, final Encoder<ILoggingEvent> encoder) {
+
+        OutputStreamAppender<ILoggingEvent> appender;
+        if (FILE_NAME_CONSOLE.equals(fileName)) {
+            appender = new ConsoleAppender<ILoggingEvent>();
+            appender.setName(FILE_NAME_CONSOLE);
+        } else {
+            SlingRollingFileAppender<ILoggingEvent> rollingAppender = new SlingRollingFileAppender<ILoggingEvent>();
+            rollingAppender.setAppend(true);
+            rollingAppender.setFile(getFileName());
+
+            Matcher sizeMatcher = SIZE_SPEC.matcher(getLogRotation());
+            if (sizeMatcher.matches()) {
+                // group 1 is the base size and is an integer number
+                final long baseSize = Long.parseLong(sizeMatcher.group(1));
+
+                // this will take the final size value
+                final long maxSize;
+
+                // group 2 is optional and is the size spec. If not null it is
+                // at least one character long and the first character is enough
+                // for use to know (the second is of no use here)
+                final String factorString = sizeMatcher.group(2);
+                if (factorString == null) {
+                    // no factor define, hence no multiplication
+                    maxSize = baseSize;
+                } else {
+                    switch (factorString.charAt(0)) {
+                        case 'k':
+                        case 'K':
+                            maxSize = baseSize * FACTOR_KB;
+                            break;
+                        case 'm':
+                        case 'M':
+                            maxSize = baseSize * FACTOR_MB;
+                            break;
+                        case 'g':
+                        case 'G':
+                            maxSize = baseSize * FACTOR_GB;
+                            break;
+                        default:
+                            // we don't really expect this according to the
+                            // pattern
+                            maxSize = baseSize;
+                    }
+                }
+
+                SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>();
+                triggeringPolicy.setMaxFileSize(String.valueOf(maxSize));
+                triggeringPolicy.setContext(context);
+                triggeringPolicy.start();
+                rollingAppender.setTriggeringPolicy(triggeringPolicy);
+
+                FixedWindowRollingPolicy pol = new FixedWindowRollingPolicy();
+                pol.setMinIndex(1);
+                pol.setMaxIndex(getLogNumber());
+                pol.setFileNamePattern(getFileName() + "%i");
+                pol.setContext(context);
+                pol.setParent(rollingAppender);
+                pol.start();
+                rollingAppender.setRollingPolicy(pol);
+            } else {
+                TimeBasedRollingPolicy<ILoggingEvent> policy = new TimeBasedRollingPolicy<ILoggingEvent>();
+                policy.setFileNamePattern(createFileNamePattern(getFileName(), getLogRotation()));
+                policy.setMaxHistory(getLogNumber());
+                policy.setContext(context);
+                policy.setParent(rollingAppender);
+                policy.start();
+                rollingAppender.setTriggeringPolicy(policy);
+            }
+
+            rollingAppender.setLogWriter(this);
+            rollingAppender.setName(getFileName());
+
+            appender = rollingAppender;
+        }
+
+        appender.setContext(context);
+        appender.setEncoder(encoder);
+        appender.start();
+        return appender;
+    }
+
+    public static String createFileNamePattern(String fileName, String pattern) {
+        // Default file name pattern "'.'yyyy-MM-dd"
+        // http://sling.apache.org/site/logging.html#Logging-ScheduledRotation
+        if (pattern.startsWith("'.'")) {
+            pattern = pattern.substring(3); // 3 = '.' length
+            pattern = ".%d{" + pattern + "}";
+        }
+
+        // Legacy pattern which does not start with '.' Just wrap them with %d{}
+        if (!pattern.contains("%d{")) {
+            pattern = "%d{" + pattern + "}";
+        }
+        return fileName + pattern;
+
+    }
+
+    @Override
+    public String toString() {
+        return "LogWriter{" + "configurationPID='" + configurationPID + '\'' + ", fileName='" + fileName + '\''
+            + ", logNumber=" + logNumber + ", logRotation='" + logRotation + '\'' + '}';
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,532 @@
+package org.apache.sling.commons.log.logback.internal;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.gaffer.GafferUtil;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.LoggerContextAwareBase;
+import ch.qos.logback.classic.spi.LoggerContextListener;
+import ch.qos.logback.classic.util.EnvUtil;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.joran.GenericConfigurator;
+import ch.qos.logback.core.joran.event.SaxEvent;
+import ch.qos.logback.core.joran.spi.InterpretationContext;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.status.OnConsoleStatusListener;
+import ch.qos.logback.core.status.StatusListener;
+import ch.qos.logback.core.status.StatusListenerAsList;
+import ch.qos.logback.core.status.StatusUtil;
+import ch.qos.logback.core.util.StatusPrinter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.LoggerFactory;
+
+public class LogbackManager extends LoggerContextAwareBase {
+    private static final String PREFIX = "org.apache.sling.commons.log";
+
+    private static final String DEBUG = PREFIX + "." + "debug";
+
+    private static final String PLUGIN_URL = "slinglog";
+
+    private static final String PRINTER_URL = "slinglogs";
+
+    private static final String RESET_EVENT_TOPIC = "org/apache/sling/commons/log/RESET";
+
+    private final String rootDir;
+
+    private final String contextName = "sling";
+
+    private final LogConfigManager logConfigManager;
+
+    private final List<LogbackResetListener> resetListeners = new ArrayList<LogbackResetListener>();
+
+    private final org.slf4j.Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * Acts as a bridge between Logback and OSGi
+     */
+    private final LoggerContextListener osgiIntegrationListener = new OsgiIntegrationListener();
+
+    private final boolean debug;
+
+    private final boolean started;
+
+    private final Semaphore resetLock = new Semaphore(1);
+
+    private final AtomicBoolean configChanged = new AtomicBoolean();
+
+    private final AppenderTracker appenderTracker;
+
+    private final ConfigSourceTracker configSourceTracker;
+
+    private final List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
+
+    /**
+     * Time at which reset started. Used as the threshold for logging error
+     * messages from status printer
+     */
+    private volatile long resetStartTime;
+
+    public LogbackManager(BundleContext bundleContext) throws InvalidSyntaxException {
+        final long startTime = System.currentTimeMillis();
+        setLoggerContext((LoggerContext) LoggerFactory.getILoggerFactory());
+
+        this.rootDir = getRootDir(bundleContext);
+
+        this.debug = Boolean.parseBoolean(bundleContext.getProperty(DEBUG));
+
+        this.appenderTracker = new AppenderTracker(bundleContext, getLoggerContext());
+        this.configSourceTracker = new ConfigSourceTracker(bundleContext, this);
+
+        // TODO Make it configurable
+        // TODO: what should it be ?
+        getLoggerContext().setName(contextName);
+        this.logConfigManager = new LogConfigManager(getLoggerContext(), bundleContext, rootDir, this);
+
+        resetListeners.add(logConfigManager);
+        resetListeners.add(appenderTracker);
+        resetListeners.add(configSourceTracker);
+
+        getLoggerContext().addListener(osgiIntegrationListener);
+
+        configure();
+        registerWebConsoleSupport(bundleContext);
+        registerEventHandler(bundleContext);
+        StatusPrinter.printInCaseOfErrorsOrWarnings(getLoggerContext(), startTime);
+        started = true;
+    }
+
+    public void shutdown() {
+        for (ServiceRegistration reg : registrations) {
+            reg.unregister();
+        }
+
+        appenderTracker.close();
+        configSourceTracker.close();
+        getLoggerContext().removeListener(osgiIntegrationListener);
+        logConfigManager.close();
+        getLoggerContext().stop();
+    }
+
+    public void configChanged() {
+        if (!started) {
+            return;
+        }
+
+        if (resetLock.tryAcquire()) {
+            scheduleConfigReload();
+        } else {
+            configChanged.set(true);
+            addInfo("LoggerContext reset in progress. Marking config changed to true");
+        }
+    }
+
+    public LogConfigManager getLogConfigManager() {
+        return logConfigManager;
+    }
+
+    public AppenderTracker getAppenderTracker() {
+        return appenderTracker;
+    }
+
+    public ConfigSourceTracker getConfigSourceTracker() {
+        return configSourceTracker;
+    }
+
+    public void addSubsitutionProperties(InterpretationContext ic) {
+        ic.addSubstitutionProperty("sling.home", rootDir);
+    }
+
+    public URL getDefaultConfig() {
+        return getClass().getClassLoader().getResource("logback-empty.xml");
+    }
+
+    private void configure() {
+        ConfiguratorCallback cb = new DefaultCallback();
+
+        // Check first for an explicit configuration file
+        File configFile = logConfigManager.getLogbackConfigFile();
+        if (configFile != null) {
+            cb = new FilenameConfiguratorCallback(configFile);
+        }
+
+        configure(cb);
+    }
+
+    private void configure(ConfiguratorCallback cb) {
+        StatusListener statusListener = new StatusListenerAsList();
+        if (debug) {
+            statusListener = new OnConsoleStatusListener();
+        }
+
+        getStatusManager().add(statusListener);
+        addInfo("Resetting context: " + getLoggerContext().getName());
+        resetContext(statusListener);
+
+        StatusUtil statusUtil = new StatusUtil(getLoggerContext());
+        JoranConfigurator configurator = createConfigurator();
+        final List<SaxEvent> eventList = configurator.recallSafeConfiguration();
+        final long threshold = System.currentTimeMillis();
+
+        try {
+            cb.perform(configurator);
+            if (statusUtil.hasXMLParsingErrors(threshold)) {
+                cb.fallbackConfiguration(eventList, createConfigurator(), statusListener);
+            }
+            addInfo("Context: " + getLoggerContext().getName() + " reloaded.");
+        } catch (JoranException je) {
+            cb.fallbackConfiguration(eventList, createConfigurator(), statusListener);
+        } finally {
+            getStatusManager().remove(statusListener);
+            StatusPrinter.printInCaseOfErrorsOrWarnings(getLoggerContext(), resetStartTime);
+        }
+    }
+
+    private JoranConfigurator createConfigurator() {
+        SlingConfigurator configurator = new SlingConfigurator();
+        configurator.setContext(getLoggerContext());
+        return configurator;
+    }
+
+    private void resetContext(StatusListener statusListener) {
+        getLoggerContext().reset();
+
+        // after a reset the statusListenerAsList gets removed as a listener
+        if (statusListener != null && !getStatusManager().getCopyOfStatusListenerList().contains(statusListener)) {
+            getStatusManager().add(statusListener);
+        }
+    }
+
+    private void scheduleConfigReload() {
+        getLoggerContext().getExecutorService().submit(new LoggerReconfigurer());
+    }
+
+    private String getRootDir(BundleContext bundleContext) {
+        String rootDir = bundleContext.getProperty("sling.home");
+        if (rootDir == null) {
+            rootDir = new File(".").getAbsolutePath();
+        }
+        addInfo("Using rootDir as " + rootDir);
+        return rootDir;
+    }
+
+    private class LoggerReconfigurer implements Runnable {
+
+        public void run() {
+            // TODO Might be better to run a job to monitor refreshRequirement
+            boolean configChanged = false;
+            try {
+                addInfo("Performing configuration");
+                configure();
+                configChanged = LogbackManager.this.configChanged.getAndSet(false);
+                if (configChanged) {
+                    scheduleConfigReload();
+                }
+            } catch (Exception e) {
+                log.warn("Error occurred while re-configuring logger", e);
+                addError("Error occurred while re-configuring logger", e);
+            } finally {
+                if (!configChanged) {
+                    resetLock.release();
+                    addInfo("Re configuration done");
+                }
+            }
+        }
+    }
+
+    // ~-------------------------------LogggerContextListener
+
+    private class OsgiIntegrationListener implements LoggerContextListener {
+
+        public boolean isResetResistant() {
+            // The integration listener has to survive resets from other causes
+            // like reset when Logback detects change in config file and reloads
+            // on
+            // on its own in ReconfigureOnChangeFilter
+            return true;
+        }
+
+        public void onStart(LoggerContext context) {
+        }
+
+        public void onReset(LoggerContext context) {
+            addInfo("OsgiIntegrationListener : context reset detected. Adding LogManager to context map and firing"
+                + " listeners");
+
+            // Attach a console appender to handle logging untill we configure
+            // one. This would be
+            // removed in LogConfigManager.reset
+            getLoggerContext().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).addAppender(
+                logConfigManager.getDefaultAppender());
+
+            // Now record the time of reset with a default appender attached to
+            // root logger
+            resetStartTime = System.currentTimeMillis();
+
+            context.putObject(LogbackManager.class.getName(), LogbackManager.this);
+            for (LogbackResetListener l : resetListeners) {
+                l.onReset(context);
+            }
+        }
+
+        public void onStop(LoggerContext context) {
+        }
+
+        public void onLevelChange(Logger logger, Level level) {
+        }
+
+    }
+
+    // ~--------------------------------Configurator Base
+
+    private class SlingConfigurator extends JoranConfigurator {
+
+        @Override
+        protected void buildInterpreter() {
+            super.buildInterpreter();
+            addSubsitutionProperties(interpreter.getInterpretationContext());
+        }
+    }
+
+    // ~--------------------------------Configuration Support
+
+    private abstract class ConfiguratorCallback {
+        abstract void perform(JoranConfigurator configurator) throws JoranException;
+
+        /**
+         * Logic based on
+         * ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter.
+         * ReconfiguringThread
+         */
+        public void fallbackConfiguration(List<SaxEvent> eventList, JoranConfigurator configurator,
+                StatusListener statusListener) {
+            URL mainURL = getMainUrl();
+            if (mainURL != null) {
+                if (eventList != null) {
+                    addWarn("Falling back to previously registered safe configuration.");
+                    try {
+                        resetContext(statusListener);
+                        GenericConfigurator.informContextOfURLUsedForConfiguration(context, mainURL);
+                        configurator.doConfigure(eventList);
+                        addInfo("Re-registering previous fallback configuration once more as a fallback configuration point");
+                        configurator.registerSafeConfiguration();
+                    } catch (JoranException e) {
+                        addError("Unexpected exception thrown by a configuration considered safe.", e);
+                    }
+                } else {
+                    addWarn("No previous configuration to fall back on.");
+                }
+            }
+        }
+
+        protected URL getMainUrl() {
+            return null;
+        }
+    }
+
+    private class FilenameConfiguratorCallback extends ConfiguratorCallback {
+        private final File configFile;
+
+        public FilenameConfiguratorCallback(File configFile) {
+            this.configFile = configFile;
+        }
+
+        public void perform(JoranConfigurator configurator) throws JoranException {
+            final String path = configFile.getAbsolutePath();
+            addInfo("Configuring from " + path);
+            if (configFile.getName().endsWith("xml")) {
+                configurator.doConfigure(configFile);
+            } else if (configFile.getName().endsWith("groovy")) {
+                if (EnvUtil.isGroovyAvailable()) {
+                    // avoid directly referring to GafferConfigurator so as to
+                    // avoid
+                    // loading groovy.lang.GroovyObject . See also
+                    // http://jira.qos.ch/browse/LBCLASSIC-214
+                    GafferUtil.runGafferConfiguratorOn(getLoggerContext(), this, configFile);
+                } else {
+                    addError("Groovy classes are not available on the class path. ABORTING INITIALIZATION.");
+                }
+            }
+        }
+
+        @Override
+        protected URL getMainUrl() {
+            try {
+                return configFile.toURI().toURL();
+            } catch (MalformedURLException e) {
+                addWarn("Cannot convert file to url " + configFile.getAbsolutePath(), e);
+                return null;
+            }
+        }
+    }
+
+    private class DefaultCallback extends ConfiguratorCallback {
+        public void perform(JoranConfigurator configurator) throws JoranException {
+            configurator.doConfigure(getMainUrl());
+        }
+
+        @Override
+        protected URL getMainUrl() {
+            return getDefaultConfig();
+        }
+    }
+
+    // ~ ----------------------------------------------WebConsole Support
+
+    public LoggerStateContext determineLoggerState() {
+        final List<Logger> loggers = getLoggerContext().getLoggerList();
+        final LoggerStateContext ctx = new LoggerStateContext(loggers);
+        for (Logger logger : loggers) {
+            if (logger.iteratorForAppenders().hasNext() || logger.getLevel() != null) {
+                ctx.loggerInfos.add(logger);
+            }
+
+            Iterator<Appender<ILoggingEvent>> itr = logger.iteratorForAppenders();
+            while (itr.hasNext()) {
+                Appender<ILoggingEvent> a = itr.next();
+                if (a.getName() != null && !ctx.appenders.containsKey(a.getName())) {
+                    ctx.appenders.put(a.getName(), a);
+                }
+            }
+        }
+        return ctx;
+    }
+
+    public class LoggerStateContext {
+        final LoggerContext loggerContext = getLoggerContext();
+
+        final List<Logger> allLoggers;
+
+        /**
+         * List of logger which have explicitly defined level or appenders set
+         */
+        final List<Logger> loggerInfos = new ArrayList<Logger>();
+
+        final Map<String, Appender<ILoggingEvent>> appenders = new HashMap<String, Appender<ILoggingEvent>>();
+
+        final Map<Appender<ILoggingEvent>, AppenderTracker.AppenderInfo> dynamicAppenders = new HashMap<Appender<ILoggingEvent>, AppenderTracker.AppenderInfo>();
+
+        private LoggerStateContext(List<Logger> allLoggers) {
+            this.allLoggers = allLoggers;
+            for (AppenderTracker.AppenderInfo ai : getAppenderTracker().getAppenderInfos()) {
+                dynamicAppenders.put(ai.appender, ai);
+            }
+        }
+
+        int getNumberOfLoggers() {
+            return allLoggers.size();
+        }
+
+        int getNumOfDynamicAppenders() {
+            return getAppenderTracker().getAppenderInfos().size();
+        }
+
+        int getNumOfAppenders() {
+            return appenders.size();
+        }
+
+        boolean isDynamicAppender(Appender<ILoggingEvent> a) {
+            return dynamicAppenders.containsKey(a);
+        }
+
+        Collection<Appender<ILoggingEvent>> getAllAppenders() {
+            return appenders.values();
+        }
+    }
+
+    private void registerWebConsoleSupport(BundleContext context) {
+        final ServiceFactory serviceFactory = new PluginServiceFactory(PLUGIN_URL);
+
+        Properties pluginProps = new Properties();
+        pluginProps.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+        pluginProps.put(Constants.SERVICE_DESCRIPTION, "Sling Log Support");
+        pluginProps.put("felix.webconsole.label", PLUGIN_URL);
+        pluginProps.put("felix.webconsole.title", "Log Support");
+        pluginProps.put("felix.webconsole.category", "Sling");
+        pluginProps.put("felix.webconsole.css", new String[] {
+            "/" + PLUGIN_URL + "/res/ui/prettify.css", "/" + PLUGIN_URL + "/res/ui/log.css"
+        });
+
+        registrations.add(context.registerService("javax.servlet.Servlet", serviceFactory, pluginProps));
+
+        Properties printerProps = new Properties();
+        printerProps.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+        printerProps.put(Constants.SERVICE_DESCRIPTION, "Sling Log Configuration Printer");
+        printerProps.put("felix.webconsole.label", PRINTER_URL);
+        printerProps.put("felix.webconsole.title", "Log Files");
+        printerProps.put("felix.webconsole.configprinter.modes", "always");
+
+        // TODO need to see to add support for Inventory Feature
+        registrations.add(context.registerService(SlingConfigurationPrinter.class.getName(),
+            new SlingConfigurationPrinter(this), printerProps));
+    }
+
+    private class PluginServiceFactory implements ServiceFactory {
+        private Object instance;
+
+        private final String label;
+
+        private PluginServiceFactory(String label) {
+            this.label = label;
+        }
+
+        public Object getService(Bundle bundle, ServiceRegistration registration) {
+            synchronized (this) {
+                if (this.instance == null) {
+                    this.instance = new SlingLogPanel(LogbackManager.this, label);
+                }
+                return instance;
+            }
+        }
+
+        public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+        }
+    }
+
+    private void registerEventHandler(BundleContext bundleContext) {
+        Properties props = new Properties();
+        props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+        props.put(Constants.SERVICE_DESCRIPTION, "Sling Log Reset Event Handler");
+        props.put("event.topics", new String[] {
+            RESET_EVENT_TOPIC
+        });
+
+        registrations.add(bundleContext.registerService("org.osgi.service.event.EventHandler", new ServiceFactory() {
+            private Object instance;
+
+            @Override
+            public Object getService(Bundle bundle, ServiceRegistration serviceRegistration) {
+                synchronized (this) {
+                    if (this.instance == null) {
+                        this.instance = new ConfigResetRequestHandler(LogbackManager.this);
+                    }
+                    return instance;
+                }
+            }
+
+            @Override
+            public void ungetService(Bundle bundle, ServiceRegistration serviceRegistration, Object o) {
+            }
+        }, props));
+    }
+
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackResetListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackResetListener.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackResetListener.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackResetListener.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,27 @@
+/*
+ * 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.sling.commons.log.logback.internal;
+
+import ch.qos.logback.classic.LoggerContext;
+
+public interface LogbackResetListener {
+
+    void onReset(LoggerContext context);
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/OsgiInternalAction.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/OsgiInternalAction.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/OsgiInternalAction.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/OsgiInternalAction.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,121 @@
+/*
+ * 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.sling.commons.log.logback.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import ch.qos.logback.core.joran.action.Action;
+import ch.qos.logback.core.joran.event.SaxEvent;
+import ch.qos.logback.core.joran.event.SaxEventRecorder;
+import ch.qos.logback.core.joran.spi.ActionException;
+import ch.qos.logback.core.joran.spi.InterpretationContext;
+import ch.qos.logback.core.joran.spi.JoranException;
+
+import org.apache.sling.commons.log.logback.internal.util.Util;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+
+import static org.apache.sling.commons.log.logback.internal.ConfigSourceTracker.ConfigSourceInfo;
+
+/**
+ * Joran action enabling integration between OSGi and Logback. It is based on
+ * {@link ch.qos.logback.core.joran.action.IncludeAction}. It supports including
+ * config fragments provided through OSGi ServiceRegistry
+ */
+public class OsgiInternalAction extends Action {
+    private static final String INCLUDED_TAG = "included";
+
+    @Override
+    public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException {
+        populateSubstitutionProperties(ec);
+
+        // TO CHECK Should we add the config fragment at end
+        final Collection<ConfigSourceInfo> providers = getFragmentProviders();
+        List<SaxEvent> consolidatedEventList = new ArrayList<SaxEvent>();
+        for (ConfigSourceInfo cp : providers) {
+            InputSource is = cp.getConfigProvider().getConfigSource();
+            try {
+                SaxEventRecorder recorder = new SaxEventRecorder(context);
+                recorder.recordEvents(is);
+                // remove the <included> tag from the beginning and </included>
+                // from the end
+                trimHeadAndTail(recorder);
+                consolidatedEventList.addAll(recorder.getSaxEventList());
+            } catch (JoranException e) {
+                addError("Error while parsing xml obtained from  [" + cp + "]", e);
+            } finally {
+                Util.close(is);
+            }
+        }
+
+        // offset = 2, because we need to get past this element as well as the
+        // end element
+        ec.getJoranInterpreter().getEventPlayer().addEventsDynamically(consolidatedEventList, 2);
+
+    }
+
+    private void populateSubstitutionProperties(InterpretationContext ec) {
+        getLogbackManager().addSubsitutionProperties(ec);
+    }
+
+    @Override
+    public void end(InterpretationContext ec, String name) throws ActionException {
+        // do nothing
+    }
+
+    private Collection<ConfigSourceInfo> getFragmentProviders() {
+        ConfigSourceTracker tracker = (ConfigSourceTracker) getContext().getObject(ConfigSourceTracker.class.getName());
+        if (tracker != null) {
+            return tracker.getSources();
+        }
+        return Collections.emptyList();
+    }
+
+    private LogbackManager getLogbackManager() {
+        LogbackManager lm = (LogbackManager) getContext().getObject(LogbackManager.class.getName());
+        if (lm == null) {
+            throw new IllegalStateException("LogbackManager not found in Context map");
+        }
+        return lm;
+    }
+
+    private static void trimHeadAndTail(SaxEventRecorder recorder) {
+        // Let's remove the two <included> events before
+        // adding the events to the player.
+        List<SaxEvent> saxEventList = recorder.saxEventList;
+
+        if (saxEventList.size() == 0) {
+            return;
+        }
+
+        SaxEvent first = saxEventList.get(0);
+        if (first != null && first.qName.equalsIgnoreCase(INCLUDED_TAG)) {
+            saxEventList.remove(0);
+        }
+
+        SaxEvent last = saxEventList.get(recorder.saxEventList.size() - 1);
+        if (last != null && last.qName.equalsIgnoreCase(INCLUDED_TAG)) {
+            saxEventList.remove(recorder.saxEventList.size() - 1);
+        }
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,121 @@
+/*
+ * 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.sling.commons.log.logback.internal;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.FileAppender;
+
+/**
+ * The <code>SlingConfigurationPrinter</code> is an Apache Felix Web Console
+ * plugin to display the currently configured log files.
+ */
+public class SlingConfigurationPrinter {
+    private final LogbackManager logbackManager;
+
+    public SlingConfigurationPrinter(LogbackManager logbackManager) {
+        this.logbackManager = logbackManager;
+    }
+
+    /**
+     * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public void printConfiguration(PrintWriter printWriter) {
+        LogbackManager.LoggerStateContext ctx = logbackManager.determineLoggerState();
+        for (Appender<ILoggingEvent> appender : ctx.getAllAppenders()) {
+            if (appender instanceof FileAppender) {
+                final File file = new File(((FileAppender) appender).getFile());
+                if (file.exists()) {
+                    printWriter.print("Log file ");
+                    printWriter.println(file.getAbsolutePath());
+                    printWriter.println("--------------------------------------------------");
+                    FileReader fr = null;
+                    try {
+                        fr = new FileReader(file);
+                        final char[] buffer = new char[512];
+                        int len;
+                        while ((len = fr.read(buffer)) != -1) {
+                            printWriter.write(buffer, 0, len);
+                        }
+                    } catch (IOException ignore) {
+                        // we just ignore this
+                    } finally {
+                        if (fr != null) {
+                            try {
+                                fr.close();
+                            } catch (IOException ignoreCloseException) {
+                            }
+                        }
+                    }
+                    printWriter.println();
+                }
+            }
+        }
+    }
+
+    /**
+     * TODO Need to see how to implement this with LogBack as we cannot get
+     * information about all rolled over policy
+     * 
+     * @see org.apache.felix.webconsole.AttachmentProvider#getAttachments(String)
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public URL[] getAttachments(String mode) {
+        // we only provide urls for mode zip
+        if ("zip".equals(mode)) {
+            final List<URL> urls = new ArrayList<URL>();
+            LogbackManager.LoggerStateContext ctx = logbackManager.determineLoggerState();
+            for (Appender<ILoggingEvent> appender : ctx.getAllAppenders()) {
+                if (appender instanceof FileAppender) {
+                    final File file = new File(((FileAppender) appender).getFile());
+                    // TODO With LogBack there is no straightforward way to get
+                    // information
+                    // about rolled over files
+                    // final File[] files =
+                    // writer.getFileRotator().getRotatedFiles(writer.getFile());
+                    final File[] files = new File[] {
+                        file
+                    };
+                    for (File f : files) {
+                        try {
+                            urls.add(f.toURI().toURL());
+                        } catch (MalformedURLException mue) {
+                            // we just ignore this file then
+                        }
+                    }
+                }
+            }
+            if (urls.size() > 0) {
+                return urls.toArray(new URL[urls.size()]);
+            }
+        }
+        return null;
+    }
+
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,412 @@
+/*
+ * 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.sling.commons.log.logback.internal;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.helpers.Transform;
+import ch.qos.logback.core.status.Status;
+import ch.qos.logback.core.util.CachingDateFormatter;
+
+import org.apache.sling.commons.log.logback.internal.LogbackManager.LoggerStateContext;
+import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender;
+import org.apache.sling.commons.log.logback.internal.util.Util;
+import org.apache.sling.commons.log.logback.internal.util.XmlUtil;
+import org.osgi.framework.Constants;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.InputSource;
+
+import static org.apache.sling.commons.log.logback.internal.AppenderTracker.AppenderInfo;
+import static org.apache.sling.commons.log.logback.internal.ConfigSourceTracker.ConfigSourceInfo;
+
+/**
+ * The <code>SlingLogPanel</code> is a Felix Web Console plugin to display the
+ * current active log bundle configuration.
+ * <p>
+ * In future revisions of this plugin, the configuration may probably even be
+ * modified through this panel.
+ */
+public class SlingLogPanel extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    private final CachingDateFormatter SDF = new CachingDateFormatter("yyyy-MM-dd HH:mm:ss");
+
+    private final LogbackManager logbackManager;
+
+    private final String labelRes;
+
+    private final int labelResLen;
+
+    private static final org.slf4j.Logger log = LoggerFactory.getLogger(SlingLogPanel.class);
+
+    public SlingLogPanel(final LogbackManager logbackManager, String label) {
+        this.logbackManager = logbackManager;
+        this.labelRes = '/' + label + '/';
+        this.labelResLen = labelRes.length() - 1;
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+
+        final PrintWriter pw = resp.getWriter();
+
+        final String consoleAppRoot = (String) req.getAttribute("felix.webconsole.appRoot");
+        final String pluginRoot = (String) req.getAttribute("felix.webconsole.pluginRoot");
+
+        final LoggerStateContext ctx = logbackManager.determineLoggerState();
+        appendLoggerStatus(pw, ctx);
+        appendLoggerData(pw, ctx);
+        addAppenderData(pw, consoleAppRoot, ctx);
+        appendLogbackMainConfig(pw);
+        appendLogbackFragments(pw, consoleAppRoot);
+        appendLogbackStatus(pw, ctx);
+        enablePrettifier(pw, pluginRoot);
+    }
+
+    private void enablePrettifier(PrintWriter pw, String pluginRoot) {
+        pw.printf("<script type=\"text/javascript\" src=\"%s/res/ui/prettify.js\"></script>", pluginRoot);
+        pw.println("<script>$(document).ready(prettyPrint);</script>");
+
+    }
+
+    private void appendLoggerStatus(PrintWriter pw, LoggerStateContext ctx) {
+        pw.printf(
+            "<p class='statline'>Log Service Stats: %d categories, %d appenders(s), %d Dynamic appenders(s)</p>%n",
+            ctx.getNumberOfLoggers(), ctx.getNumOfAppenders(), ctx.getNumOfDynamicAppenders());
+    }
+
+    private void appendLoggerData(PrintWriter pw, LoggerStateContext ctx) {
+        pw.println("<div class='table'>");
+
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logger</div>");
+
+        pw.println("<table class='nicetable ui-widget'>");
+
+        pw.println("<thead class='ui-widget-header'>");
+        pw.println("<tr>");
+        pw.println("<th>Log Level</th>");
+        pw.println("<th>Name</th>");
+        pw.println("<th>Appender</th>");
+        // pw.println("<th>" + cfgColTitle + "</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody class='ui-widget-content'>");
+
+        for (Logger logger : ctx.loggerInfos) {
+            pw.println("<tr>");
+            pw.println("<td>" + logger.getLevel() + "</td>");
+            pw.println("<td>" + logger.getName() + "</td>");
+
+            pw.println("<td>");
+            pw.println("<ul>");
+            Iterator<Appender<ILoggingEvent>> itr = logger.iteratorForAppenders();
+            while (itr.hasNext()) {
+                Appender<ILoggingEvent> a = itr.next();
+                pw.print("<li>");
+                pw.println(getName(a));
+                pw.print("</li>");
+            }
+            pw.println("</ul>");
+            pw.println("</td>");
+
+            pw.println("</tr>");
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void addAppenderData(PrintWriter pw, String consoleAppRoot, LoggerStateContext ctx) {
+        pw.println("<div class='table'>");
+
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Appender</div>");
+
+        pw.println("<table class='nicetable ui-widget'>");
+
+        pw.println("<thead class='ui-widget-header'>");
+        pw.println("<tr>");
+        pw.println("<th>Appender</th>");
+        pw.println("<th>" + getConfigColTitle(consoleAppRoot) + "</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+        pw.println("<tbody class='ui-widget-content'>");
+
+        for (Appender<ILoggingEvent> appender : ctx.appenders.values()) {
+            pw.println("<tr>");
+            pw.println("<td>" + getName(appender) + "</td>");
+            pw.println("<td>" + formatPid(consoleAppRoot, appender, ctx) + "</td>");
+            pw.println("</tr>");
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void appendLogbackStatus(PrintWriter pw, LoggerStateContext ctx) {
+        pw.println("<div class='table'>");
+
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logback Status</div>");
+        pw.println("<div style='overflow-y:scroll; height:400px'>");
+        pw.println("<table class='nicetable ui-widget'>");
+
+        pw.println("<thead class='ui-widget-header'>");
+        pw.println("<tr>");
+        pw.println("<th>Date</th>");
+        pw.println("<th>Level</th>");
+        pw.println("<th>Origin</th>");
+        pw.println("<th>Message</th>");
+        pw.println("</tr>");
+        pw.println("</thead>");
+
+        pw.println("<tbody class='ui-widget-content'  >");
+
+        List<Status> statusList = ctx.loggerContext.getStatusManager().getCopyOfStatusList();
+        for (Status s : statusList) {
+            pw.println("<tr>");
+            pw.println("<td class=\"date\">" + SDF.format(s.getDate()) + "</td>");
+            pw.println("<td class=\"level\">" + statusLevelAsString(s) + "</td>");
+            pw.println("<td>" + abbreviatedOrigin(s) + "</td>");
+            pw.println("<td>" + s.getMessage() + "</td>");
+            pw.println("</tr>");
+
+            // noinspection ThrowableResultOfMethodCallIgnored
+            if (s.getThrowable() != null) {
+                printThrowable(pw, s.getThrowable());
+            }
+        }
+
+        pw.println("</tbody>");
+
+        pw.println("</table>");
+        pw.print("</div>");
+        pw.println("</div>");
+    }
+
+    private void appendLogbackMainConfig(PrintWriter pw) {
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logback Config</div>");
+        pw.println("<table class='nicetable ui-widget'>");
+        pw.println("<tbody class='ui-widget-content'>");
+
+        File configFile = null;
+        URL url = null;
+        InputSource source = null;
+        try {
+            String msg;
+            configFile = logbackManager.getLogConfigManager().getLogbackConfigFile();
+            if (configFile != null) {
+                source = new InputSource(new BufferedInputStream(new FileInputStream(configFile)));
+                msg = "Source " + configFile.getAbsolutePath();
+            } else {
+                url = logbackManager.getDefaultConfig();
+                URLConnection uc = url.openConnection();
+                uc.setDefaultUseCaches(false);
+                source = new InputSource(new BufferedInputStream(uc.getInputStream()));
+                msg = "Source : Default";
+            }
+
+            pw.println("<tr>");
+            pw.println("<td>" + msg + "</td>");
+            pw.println("</tr>");
+
+            pw.println("<tr><td>");
+            String textContent = XmlUtil.escapeXml(XmlUtil.prettyPrint(source));
+            pw.print("<pre class=\"prettyprint lang-xml\" style=\"border: 0px\">");
+            pw.print(textContent);
+            pw.print("</pre>");
+            pw.println("</td></tr>");
+        } catch (IOException e) {
+            String msg = "Error occurred while opening file [" + configFile + "]";
+            if (url != null) {
+                msg = "Error occurred while opening url [" + url + "]";
+            }
+            log.warn(msg, e);
+        } finally {
+            Util.close(source);
+        }
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    private void appendLogbackFragments(PrintWriter pw, String consoleAppRoot) {
+        final Collection<ConfigSourceInfo> configSources = logbackManager.getConfigSourceTracker().getSources();
+
+        if (configSources.isEmpty()) {
+            return;
+        }
+
+        pw.println("<div class='table'>");
+        pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logback Config Fragments</div>");
+        pw.println("<table class='nicetable ui-widget'>");
+        pw.println("<tbody class='ui-widget-content'>");
+
+        for (ConfigSourceInfo ci : configSources) {
+            final String pid = ci.getReference().getProperty(Constants.SERVICE_ID).toString();
+            String url = createUrl(consoleAppRoot, "services", pid);
+            pw.println("<tr>");
+            pw.println("<td>" + url + "</td>");
+            pw.println("</tr>");
+
+            pw.println("<tr>");
+            pw.println("<td>");
+            // prettify.js adds a border. We eed to remove that
+            pw.print("<pre class=\"prettyprint lang-xml\" style=\"border: 0px\">");
+            pw.print(ci.getSourceAsEscapedString());
+            pw.print("</pre>");
+
+            pw.println("</td>");
+            pw.println("</tr>");
+        }
+
+        pw.println("</tbody>");
+        pw.println("</table>");
+        pw.println("</div>");
+    }
+
+    /**
+     * Called internally by {@link AbstractWebConsolePlugin} to load resources.
+     * This particular implementation depends on the label. As example, if the
+     * plugin is accessed as <code>/system/console/abc</code>, and the plugin
+     * resources are accessed like <code>/system/console/abc/res/logo.gif</code>
+     * , the code here will try load resource <code>/res/logo.gif</code> from
+     * the bundle, providing the plugin.
+     * 
+     * @param path the path to read.
+     * @return the URL of the resource or <code>null</code> if not found.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    protected URL getResource(String path) {
+        return (path != null && path.startsWith(labelRes)) ? //
+                getClass().getResource(path.substring(labelResLen))
+                : null;
+    }
+
+    private static String getName(Appender<ILoggingEvent> appender) {
+        // For legacy config based appender the appender name is the file
+        // name. So omit the appender name
+        if (appender instanceof SlingRollingFileAppender) {
+            return "File : " + ((FileAppender) appender).getFile();
+        }
+
+        // For normal file appender we also display the name of appender
+        if (appender instanceof FileAppender) {
+            return String.format("File : [%s] %s", appender.getName(), ((FileAppender) appender).getFile());
+        }
+
+        return String.format("%s (%s)", appender.getName(), appender.getClass().getName());
+    }
+
+    private static String formatPid(final String consoleAppRoot, final Appender<ILoggingEvent> appender,
+            final LoggerStateContext ctx) {
+        if (appender instanceof SlingRollingFileAppender) {
+            final LogWriter lw = ((SlingRollingFileAppender) appender).getLogWriter();
+            if (lw.isImplicit()) {
+                return "[implicit]";
+            }
+
+            final String pid = lw.getConfigurationPID();
+            return createUrl(consoleAppRoot, "configMgr", pid);
+        } else if (ctx.isDynamicAppender(appender)) {
+            final AppenderInfo ai = ctx.dynamicAppenders.get(appender);
+
+            final String pid = ai.serviceReference.getProperty(Constants.SERVICE_ID).toString();
+            return createUrl(consoleAppRoot, "services", pid);
+        } else {
+            return "[others]";
+        }
+    }
+
+    private static String getConfigColTitle(String consoleAppRoot) {
+        return (consoleAppRoot == null) ? "PID" : "Configuration";
+    }
+
+    private static String createUrl(String consoleAppRoot, String subContext, String pid) {
+        // no recent web console, so just render the pid as the link
+        if (consoleAppRoot == null) {
+            return "<a href=\"" + subContext + "/" + pid + "\">" + pid + "</a>";
+        }
+
+        // recent web console has app root and hence we can use an image
+        return "<a href=\"" + subContext + "/" + pid + "\"><img src=\"" + consoleAppRoot
+            + "/res/imgs/component_configure.png\" border=\"0\" /></a>";
+    }
+
+    // ~------------------------------------------------Status Manager
+    // Based on ch.qos.logback.core.status.ViewStatusMessagesServletBase
+
+    private static String statusLevelAsString(Status s) {
+        switch (s.getEffectiveLevel()) {
+            case Status.INFO:
+                return "INFO";
+            case Status.WARN:
+                return "<span class=\"warn\">WARN</span>";
+            case Status.ERROR:
+                return "<span class=\"error\">ERROR</span>";
+        }
+        return null;
+    }
+
+    private static String abbreviatedOrigin(Status s) {
+        Object o = s.getOrigin();
+        if (o == null) {
+            return null;
+        }
+        String fqClassName = o.getClass().getName();
+        int lastIndex = fqClassName.lastIndexOf(CoreConstants.DOT);
+        if (lastIndex != -1) {
+            return fqClassName.substring(lastIndex + 1, fqClassName.length());
+        } else {
+            return fqClassName;
+        }
+    }
+
+    private static void printThrowable(PrintWriter pw, Throwable t) {
+        pw.println("  <tr>");
+        pw.println("    <td colspan=\"4\" class=\"exception\"><pre>");
+        StringWriter sw = new StringWriter();
+        PrintWriter expPw = new PrintWriter(sw);
+        t.printStackTrace(expPw);
+        pw.println(Transform.escapeTags(sw.getBuffer()));
+        pw.println("    </pre></td>");
+        pw.println("  </tr>");
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigAdminSupport.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigAdminSupport.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigAdminSupport.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigAdminSupport.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,82 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.sling.commons.log.logback.internal.LogConfigManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+public class ConfigAdminSupport {
+
+    private ServiceRegistration loggingConfigurable;
+
+    private ServiceRegistration writerConfigurer;
+
+    private ServiceRegistration configConfigurer;
+
+    public ConfigAdminSupport(BundleContext context, LogConfigManager logConfigManager) {
+        // prepare registration properties (will be reused)
+        Dictionary<String, String> props = new Hashtable<String, String>();
+        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
+
+        // register for official (global) configuration now
+        props.put(Constants.SERVICE_PID, LogConfigManager.PID);
+        props.put(Constants.SERVICE_DESCRIPTION, "LogManager Configuration Admin support");
+        loggingConfigurable = context.registerService("org.osgi.service.cm.ManagedService",
+            new ConfigurationServiceFactory(logConfigManager,
+                "org.apache.sling.commons.log.logback.internal.config.GlobalConfigurator"), props);
+
+        // register for log writer configuration
+        ConfigurationServiceFactory msf = new ConfigurationServiceFactory(logConfigManager,
+            "org.apache.sling.commons.log.logback.internal.config.LogWriterManagedServiceFactory");
+        props.put(Constants.SERVICE_PID, LogConfigManager.FACTORY_PID_WRITERS);
+        props.put(Constants.SERVICE_DESCRIPTION, "LogWriter configurator");
+        writerConfigurer = context.registerService("org.osgi.service.cm.ManagedServiceFactory", msf, props);
+
+        // register for log configuration
+        msf = new ConfigurationServiceFactory(logConfigManager,
+            "org.apache.sling.commons.log.logback.internal.config.LoggerManagedServiceFactory");
+        props.put(Constants.SERVICE_PID, LogConfigManager.FACTORY_PID_CONFIGS);
+        props.put(Constants.SERVICE_DESCRIPTION, "Logger configurator");
+        configConfigurer = context.registerService("org.osgi.service.cm.ManagedServiceFactory", msf, props);
+
+    }
+
+    public void shutdown() {
+        if (loggingConfigurable != null) {
+            loggingConfigurable.unregister();
+            loggingConfigurable = null;
+        }
+
+        if (writerConfigurer != null) {
+            writerConfigurer.unregister();
+            writerConfigurer = null;
+        }
+
+        if (configConfigurer != null) {
+            configConfigurer.unregister();
+            configConfigurer = null;
+        }
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationException.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationException.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationException.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationException.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,46 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+public class ConfigurationException extends Exception {
+
+    private static final long serialVersionUID = -9213226340780391070L;
+
+    private final String property;
+
+    private final String reason;
+
+    public ConfigurationException(final String property, final String reason) {
+        this(property, reason, null);
+    }
+
+    public ConfigurationException(final String property, final String reason, final Throwable cause) {
+        super("", cause);
+        this.property = property;
+        this.reason = reason;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationServiceFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationServiceFactory.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationServiceFactory.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/ConfigurationServiceFactory.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,70 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+import org.apache.sling.commons.log.logback.internal.LogConfigManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+public class ConfigurationServiceFactory implements ServiceFactory {
+
+    private final LogConfigManager logConfigManager;
+
+    private final String serviceClass;
+
+    private int useCount;
+
+    private Object service;
+
+    public ConfigurationServiceFactory(final LogConfigManager logConfigManager, final String serviceClass) {
+        this.logConfigManager = logConfigManager;
+        this.serviceClass = serviceClass;
+    }
+
+    public Object getService(Bundle bundle, ServiceRegistration registration) {
+        if (service == null) {
+            useCount = 1;
+            service = createInstance();
+        } else {
+            useCount++;
+        }
+        return service;
+    }
+
+    public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+        useCount--;
+        if (useCount <= 0) {
+            service = null;
+        }
+    }
+
+    private Object createInstance() {
+        try {
+            Class<?> type = getClass().getClassLoader().loadClass(serviceClass);
+            Object instance = type.newInstance();
+            if (instance instanceof LogConfigurator) {
+                ((LogConfigurator) instance).setLogConfigManager(logConfigManager);
+            }
+            return instance;
+        } catch (Throwable t) {
+            throw new RuntimeException("Failed to create " + serviceClass + " instance", t);
+        }
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/GlobalConfigurator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/GlobalConfigurator.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/GlobalConfigurator.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/GlobalConfigurator.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,36 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+import java.util.Dictionary;
+
+import org.osgi.service.cm.ManagedService;
+
+class GlobalConfigurator extends LogConfigurator implements ManagedService {
+
+    @SuppressWarnings("unchecked")
+    public void updated(Dictionary properties) throws org.osgi.service.cm.ConfigurationException { // unchecked
+        try {
+            getLogConfigManager().updateGlobalConfiguration(properties);
+        } catch (ConfigurationException ce) {
+            throw new org.osgi.service.cm.ConfigurationException(ce.getProperty(), ce.getReason(), ce);
+        }
+    }
+
+}
\ No newline at end of file

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogConfigurator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogConfigurator.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogConfigurator.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogConfigurator.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,35 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+import org.apache.sling.commons.log.logback.internal.LogConfigManager;
+
+public class LogConfigurator {
+
+    private LogConfigManager logConfigManager;
+
+    void setLogConfigManager(final LogConfigManager logConfigManager) {
+        this.logConfigManager = logConfigManager;
+    }
+
+    LogConfigManager getLogConfigManager() {
+        return logConfigManager;
+    }
+
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogWriterManagedServiceFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogWriterManagedServiceFactory.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogWriterManagedServiceFactory.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LogWriterManagedServiceFactory.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,49 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+import java.util.Dictionary;
+
+import org.osgi.service.cm.ManagedServiceFactory;
+
+class LogWriterManagedServiceFactory extends LogConfigurator implements ManagedServiceFactory {
+
+    public String getName() {
+        return "LogWriter configurator";
+    }
+
+    @SuppressWarnings("unchecked")
+    public void updated(String pid, Dictionary configuration) throws org.osgi.service.cm.ConfigurationException {
+        try {
+            getLogConfigManager().updateLogWriter(pid, configuration, true);
+        } catch (ConfigurationException ce) {
+            throw new org.osgi.service.cm.ConfigurationException(ce.getProperty(), ce.getReason(), ce);
+        }
+
+    }
+
+    public void deleted(String pid) {
+        try {
+            getLogConfigManager().updateLogWriter(pid, null, true);
+        } catch (ConfigurationException ce) {
+            // not expected
+            getLogConfigManager().internalFailure("Unexpected Configuration Problem", ce);
+        }
+    }
+}
\ No newline at end of file

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LoggerManagedServiceFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LoggerManagedServiceFactory.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LoggerManagedServiceFactory.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/config/LoggerManagedServiceFactory.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,48 @@
+/*
+ * 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.sling.commons.log.logback.internal.config;
+
+import java.util.Dictionary;
+
+import org.osgi.service.cm.ManagedServiceFactory;
+
+class LoggerManagedServiceFactory extends LogConfigurator implements ManagedServiceFactory {
+
+    public String getName() {
+        return "Logger configurator";
+    }
+
+    public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary configuration)
+            throws org.osgi.service.cm.ConfigurationException {
+        try {
+            getLogConfigManager().updateLoggerConfiguration(pid, configuration, true);
+        } catch (ConfigurationException ce) {
+            throw new org.osgi.service.cm.ConfigurationException(ce.getProperty(), ce.getReason(), ce);
+        }
+    }
+
+    public void deleted(String pid) {
+        try {
+            getLogConfigManager().updateLoggerConfiguration(pid, null, true);
+        } catch (ConfigurationException ce) {
+            // not expected
+            getLogConfigManager().internalFailure("Unexpected Configuration Problem", ce);
+        }
+    }
+}
\ No newline at end of file

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/LoggerSpecificEncoder.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/LoggerSpecificEncoder.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/LoggerSpecificEncoder.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/LoggerSpecificEncoder.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,77 @@
+/*
+ * 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.sling.commons.log.logback.internal.util;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.pattern.PatternLayoutEncoderBase;
+
+import org.apache.sling.commons.log.logback.internal.LogConfig;
+
+public class LoggerSpecificEncoder extends PatternLayoutEncoderBase<ILoggingEvent> {
+    private Map<String, Layout<ILoggingEvent>> layoutByCategory = new ConcurrentHashMap<String, Layout<ILoggingEvent>>();
+
+    private final Layout<ILoggingEvent> defaultLayout;
+
+    public LoggerSpecificEncoder(Layout<ILoggingEvent> defaultLayout) {
+        this.defaultLayout = defaultLayout;
+    }
+
+    public void doEncode(ILoggingEvent event) throws IOException {
+        String txt = getLayout(event.getLoggerName()).doLayout(event);
+        outputStream.write(convertToBytes(txt));
+        if (isImmediateFlush()) outputStream.flush();
+    }
+
+    private Layout<ILoggingEvent> getLayout(String loggerName) {
+        // TODO Handle layout inheritance wrt logger names
+        Layout<ILoggingEvent> layout = layoutByCategory.get(loggerName);
+        if (layout == null) {
+            layout = defaultLayout;
+        }
+        return layout;
+    }
+
+    private byte[] convertToBytes(String s) {
+        Charset charset = getCharset();
+        if (charset == null) {
+            return s.getBytes();
+        } else {
+            try {
+                return s.getBytes(charset.name());
+            } catch (UnsupportedEncodingException e) {
+                throw new IllegalStateException("An existing charset cannot possibly be unsupported.");
+            }
+        }
+    }
+
+    public void addLogConfig(LogConfig config) {
+        Layout<ILoggingEvent> layout = config.createLayout();
+        for (String category : config.getCategories()) {
+            layoutByCategory.put(category, layout);
+        }
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingRollingFileAppender.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingRollingFileAppender.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingRollingFileAppender.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingRollingFileAppender.java Wed Aug 28 05:56:33 2013
@@ -0,0 +1,42 @@
+/*
+ * 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.sling.commons.log.logback.internal.util;
+
+import ch.qos.logback.core.rolling.RollingFileAppender;
+
+import org.apache.sling.commons.log.logback.internal.LogWriter;
+
+/**
+ * Custom class to allow the SlingLogPanel to differentiate between default
+ * appenders and Sling Config based appenders
+ * 
+ * @param <E>
+ */
+public class SlingRollingFileAppender<E> extends RollingFileAppender<E> {
+    private LogWriter logWriter;
+
+    public LogWriter getLogWriter() {
+        return logWriter;
+    }
+
+    public void setLogWriter(LogWriter logWriter) {
+        this.logWriter = logWriter;
+    }
+}

Added: sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java?rev=1518083&view=auto
==============================================================================
--- sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java (added)
+++ sling/trunk/bundles/commons/log/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java Wed Aug 28 05:56:33 2013
@@ -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.sling.commons.log.logback.internal.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.xml.sax.InputSource;
+
+public class Util {
+
+    public static List<String> toList(Object values) {
+        if (values == null) {
+            return Collections.emptyList();
+        }
+
+        Object[] valueArray;
+        if (values.getClass().isArray()) {
+            valueArray = (Object[]) values;
+        } else if (values instanceof Collection<?>) {
+            valueArray = ((Collection<?>) values).toArray();
+        } else {
+            valueArray = new Object[] {
+                values
+            };
+        }
+
+        List<String> valuesList = new ArrayList<String>(valueArray.length);
+        for (Object valueObject : valueArray) {
+            if (valueObject != null) {
+                String[] splitValues = valueObject.toString().split(",");
+                for (String value : splitValues) {
+                    value = value.trim();
+                    if (value.length() > 0) {
+                        valuesList.add(value);
+                    }
+                }
+            }
+        }
+
+        return valuesList;
+    }
+
+    public static void close(InputSource is) {
+        Closeable c = is.getByteStream();
+        if (c == null) {
+            c = is.getCharacterStream();
+        }
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+    }
+
+}