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
+ }
+ }
+ }
+
+}