| /* |
| * 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.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Semaphore; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.sling.commons.log.logback.internal.AppenderTracker.AppenderInfo; |
| import org.apache.sling.commons.log.logback.internal.stacktrace.PackageInfoCollector; |
| import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender; |
| import org.apache.sling.commons.log.logback.internal.util.SlingStatusPrinter; |
| import org.apache.sling.commons.log.logback.webconsole.LogPanel; |
| 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.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.framework.hooks.weaving.WeavingHook; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.slf4j.LoggerFactory; |
| import org.slf4j.bridge.SLF4JBridgeHandler; |
| |
| 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.jul.LevelChangePropagator; |
| 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.turbo.TurboFilter; |
| 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; |
| |
| public class LogbackManager extends LoggerContextAwareBase { |
| private static final String JUL_SUPPORT = "org.apache.sling.commons.log.julenabled"; |
| |
| private static final String PREFIX = "org.apache.sling.commons.log"; |
| |
| private static final String DEBUG = PREFIX + "." + "debug"; |
| |
| private static final String PRINTER_URL = "slinglogs"; |
| |
| private static final String RESET_EVENT_TOPIC = "org/apache/sling/commons/log/RESET"; |
| |
| /** |
| * Framework property specifying the root location used to resolve relative paths |
| */ |
| private static final String SLING_LOG_ROOT = "sling.log.root"; |
| |
| public static final String PACKAGE_INFO_COLLECTOR_DESC = "Sling Log Package Info Collector"; |
| |
| private final BundleContext bundleContext; |
| |
| 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; |
| |
| /** |
| * 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 Object configChangedFlagLock = new Object(); |
| |
| private boolean configChanged = false; |
| |
| private final AppenderTracker appenderTracker; |
| |
| private final ConfigSourceTracker configSourceTracker; |
| |
| private final FilterTracker filterTracker; |
| |
| private final TurboFilterTracker turboFilterTracker; |
| |
| private final List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>(); |
| |
| private final List<ServiceTracker> serviceTrackers = new ArrayList<ServiceTracker>(); |
| |
| private final boolean bridgeHandlerInstalled; |
| |
| private final PackageInfoCollector packageInfoCollector = new PackageInfoCollector(); |
| |
| /** |
| * 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 { |
| this.bundleContext = bundleContext; |
| |
| setLoggerContext((LoggerContext) LoggerFactory.getILoggerFactory()); |
| |
| this.log = LoggerFactory.getLogger(getClass()); |
| this.rootDir = getRootDir(bundleContext); |
| this.debug = Boolean.parseBoolean(bundleContext.getProperty(DEBUG)); |
| this.bridgeHandlerInstalled = installSlf4jBridgeHandler(bundleContext); |
| |
| this.appenderTracker = new AppenderTracker(bundleContext, getLoggerContext()); |
| this.configSourceTracker = new ConfigSourceTracker(bundleContext, this); |
| this.filterTracker = new FilterTracker(bundleContext,this); |
| this.turboFilterTracker = new TurboFilterTracker(bundleContext,getLoggerContext()); |
| |
| getLoggerContext().setName(contextName); |
| this.logConfigManager = new LogConfigManager(getLoggerContext(), bundleContext, rootDir, this); |
| |
| resetListeners.add(new LevelChangePropagatorChecker()); |
| resetListeners.add(logConfigManager); |
| resetListeners.add(appenderTracker); |
| resetListeners.add(configSourceTracker); |
| resetListeners.add(filterTracker); |
| resetListeners.add(turboFilterTracker); |
| resetListeners.add(new RootLoggerListener()); //Should be invoked at last |
| |
| //Record trackers for shutdown later |
| serviceTrackers.add(appenderTracker); |
| serviceTrackers.add(configSourceTracker); |
| serviceTrackers.add(filterTracker); |
| serviceTrackers.add(turboFilterTracker); |
| |
| getLoggerContext().addListener(osgiIntegrationListener); |
| registerWebConsoleSupport(); |
| registerEventHandler(); |
| |
| // initial configuration must be done synchronously (aka immediately) |
| addInfo("LogbackManager: BEGIN initial configuration"); |
| failSafeConfigure(); |
| addInfo("LogbackManager: END initialconfiguration"); |
| |
| // now open the gate for regular configuration |
| started = true; |
| |
| //Now check once if any other config was added while we were starting |
| logConfigManager.checkForNewConfigsWhileStarting(getLoggerContext()); |
| } |
| |
| public void shutdown() { |
| if(bridgeHandlerInstalled){ |
| SLF4JBridgeHandler.uninstall(); |
| } |
| |
| logConfigManager.close(); |
| |
| for(ServiceTracker tracker : serviceTrackers){ |
| tracker.close(); |
| } |
| |
| for (ServiceRegistration reg : registrations) { |
| reg.unregister(); |
| } |
| |
| getLoggerContext().removeListener(osgiIntegrationListener); |
| |
| getLoggerContext().stop(); |
| } |
| |
| //-------------------------------------- Config reset handling ---------- |
| |
| public void configChanged() { |
| if (!started) { |
| return; |
| } |
| |
| /* |
| Logback reset cannot be done concurrently. So when Logback is being reset |
| we note down any new request for reset. Later when the thread which performs |
| reset finishes, then it checks if any request for reset pending. if yes |
| then it again tries to reschedules a job to perform reset in rescheduleIfConfigChanged |
| |
| Logback reset is done under a lock 'resetLock' so that Logback |
| is not reconfigured concurrently. Only the thread which acquires the |
| 'resetLock' can submit the task for reload (actual reload done async) |
| |
| Once the reload is done the lock is released in LoggerReconfigurer#run |
| |
| The way locking works is any thread which changes config |
| invokes configChanged. Here two things are possible |
| |
| 1. Log reset in progress i.e. resetLock already acquired |
| In this case the thread would just set the 'configChanged' flag to true |
| |
| 2. No reset in progress. Thread would acquire the resetLock and submit the |
| job to reset Logback |
| |
| |
| Any such change is synchronized with configChangedFlagLock such that a request |
| for config changed is not missed |
| */ |
| |
| synchronized (configChangedFlagLock){ |
| if (resetLock.tryAcquire()) { |
| configChanged = false; |
| scheduleConfigReload(); |
| } else { |
| configChanged = true; |
| addInfo("LoggerContext reset in progress. Marking config changed to true"); |
| } |
| } |
| } |
| |
| private void rescheduleIfConfigChanged(){ |
| synchronized (configChangedFlagLock){ |
| //If config changed then only acquire a lock |
| //and proceed to reload |
| if(configChanged){ |
| if(resetLock.tryAcquire()){ |
| configChanged = false; |
| scheduleConfigReload(); |
| } |
| //else some other thread acquired the resetlock |
| //and reset is in progress. That job would |
| //eventually call rescheduleIfConfigChanged again |
| //and configChanged request would be taken care of |
| } |
| } |
| } |
| |
| private void scheduleConfigReload() { |
| getLoggerContext().getExecutorService().submit(new Runnable() { |
| @Override |
| public void run() { |
| // TODO Might be better to run a job to monitor refreshRequirement |
| try { |
| failSafeConfigure(); |
| } finally { |
| resetLock.release(); |
| addInfo("Re configuration done"); |
| rescheduleIfConfigChanged(); |
| } |
| } |
| }); |
| } |
| |
| private void failSafeConfigure(){ |
| try { |
| addInfo("Performing configuration"); |
| configure(); |
| } catch (Exception e) { |
| log.warn("Error occurred while re-configuring logger", e); |
| addError("Error occurred while re-configuring logger", e); |
| } |
| } |
| |
| public void fireResetCompleteListeners(){ |
| for(LogbackResetListener listener : resetListeners){ |
| addInfo("Firing reset listener - onResetComplete "+listener.getClass()); |
| listener.onResetComplete(getLoggerContext()); |
| } |
| } |
| |
| 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 PackageInfoCollector getPackageInfoCollector() { |
| return packageInfoCollector; |
| } |
| |
| public URL getDefaultConfig() { |
| return getClass().getClassLoader().getResource("logback-empty.xml"); |
| } |
| |
| public String getRootDir() { |
| return rootDir; |
| } |
| |
| 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) { |
| long startTime = System.currentTimeMillis(); |
| 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(); |
| boolean success = false; |
| try { |
| cb.perform(configurator); |
| if (statusUtil.hasXMLParsingErrors(threshold)) { |
| cb.fallbackConfiguration(eventList, createConfigurator(), statusListener); |
| } |
| addInfo("Context: " + getLoggerContext().getName() + " reloaded."); |
| success = true; |
| } catch (Throwable t) { |
| //Need to catch any error as Logback must work in all scenarios |
| //The error would be dumped to sysout in later call to Status printer |
| addError("Error occurred while configuring Logback", t); |
| } finally { |
| if(!success){ |
| cb.fallbackConfiguration(eventList, createConfigurator(), statusListener); |
| } |
| getStatusManager().remove(statusListener); |
| SlingStatusPrinter.printInCaseOfErrorsOrWarnings(getLoggerContext(), resetStartTime, startTime, success); |
| } |
| } |
| |
| 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 String getRootDir(BundleContext bundleContext) { |
| String rootDir = bundleContext.getProperty(SLING_LOG_ROOT); |
| if (rootDir == null) { |
| rootDir = bundleContext.getProperty("sling.home"); |
| if (rootDir == null) { |
| rootDir = new File(".").getAbsolutePath(); |
| } |
| } |
| addInfo("Using rootDir as " + rootDir); |
| return rootDir; |
| } |
| |
| //~-------------------------------------------------- Slf4j Bridge Handler Support |
| |
| /** |
| * Installs the Slf4j BridgeHandler to route the JUL logs through Slf4j |
| * |
| * @return true only if the BridgeHandler is installed. |
| */ |
| private static boolean installSlf4jBridgeHandler(BundleContext bundleContext){ |
| // SLING-2373 |
| if (Boolean.parseBoolean(bundleContext.getProperty(JUL_SUPPORT))) { |
| // In config one must enable the LevelChangePropagator |
| // http://logback.qos.ch/manual/configuration.html#LevelChangePropagator |
| // make sure configuration is empty unless explicitly set |
| if (System.getProperty("java.util.logging.config.file") == null |
| && System.getProperty("java.util.logging.config.class") == null) { |
| final Thread ct = Thread.currentThread(); |
| final ClassLoader old = ct.getContextClassLoader(); |
| try { |
| ct.setContextClassLoader(LogbackManager.class.getClassLoader()); |
| System.setProperty("java.util.logging.config.class", |
| DummyLogManagerConfiguration.class.getName()); |
| java.util.logging.LogManager.getLogManager().reset(); |
| } finally { |
| ct.setContextClassLoader(old); |
| System.clearProperty("java.util.logging.config.class"); |
| } |
| } |
| |
| SLF4JBridgeHandler.install(); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * It checks if LevelChangePropagator is installed or not. If not then |
| * it installs the propagator when Slf4j Bridge Handler is installed |
| */ |
| private class LevelChangePropagatorChecker implements LogbackResetListener { |
| |
| @Override |
| public void onResetStart(LoggerContext context) { |
| |
| } |
| |
| @Override |
| public void onResetComplete(LoggerContext context) { |
| List<LoggerContextListener> listenerList = context.getCopyOfListenerList(); |
| boolean levelChangePropagatorInstalled = false; |
| for (LoggerContextListener listener : listenerList) { |
| if (listener instanceof LevelChangePropagator) { |
| levelChangePropagatorInstalled = true; |
| break; |
| } |
| } |
| |
| //http://logback.qos.ch/manual/configuration.html#LevelChangePropagator |
| if (!levelChangePropagatorInstalled |
| && bridgeHandlerInstalled) { |
| LevelChangePropagator propagator = new LevelChangePropagator(); |
| propagator.setContext(context); |
| propagator.start(); |
| context.addListener(propagator); |
| addInfo("Slf4j bridge handler found to be enabled. Installing the LevelChangePropagator"); |
| } |
| } |
| } |
| |
| /** |
| * The <code>DummyLogManagerConfiguration</code> class is used as JUL |
| * LogginManager configurator to preven reading platform default |
| * configuration which just duplicate log output to be redirected to SLF4J. |
| */ |
| @SuppressWarnings("UnusedDeclaration") |
| public static class DummyLogManagerConfiguration { |
| } |
| |
| // ~-------------------------------LoggerContextListener |
| |
| private class OsgiIntegrationListener implements LoggerContextListener { |
| |
| @Override |
| 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; |
| } |
| |
| @Override |
| public void onStart(LoggerContext context) { |
| } |
| |
| @Override |
| public void onReset(LoggerContext context) { |
| addInfo("OsgiIntegrationListener : context reset detected. Adding LogManager to context map and firing" |
| + " listeners"); |
| |
| context.setPackagingDataEnabled(false); |
| context.setMaxCallerDataDepth(logConfigManager.getMaxCallerDataDepth()); |
| registerPackageInfoCollector(); |
| |
| // Attach a console appender to handle logging untill we configure |
| // one. This would be removed in RootLoggerListener.reset |
| final Logger rootLogger = getLoggerContext().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); |
| rootLogger.setLevel(Level.INFO); |
| rootLogger.addAppender(logConfigManager.getDefaultAppender()); |
| |
| // Now record the time of reset with a default appender attached to |
| // root logger. We also add a milli second extra to account for logs which would have |
| // got fired in same duration |
| resetStartTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.toMillis(1); |
| addInfo("Registered a default console based logger"); |
| |
| context.putObject(LogbackManager.class.getName(), LogbackManager.this); |
| for (LogbackResetListener l : resetListeners) { |
| addInfo("Firing reset listener - onResetStart "+l.getClass()); |
| l.onResetStart(context); |
| } |
| } |
| |
| @Override |
| public void onStop(LoggerContext context) { |
| } |
| |
| @Override |
| public void onLevelChange(Logger logger, Level level) { |
| } |
| |
| } |
| |
| private class RootLoggerListener implements LogbackResetListener { |
| |
| @Override |
| public void onResetStart(LoggerContext context) { |
| |
| } |
| |
| @Override |
| public void onResetComplete(LoggerContext context) { |
| // Remove the default console appender that we attached at start of |
| // reset |
| ch.qos.logback.classic.Logger root = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); |
| Iterator<Appender<ILoggingEvent>> appenderItr = root.iteratorForAppenders(); |
| |
| //Root logger has at least 1 appender associated with it. Remove the one added by us |
| if (appenderItr.hasNext()) { |
| root.detachAppender(LogConfigManager.DEFAULT_CONSOLE_APPENDER_NAME); |
| addInfo("Found appender attached with root logger. Detaching the default console based logger"); |
| } else { |
| addInfo("No appender was found to be associated with root logger. Registering " + |
| "a Console based logger"); |
| } |
| } |
| } |
| |
| // ~--------------------------------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(eventList); |
| } 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; |
| } |
| |
| @Override |
| 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 { |
| @Override |
| 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, packageInfoCollector); |
| |
| //Distinguish between Logger configured via |
| //1. OSGi Config - The ones configured via ConfigAdmin |
| //2. Other means - Configured via Logback config or any other means |
| for (LogConfig lc : logConfigManager.getLogConfigs()) { |
| for (String category : lc.getCategories()) { |
| ctx.osgiConfiguredLoggers.put(category, lc); |
| } |
| } |
| |
| for (Logger logger : loggers) { |
| boolean hasOnlySlingRollingAppenders = true; |
| 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); |
| } |
| |
| if(!(a instanceof SlingRollingFileAppender)){ |
| hasOnlySlingRollingAppenders = false; |
| } |
| } |
| |
| if(logger.getLevel() == null){ |
| continue; |
| } |
| |
| boolean configuredViaOSGiConfig = |
| ctx.osgiConfiguredLoggers.containsKey(logger.getName()); |
| if (!configuredViaOSGiConfig |
| || (configuredViaOSGiConfig && !hasOnlySlingRollingAppenders)) |
| { |
| ctx.nonOSgiConfiguredLoggers.add(logger); |
| } |
| |
| } |
| |
| |
| 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> nonOSgiConfiguredLoggers = new ArrayList<Logger>(); |
| |
| final Map<String,LogConfig> osgiConfiguredLoggers = new HashMap<String, LogConfig>(); |
| |
| final Map<String, Appender<ILoggingEvent>> appenders = new HashMap<String, Appender<ILoggingEvent>>(); |
| |
| final Map<Appender<ILoggingEvent>, AppenderInfo> dynamicAppenders = |
| new HashMap<Appender<ILoggingEvent>, AppenderInfo>(); |
| |
| final Map<ServiceReference,TurboFilter> turboFilters; |
| |
| final PackageInfoCollector packageInfoCollector; |
| |
| LoggerStateContext(List<Logger> allLoggers, PackageInfoCollector packageInfoCollector) { |
| this.allLoggers = allLoggers; |
| this.packageInfoCollector = packageInfoCollector; |
| for (AppenderTracker.AppenderInfo ai : getAppenderTracker().getAppenderInfos()) { |
| dynamicAppenders.put(ai.appender, ai); |
| } |
| this.turboFilters = turboFilterTracker.getFilters(); |
| } |
| |
| 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); |
| } |
| |
| ServiceReference getTurboFilterRef(TurboFilter tf){ |
| for(Map.Entry<ServiceReference,TurboFilter> e : turboFilters.entrySet()){ |
| if(e.getValue().equals(tf)){ |
| return e.getKey(); |
| } |
| } |
| return null; |
| } |
| |
| Collection<Appender<ILoggingEvent>> getAllAppenders() { |
| return appenders.values(); |
| } |
| |
| Map<String,Appender<ILoggingEvent>> getAppenderMap(){ |
| return Collections.unmodifiableMap(appenders); |
| } |
| } |
| |
| private void registerWebConsoleSupport() { |
| Dictionary<String,Object> panelProps = new Hashtable<>(); |
| panelProps.put(Constants.SERVICE_VENDOR, "Apache Software Foundation"); |
| panelProps.put(Constants.SERVICE_DESCRIPTION, "Sling Log Panel Support"); |
| registrations.add(bundleContext.registerService(LogPanel.class.getName(), |
| new SlingLogPanel(this, bundleContext), panelProps)); |
| |
| Dictionary<String,Object> printerProps = new Hashtable<>(); |
| 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(bundleContext.registerService(SlingConfigurationPrinter.class.getName(), |
| new SlingConfigurationPrinter(this), printerProps)); |
| } |
| |
| private void registerEventHandler() { |
| Dictionary<String,Object> props = new Hashtable<>(); |
| 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)); |
| } |
| |
| private void registerPackageInfoCollector() { |
| //Weaving hook once registered would not be removed upon config changed |
| if (logConfigManager.isPackagingDataEnabled()) { |
| Dictionary<String,Object> props = new Hashtable<>(); |
| props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation"); |
| props.put(Constants.SERVICE_DESCRIPTION, PACKAGE_INFO_COLLECTOR_DESC); |
| |
| registrations.add(bundleContext.registerService(WeavingHook.class.getName(), |
| packageInfoCollector, props)); |
| } |
| } |
| |
| |
| } |