[maven-release-plugin] copy for tag org.apache.sling.commons.log-4.0.0
git-svn-id: https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.log-4.0.0@1572506 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..066727e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+Apache Sling Commons Log
+========================
+
+The "log" project packages the [Logback][2] library to manage logging
+in OSGi environment. It provide some useful extension to the default
+Logback feature set to enable better integration with OSGi. The SLF4j
+API bundle must be installed along with this bundle to provide full SLF4J
+logging support.
+
+The Logging bundle should be installed as one of the first modules in
+the OSGi framework and - provided the framework supports start levels -
+be set to start at start level 1. This ensures the Logging bundle is
+loaded as early as possible thus providing services to the framework
+and preparing logging.
+
+For more details refer to the [Logging Documentation][1]
+
+Getting Started
+===============
+
+You can compile and package the jar using the following command:
+
+ mvn package -Pide,coverage
+
+It would build the module and also produce a test coverage report also
+prepare bundle jar which is suitable to be used to run integration test
+from within IDE.
+
+[1]: http://sling.apache.org/documentation/development/logging.html
+[2]: http://logback.qos.ch/
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 83e2632..0000000
--- a/README.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-Apache Sling SLF4J Implementation
-
-
-=======================================
-Welcome to Sling - SLF4J Implementation
-=======================================
-
-The "log" project implements the SLF4J SPI and thus operates as the backend
-for the SLF4J API. The SLF4j API bundle must be installed along with this
-bundle to provide full SLF4J logging support.
-
-The Logging bundle should be installed as one of the first modules in
-the OSGi framework and - provided the framework supports start levels -
-be set to start at start level 1. This ensures the Logging bundle is
-loaded as early as possible thus providing services to the framework
-and preparing logging.
-
-See the Apache Sling web site (http://sling.apache.org) for
-documentation and other information. You are welcome to join the
-Sling mailing lists (http://sling.apache.org/site/project-information.html)
-to discuss this component and to use the Sling issue tracker
-(http://issues.apache.org/jira/browse/SLING) to report issues or request
-new features.
-
-Apache Sling is a project of the Apache Software Foundation
-(http://www.apache.org).
-
-License (see also LICENSE)
-==========================
-
-Collective work: Copyright 2007 The Apache Software Foundation.
-
-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.
-
-Getting Started
-===============
-
-This component uses a Maven 2 (http://maven.apache.org/) build
-environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
-2.0.7 or later. We recommend to use the latest Maven version.
-
-If you have Maven 2 installed, you can compile and
-package the jar using the following command:
-
- mvn package
-
-See the Maven 2 documentation for other build features.
-
-The latest source code for this component is available in the
-Subversion (http://subversion.tigris.org/) source repository of
-the Apache Software Foundation. If you have Subversion installed,
-you can checkout the latest source using the following command:
-
- svn checkout http://svn.apache.org/repos/asf/sling/trunk/commons/log
-
-See the Subversion documentation for other source control features.
diff --git a/pom.xml b/pom.xml
index 4dc9de7..36f8227 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
<properties>
<slf4j.version>1.6.4</slf4j.version>
- <logback.version>1.0.13</logback.version>
+ <logback.version>1.1.0</logback.version>
<sling.java.version>6</sling.java.version>
<pax-exam.version>3.0.0</pax-exam.version>
@@ -104,12 +104,8 @@
</Bundle-DocURL>
<_exportcontents>
org.slf4j.impl;version=${slf4j.version},
- ch.qos.logback.core;
- ch.qos.logback.core.filter;
- ch.qos.logback.core.spi;
- ch.qos.logback.classic;
- ch.qos.logback.classic.turbo;
- ch.qos.logback.classic.spi;version=${logback.version}
+ ch.qos.logback.classic*;
+ ch.qos.logback.core*;
</_exportcontents>
<Import-Package>
!org.slf4j.impl,
@@ -161,7 +157,6 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>1.8</version>
<executions>
<execution>
<id>reserve-network-port</id>
@@ -218,7 +213,6 @@
<!-- integration tests run with pax-exam -->
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
- <version>2.12</version>
<executions>
<execution>
<goals>
diff --git a/src/main/appended-resources/META-INF/NOTICE b/src/main/appended-resources/META-INF/NOTICE
index a899950..2fed022 100644
--- a/src/main/appended-resources/META-INF/NOTICE
+++ b/src/main/appended-resources/META-INF/NOTICE
@@ -7,3 +7,6 @@
This product includes software from http://www.slf4j.org/
Licensed under the MIT License
+
+This product includes software from https://github.com/dyve/jquery-autocomplete
+Licensed under the Apache License 2.0.
diff --git a/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java b/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java
deleted file mode 100644
index 83298a5..0000000
--- a/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2013, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- * or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.classic.spi;
-
-import java.net.URL;
-import java.security.CodeSource;
-import java.util.HashMap;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.Version;
-import sun.reflect.Reflection;
-// import java.security.AccessControlException; import java.security.AccessController;import java.security.PrivilegedAction;
-/**
- * Given a classname locate associated PackageInfo (jar name, version name).
- *
- * @author James Strachan
- * @Ceki Gülcü
- */
-public class PackagingDataCalculator {
-
- final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0];
-
- HashMap<String, ClassPackagingData> cache = new HashMap<String, ClassPackagingData>();
-
- private static boolean GET_CALLER_CLASS_METHOD_AVAILABLE = false; //private static boolean HAS_GET_CLASS_LOADER_PERMISSION = false;
-
- static {
- // if either the Reflection class or the getCallerClass method
- // are unavailable, then we won't invoke Reflection.getCallerClass()
- // This approach ensures that this class will *run* on JDK's lacking
- // sun.reflect.Reflection class. However, this class will *not compile*
- // on JDKs lacking sun.reflect.Reflection.
- try {
- Reflection.getCallerClass(2);
- GET_CALLER_CLASS_METHOD_AVAILABLE = true;
- } catch (NoClassDefFoundError e) {
- } catch (NoSuchMethodError e) {
- } catch (Throwable e) {
- System.err.println("Unexpected exception");
- e.printStackTrace();
- }
- }
-
-
- public void calculate(IThrowableProxy tp) {
- while (tp != null) {
- populateFrames(tp.getStackTraceElementProxyArray());
- IThrowableProxy[] suppressed = tp.getSuppressed();
- if(suppressed != null) {
- for(IThrowableProxy current:suppressed) {
- populateFrames(current.getStackTraceElementProxyArray());
- }
- }
- tp = tp.getCause();
- }
- }
-
- void populateFrames(StackTraceElementProxy[] stepArray) {
- // in the initial part of this method we populate package information for
- // common stack frames
- final Throwable t = new Throwable("local stack reference");
- final StackTraceElement[] localteSTEArray = t.getStackTrace();
- final int commonFrames = STEUtil.findNumberOfCommonFrames(localteSTEArray,
- stepArray);
- final int localFirstCommon = localteSTEArray.length - commonFrames;
- final int stepFirstCommon = stepArray.length - commonFrames;
-
- ClassLoader lastExactClassLoader = null;
- ClassLoader firsExactClassLoader = null;
-
- int missfireCount = 0;
- for (int i = 0; i < commonFrames; i++) {
- Class callerClass = null;
- if (GET_CALLER_CLASS_METHOD_AVAILABLE) {
- callerClass = Reflection.getCallerClass(localFirstCommon + i
- - missfireCount + 1);
- }
- StackTraceElementProxy step = stepArray[stepFirstCommon + i];
- String stepClassname = step.ste.getClassName();
-
- if (callerClass != null && stepClassname.equals(callerClass.getName())) {
- // see also LBCLASSIC-263
- lastExactClassLoader = callerClass.getClassLoader();
- if (firsExactClassLoader == null) {
- firsExactClassLoader = lastExactClassLoader;
- }
- ClassPackagingData pi = calculateByExactType(callerClass);
- step.setClassPackagingData(pi);
- } else {
- missfireCount++;
- ClassPackagingData pi = computeBySTEP(step, lastExactClassLoader);
- step.setClassPackagingData(pi);
- }
- }
- populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader);
- }
-
- void populateUncommonFrames(int commonFrames,
- StackTraceElementProxy[] stepArray, ClassLoader firstExactClassLoader) {
- int uncommonFrames = stepArray.length - commonFrames;
- for (int i = 0; i < uncommonFrames; i++) {
- StackTraceElementProxy step = stepArray[i];
- ClassPackagingData pi = computeBySTEP(step, firstExactClassLoader);
- step.setClassPackagingData(pi);
- }
- }
-
- private ClassPackagingData calculateByExactType(Class type) {
- String className = type.getName();
- ClassPackagingData cpd = cache.get(className);
- if (cpd != null) {
- return cpd;
- }
- String version = getImplementationVersion(type);
- String codeLocation = getCodeLocation(type);
- cpd = new ClassPackagingData(codeLocation, version);
- cache.put(className, cpd);
- return cpd;
- }
-
- private ClassPackagingData computeBySTEP(StackTraceElementProxy step,
- ClassLoader lastExactClassLoader) {
- String className = step.ste.getClassName();
- ClassPackagingData cpd = cache.get(className);
- if (cpd != null) {
- return cpd;
- }
- Class type = bestEffortLoadClass(lastExactClassLoader, className);
- String version = getImplementationVersion(type);
- String codeLocation = getCodeLocation(type);
- cpd = new ClassPackagingData(codeLocation, version, false);
- cache.put(className, cpd);
- return cpd;
- }
-
- String getImplementationVersion(Class type) {
- if (type == null) {
- return "na";
- }
-
- Bundle b = FrameworkUtil.getBundle(type);
- if(b != null){
- final Version version = b.getVersion();
- return version == org.osgi.framework.Version.emptyVersion ? "na" : version.toString();
- }
-
- Package aPackage = type.getPackage();
- if (aPackage != null) {
- String v = aPackage.getImplementationVersion();
- if (v == null) {
- return "na";
- } else {
- return v;
- }
- }
- return "na";
-
- }
-
- String getCodeLocation(Class type) {
- try {
- if (type != null) {
-
- Bundle b = FrameworkUtil.getBundle(type);
- if(b != null){
- return b.getSymbolicName();
- }
- // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar
- CodeSource codeSource = type.getProtectionDomain().getCodeSource();
- if (codeSource != null) {
- URL resource = codeSource.getLocation();
- if (resource != null) {
- String locationStr = resource.toString();
- // now lets remove all but the file name
- String result = getCodeLocation(locationStr, '/');
- if (result != null) {
- return result;
- }
- return getCodeLocation(locationStr, '\\');
- }
- }
- }
- } catch (Exception e) {
- // ignore
- }
- return "na";
- }
-
- private String getCodeLocation(String locationStr, char separator) {
- int idx = locationStr.lastIndexOf(separator);
- if (isFolder(idx, locationStr)) {
- idx = locationStr.lastIndexOf(separator, idx - 1);
- return locationStr.substring(idx + 1);
- } else if (idx > 0) {
- return locationStr.substring(idx + 1);
- }
- return null;
- }
-
- private boolean isFolder(int idx, String text) {
- return (idx != -1 && idx + 1 == text.length());
- }
-
- private Class loadClass(ClassLoader cl, String className) {
- if (cl == null) {
- return null;
- }
- try {
- return cl.loadClass(className);
- } catch (ClassNotFoundException e1) {
- return null;
- } catch (NoClassDefFoundError e1) {
- return null;
- } catch (Exception e) {
- e.printStackTrace(); // this is unexpected
- return null;
- }
-
- }
-
- /**
- * @param lastGuaranteedClassLoader may be null
- * @param className
- * @return
- */
- private Class bestEffortLoadClass(ClassLoader lastGuaranteedClassLoader,
- String className) {
- Class result = loadClass(lastGuaranteedClassLoader, className);
- if (result != null) {
- return result;
- }
- ClassLoader tccl = Thread.currentThread().getContextClassLoader();
- if (tccl != lastGuaranteedClassLoader) {
- result = loadClass(tccl, className);
- }
- if (result != null) {
- return result;
- }
-
- try {
- return Class.forName(className);
- } catch (ClassNotFoundException e1) {
- return null;
- } catch (NoClassDefFoundError e1) {
- return null;
- } catch (Exception e) {
- e.printStackTrace(); // this is unexpected
- return null;
- }
- }
-
-}
diff --git a/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java b/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java
deleted file mode 100644
index ec0797e..0000000
--- a/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
-* Logback: the reliable, generic, fast and flexible logging framework.
-* Copyright (C) 1999-2013, QOS.ch. All rights reserved.
-*
-* This program and the accompanying materials are dual-licensed under
-* either the terms of the Eclipse Public License v1.0 as published by
-* the Eclipse Foundation
-*
-* or (per the licensee's choosing)
-*
-* under the terms of the GNU Lesser General Public License version 2.1
-* as published by the Free Software Foundation.
-*/
-package ch.qos.logback.classic.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import ch.qos.logback.core.CoreConstants;
-
-/**
- * Utility class for analysing logger names.
- * Locally overriding the class for SLING-3037
- */
-public class LoggerNameUtil {
-
-
- public static int getFirstSeparatorIndexOf(String name) {
- return getSeparatorIndexOf(name, 0);
- }
-
- /**
- * Get the position of the separator character, if any, starting at position
- * 'fromIndex'.
- *
- * @param name
- * @param fromIndex
- * @return
- */
- public static int getSeparatorIndexOf(String name, int fromIndex) {
- int dotIndex = name.indexOf(CoreConstants.DOT, fromIndex);
- int dollarIndex = name.indexOf(CoreConstants.DOLLAR, fromIndex);
- if (dotIndex == -1 && dollarIndex == -1) return -1;
- if (dotIndex == -1) return dollarIndex;
- if (dollarIndex == -1) return dotIndex;
- return dotIndex < dollarIndex ? dotIndex : dollarIndex;
- }
-
- public static List<String> computeNameParts(String loggerName) {
- List<String> partList = new ArrayList<String>();
-
- int fromIndex = 0;
- while(true) {
- int index = getSeparatorIndexOf(loggerName, fromIndex);
- if(index == -1) {
- partList.add(loggerName.substring(fromIndex));
- break;
- }
- partList.add(loggerName.substring(fromIndex, index));
- fromIndex = index+1;
- }
- return partList;
- }
-}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java b/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java
index ad0ba6b..ebaff94 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/Activator.java
@@ -23,26 +23,22 @@
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.core.status.ErrorStatus;
+import ch.qos.logback.core.util.StatusPrinter;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.slf4j.bridge.SLF4JBridgeHandler;
-
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.core.status.ErrorStatus;
-import ch.qos.logback.core.util.StatusPrinter;
public class Activator implements BundleActivator {
- private static final String JUL_SUPPORT = "org.apache.sling.commons.log.julenabled";
private LogbackManager logManager;
private BundleContext context;
private Timer timer;
- private boolean bridgeHandlerInstalled;
private long startTime;
private static final AtomicInteger counter = new AtomicInteger();
public static final long INIT_TASK_PERIOD_MSEC = 1;
@@ -64,10 +60,6 @@
}
public void stop(BundleContext context) throws Exception {
- if(bridgeHandlerInstalled){
- SLF4JBridgeHandler.uninstall();
- }
-
if(timer != null){
timer.cancel();
timer = null;
@@ -80,30 +72,6 @@
}
private void initializeLogbackManager(boolean immediateInit) throws InvalidSyntaxException {
- // SLING-2373
- if (Boolean.parseBoolean(context.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(getClass().getClassLoader());
- System.setProperty("java.util.logging.config.class",
- "org.apache.sling.commons.log.internal.Activator.DummyLogManagerConfiguration");
- java.util.logging.LogManager.getLogManager().reset();
- } finally {
- ct.setContextClassLoader(old);
- System.clearProperty("java.util.logging.config.class");
- }
- }
-
- SLF4JBridgeHandler.install();
- bridgeHandlerInstalled = true;
- }
-
logManager = new LogbackManager(context);
final Logger log = LoggerFactory.getLogger(getClass());
@@ -139,14 +107,4 @@
private static boolean isSlf4jInitialized(){
return LoggerFactory.getILoggerFactory() instanceof LoggerContext;
}
-
-
- /**
- * 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 {
- }
}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java b/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java
index 3dba508..37bd4c8 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfig.java
@@ -19,6 +19,7 @@
package org.apache.sling.commons.log.logback.internal;
import java.text.MessageFormat;
+import java.util.Collections;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -53,7 +54,7 @@
this.logWriterProvider = logWriterProvider;
this.configPid = configPid;
this.pattern = pattern;
- this.categories = categories;
+ this.categories = Collections.unmodifiableSet(categories);
this.logLevel = logLevel;
this.logWriterName = logWriterName;
this.loggerContext = loggerContext;
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java b/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java
index b1c1afd..1d4accd 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/LogConfigManager.java
@@ -29,14 +29,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.sling.commons.log.logback.internal.config.ConfigAdminSupport;
-import org.apache.sling.commons.log.logback.internal.config.ConfigurationException;
-import org.apache.sling.commons.log.logback.internal.util.LoggerSpecificEncoder;
-import org.apache.sling.commons.log.logback.internal.util.Util;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
@@ -47,6 +40,13 @@
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.joran.action.ActionConst;
import ch.qos.logback.core.util.ContextUtil;
+import org.apache.sling.commons.log.logback.internal.config.ConfigAdminSupport;
+import org.apache.sling.commons.log.logback.internal.config.ConfigurationException;
+import org.apache.sling.commons.log.logback.internal.util.LoggerSpecificEncoder;
+import org.apache.sling.commons.log.logback.internal.util.Util;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class LogConfigManager implements LogbackResetListener, LogConfig.LogWriterProvider {
@@ -60,6 +60,8 @@
public static final String LOG_FILE_SIZE = "org.apache.sling.commons.log.file.size";
+ public static final String LOG_FILE_BUFFERED = "org.apache.sling.commons.log.file.buffered";
+
public static final String LOG_PATTERN = "org.apache.sling.commons.log.pattern";
public static final String LOG_PATTERN_DEFAULT = "%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n";
@@ -68,6 +70,10 @@
public static final String LOG_ADDITIV = "org.apache.sling.commons.log.additiv";
+ public static final String LOG_PACKAGING_DATA = "org.apache.sling.commons.log.packagingDataEnabled";
+
+ public static final String LOG_MAX_CLALLER_DEPTH = "org.apache.sling.commons.log.maxCallerDataDepth";
+
public static final String LOG_LEVEL_DEFAULT = "INFO";
public static final int LOG_FILE_NUMBER_DEFAULT = 5;
@@ -80,7 +86,7 @@
public static final String FACTORY_PID_CONFIGS = PID + ".factory.config";
- private static final String DEFAULT_CONSOLE_APPENDER_NAME = "org.apache.sling.commons.log.CONSOLE";
+ public static final String DEFAULT_CONSOLE_APPENDER_NAME = "org.apache.sling.commons.log.CONSOLE";
private final LoggerContext loggerContext;
@@ -118,6 +124,10 @@
private File logbackConfigFile;
+ private boolean packagingDataEnabled;
+
+ private int maxCallerDataDepth;
+
/**
* Logs a message an optional stack trace to error output. This method is
* used by the logging system in case of errors writing to the correct
@@ -204,6 +214,14 @@
return logbackConfigFile;
}
+ public Iterable<LogConfig> getLogConfigs() {
+ return configByPid.values();
+ }
+
+ public Iterable<LogWriter> getLogWriters(){
+ return writerByFileName.values();
+ }
+
public Appender<ILoggingEvent> getDefaultAppender() {
OutputStreamAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
appender.setName(DEFAULT_CONSOLE_APPENDER_NAME);
@@ -279,13 +297,11 @@
if (appender != null) {
logger.setAdditive(config.isAdditive());
logger.addAppender(appender);
+ contextUtil.addInfo("Registering appender "+appender.getName()+ "("+appender.getClass()+
+ ") with logger "+logger.getName());
}
}
}
-
- // Remove the default console appender that we attached at start of
- // reset
- context.getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(DEFAULT_CONSOLE_APPENDER_NAME);
}
@@ -404,7 +420,10 @@
fileSize = fileSizeProp.toString();
}
- LogWriter newWriter = new LogWriter(pid, getAppnderName(logFileName), fileNum, fileSize, logFileName);
+ boolean bufferedLogging = Util.toBoolean(configuration.get(LogConfigManager.LOG_FILE_BUFFERED), false);
+
+ LogWriter newWriter = new LogWriter(pid, getAppnderName(logFileName), fileNum,
+ fileSize, logFileName, bufferedLogging);
if (oldWriter != null) {
writerByFileName.remove(oldWriter.getFileName());
}
@@ -551,13 +570,22 @@
}
}
+ public boolean isPackagingDataEnabled() {
+ return packagingDataEnabled;
+ }
+
+ public int getMaxCallerDataDepth() {
+ return maxCallerDataDepth;
+ }
+
// ---------- ManagedService interface -------------------------------------
private Dictionary<String, String> getBundleConfiguration(BundleContext bundleContext) {
Dictionary<String, String> config = new Hashtable<String, String>();
final String[] props = {
- LOG_LEVEL, LOG_FILE, LOG_FILE_NUMBER, LOG_FILE_SIZE, LOG_PATTERN, LOGBACK_FILE
+ LOG_LEVEL, LOG_FILE, LOG_FILE_NUMBER, LOG_FILE_SIZE, LOG_PATTERN, LOGBACK_FILE,
+ LOG_PACKAGING_DATA
};
for (String prop : props) {
String value = bundleContext.getProperty(prop);
@@ -576,12 +604,14 @@
private void processGlobalConfig(Dictionary<String, String> configuration) {
String fileName = configuration.get(LOGBACK_FILE);
- if (fileName != null) {
+ if (fileName != null && !fileName.isEmpty()) {
File file = new File(getAbsoluteFilePath(fileName));
final String path = file.getAbsolutePath();
if (!file.exists()) {
- log.warn("Logback configuration file [{}]does not exist.", path);
- } else if (!file.canRead()) {
+ log.warn("Logback configuration file [{}] does not exist.", path);
+ } if (!file.isFile()) {
+ log.warn("Logback configuration file [{}] is not a file.", path);
+ }else if (!file.canRead()) {
log.warn("Logback configuration [{}]file cannot be read", path);
} else {
synchronized (configLock) {
@@ -589,6 +619,18 @@
}
}
}
+
+ //Process packaging data
+ Object packagingData = configuration.get(LOG_PACKAGING_DATA);
+ if (packagingData != null) {
+ packagingDataEnabled = Boolean.valueOf(packagingData.toString());
+ } else {
+ //Defaults to false i.e. disabled in OSGi env
+ packagingDataEnabled = false;
+ }
+
+ maxCallerDataDepth = Util.toInteger(configuration.get(LOG_MAX_CLALLER_DEPTH),
+ ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH);
}
// ---------- Internal helpers ---------------------------------------------
@@ -601,7 +643,7 @@
return new LogWriter(getAppnderName(logWriterName),logWriterName, defaultWriter.getLogNumber(), defaultWriter.getLogRotation());
}
- private LogWriter getDefaultWriter() {
+ public LogWriter getDefaultWriter() {
return writerByPid.get(LogConfigManager.PID);
}
@@ -613,9 +655,7 @@
return getDefaultConfig().createLayout();
}
- private Iterable<LogConfig> getLogConfigs() {
- return configByPid.values();
- }
+
/**
* Returns the <code>logFileName</code> argument converted into an absolute
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java b/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java
index b800bf9..e4e4b63 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/LogWriter.java
@@ -27,10 +27,11 @@
import ch.qos.logback.core.Context;
import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.encoder.Encoder;
+import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
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.SlingContextUtil;
import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender;
/**
@@ -72,7 +73,9 @@
private final String appenderName;
- public LogWriter(String configurationPID, String appenderName, int logNumber, String logRotation, String fileName) {
+ private final boolean bufferedLogging;
+
+ public LogWriter(String configurationPID, String appenderName, int logNumber, String logRotation, String fileName, boolean bufferedLogging) {
this.appenderName = appenderName;
if (fileName == null || fileName.length() == 0) {
fileName = FILE_NAME_CONSOLE;
@@ -90,16 +93,21 @@
this.fileName = fileName;
this.logNumber = logNumber;
this.logRotation = logRotation;
+ this.bufferedLogging = bufferedLogging;
}
public LogWriter(String appenderName,String fileName, int logNumber, String logRotation) {
- this(null, appenderName, logNumber, logRotation, fileName);
+ this(null, appenderName, logNumber, logRotation, fileName, false);
}
public String getConfigurationPID() {
return configurationPID;
}
+ public String getImplicitConfigPID(){
+ return LogConfigManager.PID;
+ }
+
public String getFileName() {
return fileName;
}
@@ -121,12 +129,14 @@
}
public Appender<ILoggingEvent> createAppender(final Context context, final Encoder<ILoggingEvent> encoder) {
-
+ SlingContextUtil ctxUtil = new SlingContextUtil(context, this);
OutputStreamAppender<ILoggingEvent> appender;
if (FILE_NAME_CONSOLE.equals(fileName)) {
appender = new ConsoleAppender<ILoggingEvent>();
appender.setName(FILE_NAME_CONSOLE);
} else {
+ ctxUtil.addInfo("Configuring appender "+getFileName());
+
SlingRollingFileAppender<ILoggingEvent> rollingAppender = new SlingRollingFileAppender<ILoggingEvent>();
rollingAppender.setAppend(true);
rollingAppender.setFile(getFileName());
@@ -183,12 +193,15 @@
rollingAppender.setRollingPolicy(pol);
} else {
TimeBasedRollingPolicy<ILoggingEvent> policy = new TimeBasedRollingPolicy<ILoggingEvent>();
- policy.setFileNamePattern(createFileNamePattern(getFileName(), getLogRotation()));
+ String fileNamePattern = createFileNamePattern(getFileName(), getLogRotation());
+ policy.setFileNamePattern(fileNamePattern);
policy.setMaxHistory(getLogNumber());
policy.setContext(context);
policy.setParent(rollingAppender);
policy.start();
rollingAppender.setTriggeringPolicy(policy);
+
+ ctxUtil.addInfo("Configured TimeBasedRollingPolicy with pattern "+ fileNamePattern);
}
rollingAppender.setLogWriter(this);
@@ -197,9 +210,19 @@
appender = rollingAppender;
}
+ if(bufferedLogging && encoder instanceof LayoutWrappingEncoder){
+ ((LayoutWrappingEncoder) encoder).setImmediateFlush(false);
+ ctxUtil.addInfo("Setting immediateFlush to false");
+ } else{
+ ctxUtil.addInfo("immediateFlush property not modified. Defaults to true");
+ }
+
appender.setContext(context);
appender.setEncoder(encoder);
appender.start();
+
+ ctxUtil.addInfo("Completed configuring appender with name "+getFileName());
+
return appender;
}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java b/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java
index 2e85234..7af2c94 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/LogbackManager.java
@@ -13,13 +13,13 @@
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-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.jul.LevelChangePropagator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggerContextAwareBase;
import ch.qos.logback.classic.spi.LoggerContextListener;
@@ -34,8 +34,9 @@
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.apache.sling.commons.log.logback.internal.AppenderTracker.AppenderInfo;
+import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender;
+import org.apache.sling.commons.log.logback.internal.util.SlingStatusPrinter;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -45,18 +46,36 @@
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.LoggerFactory;
+import org.slf4j.bridge.SLF4JBridgeHandler;
public class LogbackManager extends LoggerContextAwareBase {
+ private static final String JUL_SUPPORT = "org.apache.sling.commons.log.julenabled";
+
+ //These properties should have been defined in SlingLogPanel
+ //But we need them while registering ServiceFactory and hence
+ //would not want to load SlingLogPanel class for registration
+ //purpose as we need to run in cases where Servlet classes
+ //are not available
+ static final String APP_ROOT = "slinglog";
+
+ static final String RES_LOC = APP_ROOT + "/res/ui";
+
+ public static final String[] CSS_REFS = {
+ RES_LOC + "/jquery.autocomplete.css",
+ RES_LOC + "/prettify.css",
+ RES_LOC + "/log.css",
+ };
+
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 BundleContext bundleContext;
+
private final String rootDir;
private final String contextName = "sling";
@@ -78,7 +97,9 @@
private final Semaphore resetLock = new Semaphore(1);
- private final AtomicBoolean configChanged = new AtomicBoolean();
+ private final Object configChangedFlagLock = new Object();
+
+ private boolean configChanged = false;
private final AppenderTracker appenderTracker;
@@ -89,8 +110,11 @@
private final TurboFilterTracker turboFilterTracker;
private final List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
+
private final List<ServiceTracker> serviceTrackers = new ArrayList<ServiceTracker>();
+ private final boolean bridgeHandlerInstalled;
+
/**
* Time at which reset started. Used as the threshold for logging error
* messages from status printer
@@ -98,27 +122,30 @@
private volatile long resetStartTime;
public LogbackManager(BundleContext bundleContext) throws InvalidSyntaxException {
- final long startTime = System.currentTimeMillis();
+ 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());
- // TODO Make it configurable
- // TODO: what should it be ?
+
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);
@@ -127,15 +154,18 @@
serviceTrackers.add(turboFilterTracker);
getLoggerContext().addListener(osgiIntegrationListener);
+ registerWebConsoleSupport();
+ registerEventHandler();
- configure();
- registerWebConsoleSupport(bundleContext);
- registerEventHandler(bundleContext);
- StatusPrinter.printInCaseOfErrorsOrWarnings(getLoggerContext(), startTime);
started = true;
+ configChanged();
}
public void shutdown() {
+ if(bridgeHandlerInstalled){
+ SLF4JBridgeHandler.uninstall();
+ }
+
logConfigManager.close();
for(ServiceTracker tracker : serviceTrackers){
@@ -151,21 +181,90 @@
getLoggerContext().stop();
}
+ //-------------------------------------- Config reset handling ----------
+
public void configChanged() {
if (!started) {
return;
}
- if (resetLock.tryAcquire()) {
- scheduleConfigReload();
- } else {
- configChanged.set(true);
- addInfo("LoggerContext reset in progress. Marking config changed to true");
+ /*
+ 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 {
+ addInfo("Performing configuration");
+ configure();
+ } catch (Exception e) {
+ log.warn("Error occurred while re-configuring logger", e);
+ addError("Error occurred while re-configuring logger", e);
+ } finally {
+ resetLock.release();
+ addInfo("Re configuration done");
+ rescheduleIfConfigChanged();
+ }
+ }
+ });
+ }
+
public void fireResetCompleteListeners(){
for(LogbackResetListener listener : resetListeners){
+ addInfo("Firing reset listener - onResetComplete "+listener.getClass());
listener.onResetComplete(getLoggerContext());
}
}
@@ -190,6 +289,10 @@
return getClass().getClassLoader().getResource("logback-empty.xml");
}
+ public String getRootDir() {
+ return rootDir;
+ }
+
private void configure() {
ConfiguratorCallback cb = new DefaultCallback();
@@ -203,6 +306,7 @@
}
private void configure(ConfiguratorCallback cb) {
+ long startTime = System.currentTimeMillis();
StatusListener statusListener = new StatusListenerAsList();
if (debug) {
statusListener = new OnConsoleStatusListener();
@@ -226,13 +330,14 @@
success = true;
} catch (Throwable t) {
//Need to catch any error as Logback must work in all scenarios
- addError("Error configuring Logback", t);
+ //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);
- StatusPrinter.printInCaseOfErrorsOrWarnings(getLoggerContext(), resetStartTime);
+ SlingStatusPrinter.printInCaseOfErrorsOrWarnings(getLoggerContext(), resetStartTime, startTime, success);
}
}
@@ -251,10 +356,6 @@
}
}
- private void scheduleConfigReload() {
- getLoggerContext().getExecutorService().submit(new LoggerReconfigurer());
- }
-
private String getRootDir(BundleContext bundleContext) {
String rootDir = bundleContext.getProperty("sling.home");
if (rootDir == null) {
@@ -264,31 +365,84 @@
return rootDir;
}
- private class LoggerReconfigurer implements Runnable {
+ //~-------------------------------------------------- Slf4j Bridge Handler Support
- 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();
+ /**
+ * 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");
}
- } 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");
+ }
+
+ 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");
+ }
}
}
- // ~-------------------------------LogggerContextListener
+ /**
+ * 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 {
@@ -307,20 +461,24 @@
addInfo("OsgiIntegrationListener : context reset detected. Adding LogManager to context map and firing"
+ " listeners");
+ context.setPackagingDataEnabled(logConfigManager.isPackagingDataEnabled());
+ context.setMaxCallerDataDepth(logConfigManager.getMaxCallerDataDepth());
+
// Attach a console appender to handle logging untill we configure
- // one. This would be removed in LogConfigManager.reset
+ // 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);
}
}
@@ -333,6 +491,31 @@
}
+ 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 {
@@ -432,19 +615,45 @@
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);
- }
+ //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;
}
@@ -456,7 +665,9 @@
/**
* List of logger which have explicitly defined level or appenders set
*/
- final List<Logger> loggerInfos = new ArrayList<Logger>();
+ 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>>();
@@ -507,20 +718,18 @@
}
}
- private void registerWebConsoleSupport(BundleContext context) {
- final ServiceFactory serviceFactory = new PluginServiceFactory(PLUGIN_URL);
+ private void registerWebConsoleSupport() {
+ final ServiceFactory serviceFactory = new PluginServiceFactory();
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.label", APP_ROOT);
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"
- });
+ pluginProps.put("felix.webconsole.css", CSS_REFS);
- registrations.add(context.registerService("javax.servlet.Servlet", serviceFactory, pluginProps));
+ registrations.add(bundleContext.registerService("javax.servlet.Servlet", serviceFactory, pluginProps));
Properties printerProps = new Properties();
printerProps.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
@@ -530,23 +739,17 @@
printerProps.put("felix.webconsole.configprinter.modes", "always");
// TODO need to see to add support for Inventory Feature
- registrations.add(context.registerService(SlingConfigurationPrinter.class.getName(),
+ registrations.add(bundleContext.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);
+ this.instance = new SlingLogPanel(LogbackManager.this,bundleContext);
}
return instance;
}
@@ -556,7 +759,7 @@
}
}
- private void registerEventHandler(BundleContext bundleContext) {
+ private void registerEventHandler() {
Properties props = new Properties();
props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
props.put(Constants.SERVICE_DESCRIPTION, "Sling Log Reset Event Handler");
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java b/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java
index c275210..bcd3535 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/SlingConfigurationPrinter.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.io.FileReader;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
@@ -30,12 +31,16 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.rolling.RollingFileAppender;
+import ch.qos.logback.core.status.Status;
+import ch.qos.logback.core.util.CachingDateFormatter;
/**
* The <code>SlingConfigurationPrinter</code> is an Apache Felix Web Console
* plugin to display the currently configured log files.
*/
public class SlingConfigurationPrinter {
+ private static final CachingDateFormatter SDF = new CachingDateFormatter("yyyy-MM-dd HH:mm:ss");
private final LogbackManager logbackManager;
public SlingConfigurationPrinter(LogbackManager logbackManager) {
@@ -48,6 +53,7 @@
@SuppressWarnings("UnusedDeclaration")
public void printConfiguration(PrintWriter printWriter) {
LogbackManager.LoggerStateContext ctx = logbackManager.determineLoggerState();
+ dumpLogbackStatus(logbackManager, printWriter);
for (Appender<ILoggingEvent> appender : ctx.getAllAppenders()) {
if (appender instanceof FileAppender) {
final File file = new File(((FileAppender) appender).getFile());
@@ -69,7 +75,7 @@
if (fr != null) {
try {
fr.close();
- } catch (IOException ignoreCloseException) {
+ } catch (IOException ignored) {
}
}
}
@@ -80,8 +86,10 @@
}
/**
- * TODO Need to see how to implement this with LogBack as we cannot get
- * information about all rolled over policy
+ * Attempts to determine all log files created even via rotation.
+ * if some complex rotation logic is used where rotated file get different names
+ * or get created in different directory then those files would not be
+ * included
*
* @see org.apache.felix.webconsole.AttachmentProvider#getAttachments(String)
*/
@@ -93,15 +101,7 @@
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
- };
+ final File[] files = getRotatedFiles((FileAppender) appender);
for (File f : files) {
try {
urls.add(f.toURI().toURL());
@@ -118,4 +118,55 @@
return null;
}
+ private static File[] getRotatedFiles(FileAppender app) {
+ final File file = new File(app.getFile());
+
+ //If RollingFileAppender then make an attempt to list files
+ //This might not work in all cases if complex rolling patterns
+ //are used in Logback
+ if (app instanceof RollingFileAppender) {
+ final File dir = file.getParentFile();
+ final String baseName = file.getName();
+ return dir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.startsWith(baseName);
+ }
+ });
+ }
+
+ //Not a RollingFileAppender then just return the actual file
+ return new File[]{file};
+ }
+
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ private static void dumpLogbackStatus(LogbackManager logbackManager, PrintWriter pw) {
+ List<Status> statusList = logbackManager.getStatusManager().getCopyOfStatusList();
+ pw.println("Logback Status");
+ pw.println("--------------------------------------------------");
+ for (Status s : statusList) {
+ pw.printf("%s *%s* %s - %s %n",
+ SDF.format(s.getDate()),
+ statusLevelAsString(s),
+ SlingLogPanel.abbreviatedOrigin(s),
+ s.getMessage());
+ if (s.getThrowable() != null) {
+ s.getThrowable().printStackTrace(pw);
+ }
+ }
+
+ pw.println();
+ }
+
+ private static String statusLevelAsString(Status s) {
+ switch (s.getEffectiveLevel()) {
+ case Status.INFO:
+ return "INFO";
+ case Status.WARN:
+ return "WARN";
+ case Status.ERROR:
+ return "ERROR";
+ }
+ return null;
+ }
+
}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java b/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java
index 540f7db..1e352da 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/SlingLogPanel.java
@@ -27,13 +27,19 @@
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.turbo.TurboFilter;
@@ -45,20 +51,26 @@
import ch.qos.logback.core.util.CachingDateFormatter;
import org.apache.sling.commons.log.logback.internal.AppenderTracker.AppenderInfo;
import org.apache.sling.commons.log.logback.internal.LogbackManager.LoggerStateContext;
+import org.apache.sling.commons.log.logback.internal.config.ConfigurationException;
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.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import static org.apache.sling.commons.log.logback.internal.ConfigSourceTracker.ConfigSourceInfo;
+import static org.apache.sling.commons.log.logback.internal.LogbackManager.APP_ROOT;
+import static org.apache.sling.commons.log.logback.internal.LogbackManager.RES_LOC;
/**
* The <code>SlingLogPanel</code> is a Felix Web Console plugin to display the
* current active log bundle configuration.
- * <p>
+ * <p/>
* In future revisions of this plugin, the configuration may probably even be
* modified through this panel.
*/
@@ -68,7 +80,18 @@
private final CachingDateFormatter SDF = new CachingDateFormatter("yyyy-MM-dd HH:mm:ss");
+ private static final String[] LEVEL_NAMES = {
+ Level.ERROR.levelStr,
+ Level.WARN.levelStr,
+ Level.INFO.levelStr,
+ Level.DEBUG.levelStr,
+ Level.TRACE.levelStr,
+ };
+
+ private static final String PACKAGE_SEPARATOR = ".";
+
private final LogbackManager logbackManager;
+ private final BundleContext bundleContext;
private final String labelRes;
@@ -76,9 +99,10 @@
private static final org.slf4j.Logger log = LoggerFactory.getLogger(SlingLogPanel.class);
- public SlingLogPanel(final LogbackManager logbackManager, String label) {
+ public SlingLogPanel(final LogbackManager logbackManager, BundleContext bundleContext) {
this.logbackManager = logbackManager;
- this.labelRes = '/' + label + '/';
+ this.bundleContext = bundleContext;
+ this.labelRes = '/' + APP_ROOT + '/';
this.labelResLen = labelRes.length() - 1;
}
@@ -92,32 +116,161 @@
final LoggerStateContext ctx = logbackManager.determineLoggerState();
appendLoggerStatus(pw, ctx);
- appendLoggerData(pw, ctx);
+ appendOsgiConfiguredLoggerData(pw, consoleAppRoot);
+ appendOtherLoggerData(pw, ctx);
addAppenderData(pw, consoleAppRoot, ctx);
- appendTurboFilterData(pw, consoleAppRoot,ctx);
+ appendTurboFilterData(pw, consoleAppRoot, ctx);
appendLogbackMainConfig(pw);
appendLogbackFragments(pw, consoleAppRoot);
appendLogbackStatus(pw, ctx);
- enablePrettifier(pw, pluginRoot);
+ addScriptBlock(pw, ctx);
}
+ @Override
+ protected void doPost(final HttpServletRequest req, final HttpServletResponse resp)
+ throws ServletException, IOException {
+ // check if a configuration should be deleted
+ boolean isDelete = req.getParameter("delete") != null;
+ // get the configuration pid
+ String pid = req.getParameter("pid");
+ try {
+ if (isDelete) {
+ // in delete mode remove the logger with the given pid
+ removeLogger(pid);
+ } else {
+ // get the logger parameters and configure the logger
+ // if the given pid is empty a new logger with be created
+ String logger = req.getParameter("logger");
+ String logLevel = req.getParameter("loglevel");
+ String logFile = req.getParameter("logfile");
+ String[] loggers = req.getParameterValues("logger");
+ if (null != logger) {
+ configureLogger(pid, logLevel, loggers, logFile);
+ }
+ }
+ } catch (ConfigurationException e) {
+ internalFailure("", e);
+ }
+ // send the redirect back to the logpanel
+ final String consoleAppRoot = (String) req
+ .getAttribute("felix.webconsole.appRoot");
+ resp.sendRedirect(consoleAppRoot + "/" + APP_ROOT);
+ }
- private void enablePrettifier(PrintWriter pw, String pluginRoot) {
- pw.printf("<script type=\"text/javascript\" src=\"%s/res/ui/prettify.js\"></script>", pluginRoot);
+ private void addScriptBlock(PrintWriter pw, LoggerStateContext ctx) {
+ pw.println("<script type=\"text/javascript\" src=\"" + RES_LOC + "/slinglog.js\"></script>");
+ pw.println("<script type=\"text/javascript\" src=\"" + RES_LOC + "/jquery.autocomplete.min.js\"></script>");
+ pw.println("<script type=\"text/javascript\" src=\"" + RES_LOC + "/prettify.js\"></script>");
+
+ pw.println("<script type=\"text/javascript\">$(document).ready(function() { initializeSlingLogPanel(); });</script>");
+ pw.println("<script>");
+ // write all present loggers as script variable so the autocomplete script can search over them
+ pw.println("var loggers=[");
+ Set<String> loggers = new TreeSet<String>();
+
+ for (Logger logger : ctx.loggerContext.getLoggerList()) {
+ loggers.add(logger.getName());
+ }
+
+ Set<String> packageList = new TreeSet<String>();
+ for (String logger : loggers) {
+ int pos = logger.lastIndexOf(PACKAGE_SEPARATOR);
+ if (pos != -1) {
+ String pack = logger.substring(0, pos);
+ packageList.add(pack);
+ }
+ }
+ loggers.addAll(packageList);
+ for (Iterator<String> loggerIt = loggers.iterator(); loggerIt.hasNext(); ) {
+ String logger = loggerIt.next();
+ pw.print("'" + logger + "'");
+ if (loggerIt.hasNext()) {
+ pw.print(",");
+ }
+ }
+ pw.println("];");
+ pw.println("</script>");
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());
+ "<p class='statline'>Log Service Stats: %d categories, %d appender, %d Dynamic appenders</p>%n",
+ ctx.getNumberOfLoggers(), ctx.getNumOfAppenders(), ctx.getNumOfDynamicAppenders());
}
- private void appendLoggerData(PrintWriter pw, LoggerStateContext ctx) {
+ private void appendOsgiConfiguredLoggerData(PrintWriter pw, String consoleAppRoot) {
pw.println("<div class='table'>");
- pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logger</div>");
+ pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logger (Configured via OSGi Config)</div>");
+
+ pw.println("<form method='POST'><table id=\"loggerConfig\" class='tablesorter nicetable ui-widget'>");
+
+ pw.println("<thead class='ui-widget-header'>");
+ pw.println("<tr>");
+ pw.println("<th>Log Level</th>");
+ pw.println("<th>Log File</th>");
+ pw.println("<th>Logger</th>");
+ pw.println("<th width=\"20%\">" + getConfigColTitle(consoleAppRoot) + "</th>");
+ pw.println("</tr>");
+ pw.println("</thead>");
+ pw.println("<tbody class='ui-widget-content'>");
+
+ final LogConfigManager configManager = logbackManager.getLogConfigManager();
+ String rootPath = logbackManager.getRootDir();
+ boolean shortenPaths = areAllLogfilesInSameFolder(configManager.getLogWriters(), rootPath);
+ for (LogConfig logConfig : configManager.getLogConfigs()) {
+ pw.println("<tr id=\"" + logConfig.getConfigPid() + "\">");
+ pw.println("<td><span class=\"logLevels\" data-currentloglevel=\""
+ + logConfig.getLogLevel().levelStr + "\">" + logConfig.getLogLevel().levelStr
+ + "</span></td>");
+ pw.println("<td><span class=\"logFile\">" + getPath(logConfig.getLogWriterName(), rootPath, shortenPaths) + "</span></td>");
+
+ pw.println("<td><span class=\"loggers\">");
+ String sep = "";
+ for (String cat : logConfig.getCategories()) {
+ pw.println(sep + "<span class=\"logger\">" + cat + "</span>");
+ sep = "<br />";
+ }
+ pw.println("</td>");
+
+ String pid = logConfig.getConfigPid();
+ String url = createUrl(consoleAppRoot, "configMgr", pid, true);
+ if (logConfig.getCategories().contains(Logger.ROOT_LOGGER_NAME)) {
+ url = createUrl(consoleAppRoot, "configMgr", pid, false);
+ }
+ pw.println("<td>" + url + "</td>");
+ pw.println("</tr>");
+ }
+
+ pw.println("</tbody><tfoot>");
+ pw.println("<tr id=\"newlogger\">");
+ pw.println("<td><span id=\"allLogLevels\" class=\"logLevels\" data-loglevels=\"");
+ String sep = "";
+ for (String levelName : LEVEL_NAMES) {
+ pw.print(sep + levelName);
+ sep = ",";
+ }
+
+ pw.println("\"></span></td>");
+ pw.println("<td><span id=\"defaultLogfile\" data-defaultlogfile=\""
+ + getPath(configManager.getDefaultWriter().getFileName(), rootPath, shortenPaths)
+ + "\" class=\"logFile\"></span></td>");
+ pw.println("<td><span class=\"loggers\"></span></td>");
+ pw.println("<td><input type='submit' class=\"configureLink\" value='Add new Logger' /></td></tr></tfoot>");
+
+ pw.println("</table></form>");
+ pw.println("</div>");
+ }
+
+ private void appendOtherLoggerData(PrintWriter pw, LoggerStateContext ctx) {
+ if (ctx.nonOSgiConfiguredLoggers.isEmpty()) {
+ return;
+ }
+
+ pw.println("<div class='table'>");
+
+ pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>Logger (Configured via other means)</div>");
pw.println("<table class='nicetable ui-widget'>");
@@ -127,12 +280,11 @@
pw.println("<th>Additivity</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) {
+ for (Logger logger : ctx.nonOSgiConfiguredLoggers) {
pw.println("<tr>");
pw.println("<td>" + logger.getLevel() + "</td>");
pw.println("<td>" + Boolean.toString(logger.isAdditive()) + "</td>");
@@ -144,12 +296,11 @@
while (itr.hasNext()) {
Appender<ILoggingEvent> a = itr.next();
pw.print("<li>");
- pw.println(getName(a));
+ pw.print(getName(a));
pw.print("</li>");
}
pw.println("</ul>");
pw.println("</td>");
-
pw.println("</tr>");
}
@@ -185,8 +336,8 @@
pw.println("</div>");
}
- private void appendTurboFilterData(PrintWriter pw, String consoleAppRoot,LoggerStateContext ctx) {
- if(ctx.loggerContext.getTurboFilterList().isEmpty()){
+ private void appendTurboFilterData(PrintWriter pw, String consoleAppRoot, LoggerStateContext ctx) {
+ if (ctx.loggerContext.getTurboFilterList().isEmpty()) {
return;
}
@@ -205,7 +356,7 @@
pw.println("<tbody class='ui-widget-content'>");
- for(TurboFilter tf : ctx.loggerContext.getTurboFilterList()){
+ for (TurboFilter tf : ctx.loggerContext.getTurboFilterList()) {
pw.println("<tr>");
pw.println("<td>" + getName(tf) + "</td>");
pw.println("<td>" + formatPid(consoleAppRoot, tf, ctx) + "</td>");
@@ -348,7 +499,7 @@
* 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.
*/
@@ -359,10 +510,135 @@
: null;
}
+ /**
+ * Checks if all log files are in the same folder, then the path can displayed shortened in the panel.
+ *
+ * @param logWriters list of log writers
+ * @param rootPath root path
+ * @return true if all logfiles are in the same folder
+ */
+ private boolean areAllLogfilesInSameFolder(final Iterable<LogWriter> logWriters, final String rootPath) {
+ String lastPath = null;
+ for (final LogWriter writer : logWriters) {
+ String path = getPath(writer.getFileName(), null, false);
+ if (!path.startsWith(rootPath)) {
+ return false;
+ }
+ path = path.substring(0, rootPath.length());
+ if (lastPath == null) {
+ lastPath = path;
+ } else if (!path.equals(lastPath)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Configures the logger with the given pid. If the pid is empty a new logger configuration is created.
+ *
+ * @param pid configuration pid of the logger
+ * @param logLevel the log level to set
+ * @param loggers list of logger categories to set
+ * @param logFile log file (relative path is ok)
+ * @throws IOException when an existing configuration couldn't be updated or a configuration couldn't be created.
+ * @throws ConfigurationException when mandatory parameters where not specified
+ */
+ private void configureLogger(final String pid, final String logLevel, final String[] loggers, final String logFile)
+ throws IOException, ConfigurationException {
+ // try to get the configadmin service reference
+ ServiceReference sr = this.bundleContext
+ .getServiceReference(ConfigurationAdmin.class.getName());
+ if (sr != null) {
+ try {
+ if (logLevel == null) {
+ throw new ConfigurationException(LogConfigManager.LOG_LEVEL,
+ "Log level has to be specified.");
+ }
+ if (loggers == null) {
+ throw new ConfigurationException(LogConfigManager.LOG_LOGGERS,
+ "Logger categories have to be specified.");
+ }
+ if (logFile == null) {
+ throw new ConfigurationException(LogConfigManager.LOG_FILE,
+ "LogFile name has to be specified.");
+ }
+ // try to get the configadmin
+ final ConfigurationAdmin configAdmin = (ConfigurationAdmin) this.bundleContext
+ .getService(sr);
+ if (configAdmin != null) {
+ Configuration config;
+ if (pid == null || pid.length() == 0) {
+ config = configAdmin.createFactoryConfiguration(LogConfigManager.FACTORY_PID_CONFIGS);
+ } else {
+ config = configAdmin.getConfiguration(pid);
+ }
+ if (config != null) {
+ Dictionary<String, Object> dict = new Hashtable<String, Object>();
+ dict.put(LogConfigManager.LOG_LEVEL, logLevel.toLowerCase());
+ dict.put(LogConfigManager.LOG_LOGGERS, loggers);
+ dict.put(LogConfigManager.LOG_FILE, logFile);
+ config.update(dict);
+ }
+ }
+ } finally {
+ // release the configadmin reference
+ this.bundleContext.ungetService(sr);
+ }
+ }
+ }
+
+
+ /**
+ * Removes the logger configuration with the given pid in the configadmin.
+ *
+ * @param pid pid of the configuration to delete
+ * @throws ConfigurationException when there is no configuration for this pid
+ */
+ private void removeLogger(final String pid)
+ throws ConfigurationException {
+ // try to get the configadmin service reference
+ ServiceReference sr = this.bundleContext
+ .getServiceReference(ConfigurationAdmin.class.getName());
+ if (sr != null) {
+ try {
+ if (pid == null) {
+ throw new ConfigurationException(LogConfigManager.PID,
+ "PID has to be specified.");
+ }
+ // try to get the configadmin
+ final ConfigurationAdmin configAdmin = (ConfigurationAdmin) this.bundleContext
+ .getService(sr);
+ if (configAdmin != null) {
+ try {
+ Configuration config = configAdmin.getConfiguration(pid);
+ if (config != null) {
+ config.delete();
+ } else {
+ throw new ConfigurationException(LogConfigManager.PID,
+ "No configuration for this PID:" + pid);
+ }
+ } catch (IOException ioe) {
+ internalFailure(
+ "Cannot delete configuration for pid " + pid,
+ ioe);
+ }
+ }
+ } finally {
+ // release the configadmin reference
+ this.bundleContext.ungetService(sr);
+ }
+ }
+ }
+
+ private void internalFailure(String msg, Exception e) {
+ logbackManager.getLogConfigManager().internalFailure(msg, e);
+ }
+
private static String getName(TurboFilter tf) {
- if(tf.getName() != null){
+ if (tf.getName() != null) {
return String.format("%s (%s)", tf.getName(), tf.getClass().getName());
- } else{
+ } else {
return tf.getClass().getName();
}
}
@@ -384,18 +660,22 @@
return String.format("File : [%s] %s", appender.getName(), ((FileAppender) appender).getFile());
}
- return String.format("%s (%s)", appender.getName(), appender.getClass().getName());
+ final String appenderName = appender.getName();
+ if(appenderName == null){
+ return appender.getClass().getName();
+ } else {
+ return String.format("%s (%s)", appender.getName(), appender.getClass().getName());
+ }
}
private static String formatPid(final String consoleAppRoot, final Appender<ILoggingEvent> appender,
- final LoggerStateContext ctx) {
+ final LoggerStateContext ctx) {
if (appender instanceof SlingRollingFileAppender) {
final LogWriter lw = ((SlingRollingFileAppender) appender).getLogWriter();
+ String pid = lw.getConfigurationPID();
if (lw.isImplicit()) {
- return "[implicit]";
+ pid = lw.getImplicitConfigPID();
}
-
- final String pid = lw.getConfigurationPID();
return createUrl(consoleAppRoot, "configMgr", pid);
} else if (ctx.isDynamicAppender(appender)) {
final AppenderInfo ai = ctx.dynamicAppenders.get(appender);
@@ -412,16 +692,35 @@
}
private static String createUrl(String consoleAppRoot, String subContext, String pid) {
+ return createUrl(consoleAppRoot, subContext, pid, false);
+ }
+
+ private static String createUrl(String consoleAppRoot, String subContext, String pid, boolean inlineEditable) {
// 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>";
+ String classAttr = "class=\"configureLink\"";
+ if (!inlineEditable) {
+ classAttr = "";
+ }
+
+ return "<a " + classAttr + " href=\"" + subContext + "/" + pid + "\"><img src=\"" + consoleAppRoot
+ + "/res/imgs/component_configure.png\" border=\"0\" /></a>";
}
+ private static String getPath(String path, final String rootPath, final boolean shortenPaths) {
+ if (shortenPaths && path != null) {
+ // if the shortenPath parameter is set (all log files are in the same folder)
+ // remove the root path (root log file folder) from the paths
+ path = path.substring(rootPath.length() + 1);
+ }
+ return (path != null) ? path : "[stdout]";
+ }
+
+
// ~------------------------------------------------Status Manager
// Based on ch.qos.logback.core.status.ViewStatusMessagesServletBase
@@ -437,7 +736,7 @@
return null;
}
- private static String abbreviatedOrigin(Status s) {
+ static String abbreviatedOrigin(Status s) {
Object o = s.getOrigin();
if (o == null) {
return null;
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingContextUtil.java b/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingContextUtil.java
new file mode 100644
index 0000000..5ddcad7
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingContextUtil.java
@@ -0,0 +1,40 @@
+/*
+ * 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.Context;
+import ch.qos.logback.core.spi.ContextAwareBase;
+
+/**
+ * Custom util such that origin can be customized
+ */
+public class SlingContextUtil extends ContextAwareBase {
+ private final Object origin;
+
+ public SlingContextUtil(Context context, Object origin) {
+ this.origin = origin;
+ setContext(context);
+ }
+
+ @Override
+ protected Object getDeclaredOrigin() {
+ return origin;
+ }
+}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingStatusPrinter.java b/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingStatusPrinter.java
new file mode 100644
index 0000000..e5fa4e4
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/util/SlingStatusPrinter.java
@@ -0,0 +1,101 @@
+/*
+ * 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.PrintStream;
+import java.util.List;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.status.ErrorStatus;
+import ch.qos.logback.core.status.Status;
+import ch.qos.logback.core.status.StatusManager;
+import ch.qos.logback.core.status.StatusUtil;
+import ch.qos.logback.core.util.StatusPrinter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom StatusPrinter similar to Logback StatusPrinter to account for changes required
+ * for SLING-3410
+ */
+public class SlingStatusPrinter {
+
+ /**
+ * Based on StatusPrinter. printInCaseOfErrorsOrWarnings. This has been adapted
+ * to print more context i.e. some message from before the error message to better understand
+ * the failure scenario
+ *
+ * @param threshold time since which the message have to be checked for errors/warnings
+ * @param msgSince time form which we are interested in the message logs
+ * @param initSuccess flag indicating if Logback configuration failed or not
+ */
+ public static void printInCaseOfErrorsOrWarnings(Context context, long threshold,
+ long msgSince, boolean initSuccess) {
+ if (context == null) {
+ throw new IllegalArgumentException("Context argument cannot be null");
+ }
+ PrintStream ps = System.out;
+ StatusManager sm = context.getStatusManager();
+ if (sm == null) {
+ ps.println("WARN: Context named \"" + context.getName()
+ + "\" has no status manager");
+ } else {
+ StatusUtil statusUtil = new StatusUtil(context);
+ if (statusUtil.getHighestLevel(threshold) >= ErrorStatus.WARN) {
+ List<Status> filteredList =
+ StatusUtil.filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), msgSince);
+ print(filteredList, initSuccess);
+ }
+ }
+ }
+
+ private static void print(List<Status> statusList, boolean initSuccess) {
+ if (statusList == null) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ if (initSuccess) {
+ sb.append("While (re)configuring Logback transient issues were observed. " +
+ "More details are provided below.");
+ sb.append(CoreConstants.LINE_SEPARATOR);
+ }
+
+ String prefix = "";
+ if(initSuccess){
+ prefix = "*Logback Status* ";
+ }
+
+ for (Status s : statusList) {
+ StatusPrinter.buildStr(sb, prefix, s);
+ }
+
+ //In case logging system completely fails then log the message in System out
+ //otherwise make it part of 'normal' logs
+ if (!initSuccess) {
+ System.out.println(sb.toString());
+ } else {
+ Logger logger = LoggerFactory.getLogger(SlingStatusPrinter.class);
+ logger.info(sb.toString());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java b/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java
index 87181fd..90efb5f 100644
--- a/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/util/Util.java
@@ -76,4 +76,71 @@
}
}
+ //-------------Taken from org.apache.sling.commons.osgi.PropertiesUtil
+
+ /**
+ * Returns the boolean value of the parameter or the
+ * <code>defaultValue</code> if the parameter is <code>null</code>.
+ * If the parameter is not a <code>Boolean</code> it is converted
+ * by calling <code>Boolean.valueOf</code> on the string value of the
+ * object.
+ * @param propValue the property value or <code>null</code>
+ * @param defaultValue the default boolean value
+ */
+ public static boolean toBoolean(Object propValue, boolean defaultValue) {
+ propValue = toObject(propValue);
+ if (propValue instanceof Boolean) {
+ return (Boolean) propValue;
+ } else if (propValue != null) {
+ return Boolean.valueOf(String.valueOf(propValue));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Returns the parameter as an integer or the
+ * <code>defaultValue</code> if the parameter is <code>null</code> or if
+ * the parameter is not an <code>Integer</code> and cannot be converted to
+ * an <code>Integer</code> from the parameter's string value.
+ * @param propValue the property value or <code>null</code>
+ * @param defaultValue the default integer value
+ */
+ public static int toInteger(Object propValue, int defaultValue) {
+ propValue = toObject(propValue);
+ if (propValue instanceof Integer) {
+ return (Integer) propValue;
+ } else if (propValue != null) {
+ try {
+ return Integer.valueOf(String.valueOf(propValue));
+ } catch (NumberFormatException nfe) {
+ // don't care, fall through to default value
+ }
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Returns the parameter as a single value. If the
+ * parameter is neither an array nor a <code>java.util.Collection</code> the
+ * parameter is returned unmodified. If the parameter is a non-empty array,
+ * the first array element is returned. If the property is a non-empty
+ * <code>java.util.Collection</code>, the first collection element is returned.
+ * Otherwise <code>null</code> is returned.
+ * @param propValue the parameter to convert.
+ */
+ public static Object toObject(Object propValue) {
+ if (propValue == null) {
+ return null;
+ } else if (propValue.getClass().isArray()) {
+ Object[] prop = (Object[]) propValue;
+ return prop.length > 0 ? prop[0] : null;
+ } else if (propValue instanceof Collection<?>) {
+ Collection<?> prop = (Collection<?>) propValue;
+ return prop.isEmpty() ? null : prop.iterator().next();
+ } else {
+ return propValue;
+ }
+ }
}
diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties
index 848ef05..aefa58f 100644
--- a/src/main/resources/OSGI-INF/metatype/metatype.properties
+++ b/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -17,6 +17,8 @@
# under the License.
#
+# suppress inspection "UnusedProperty" for whole file
+
log.name = Apache Sling Logging Configuration
log.description = Global configuration setup for the Sling OSGi LogService \
implementation. See http://sling.apache.org/site/logging.html for more \
@@ -48,12 +50,8 @@
pattern. The default is "'.'yyyy-MM-dd" (daily log rotation).
log.pattern.name = Message Pattern
-log.pattern.description = Message Pattern for formatting the log messages. \
- This is a java.text.MessageFormat pattern supporting up to six arguments: \
- {0} The timestamp of type java.util.Date, {1} the log marker, {2} the name \
- of the current thread, {3} the name of the logger, {4} the debug level and \
- {5} the actual debug message. If the log call includes a Throwable, the \
- stacktrace is just appended to the message.
+log.pattern.description = Message Pattern for formatting the log messages. For \
+ complete details refer to http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout
log.factory.writer.name = Apache Sling Logging Writer Configuration
log.factory.writer.description = Configure a Logger Writer for Sling Logging. \
@@ -70,4 +68,19 @@
configuration. Each logger name applies for any child category unless configured \
otherwise. E.g. a logger name of org.apache.sling applies to logger \
org.apache.sling.commons unless there is a different configuration for \
- org.apache.sling.commons.
+ org.apache.sling.commons.
+
+log.config.packagingData.name = Packaging Data
+log.config.packagingData.description = Include the packaging data which provide \
+ details about jar name and version of jar from which the class is loaded as part \
+ of stacktrace. See http://logback.qos.ch/reasonsToSwitch.html#packagingData
+
+log.config.maxCallerDataDepth.name = Max Caller Depth
+log.config.maxCallerDataDepth.description = The stack data depth computed during caller data \
+ extraction. See http://logback.qos.ch/manual/layouts.html#caller
+
+log.file.buffered.name = Buffered Logging
+log.file.buffered.description = By default logging events are immediately written to disk \
+ and will not be lost in case your application exits without properly closing appenders. \
+ If set to true and if appenders are not closed properly when your application exits, then \
+ logging events not yet written to disk may be lost. See http://logback.qos.ch/manual/encoders.html#immediateFlush
\ No newline at end of file
diff --git a/src/main/resources/OSGI-INF/metatype/metatype.xml b/src/main/resources/OSGI-INF/metatype/metatype.xml
index 459d4e0..348f736 100644
--- a/src/main/resources/OSGI-INF/metatype/metatype.xml
+++ b/src/main/resources/OSGI-INF/metatype/metatype.xml
@@ -44,12 +44,22 @@
description="%log.file.size.description" />
<metatype:AD id="org.apache.sling.commons.log.pattern"
type="String"
- default="{0\,date\,dd.MM.yyyy HH:mm:ss.SSS} *{4}* [{2}] {3} {5}"
+ default="%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n"
name="%log.pattern.name"
description="%log.pattern.description" />
- <metatype:AD id="org.apache.sling.commons.log.configurationFile"
- type="String" name="%log.config.file.name"
+ <metatype:AD id="org.apache.sling.commons.log.configurationFile"
+ type="String"
+ name="%log.config.file.name"
description="%log.config.file.description" />
+ <metatype:AD id="org.apache.sling.commons.log.packagingDataEnabled"
+ type="Boolean"
+ name="%log.config.packagingData.name"
+ description="%log.config.packagingData.description" />
+ <metatype:AD id="org.apache.sling.commons.log.maxCallerDataDepth"
+ type="Integer"
+ default="7"
+ name="%log.config.maxCallerDataDepth.name"
+ description="%log.config.maxCallerDataDepth.description" />
</metatype:OCD>
<metatype:Designate pid="org.apache.sling.commons.log.LogManager">
<metatype:Object
@@ -70,6 +80,9 @@
<metatype:AD id="org.apache.sling.commons.log.file.size"
type="String" default="'.'yyyy-MM-dd" name="%log.file.size.name"
description="%log.file.size.description" />
+ <metatype:AD id="org.apache.sling.commons.log.file.buffered"
+ type="Boolean" default="false" name="%log.file.buffered.name"
+ description="%log.file.buffered.description" />
</metatype:OCD>
<metatype:Designate
pid="org.apache.sling.commons.log.LogManager.factory.writer"
diff --git a/src/main/resources/res/ui/jquery.autocomplete.css b/src/main/resources/res/ui/jquery.autocomplete.css
new file mode 100644
index 0000000..9a95dc7
--- /dev/null
+++ b/src/main/resources/res/ui/jquery.autocomplete.css
@@ -0,0 +1,38 @@
+/**
+ * @fileOverview CSS for jquery-autocomplete, the jQuery Autocompleter
+ * @author <a href="mailto:dylan@dyve.net">Dylan Verheul</a>
+ * @license MIT | GPL | Apache 2.0, see LICENSE.txt
+ * @see https://github.com/dyve/jquery-autocomplete
+ */
+.acResults {
+ padding: 0px;
+ border: 1px solid WindowFrame;
+ background-color: Window;
+ overflow: hidden;
+}
+
+.acResults ul {
+ margin: 0px;
+ padding: 0px;
+ list-style-position: outside;
+ list-style: none;
+}
+
+.acResults ul li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: pointer;
+ display: block;
+ font: menu;
+ font-size: 12px;
+ overflow: hidden;
+}
+
+.acLoading {
+ background : url('indicator.gif') right center no-repeat;
+}
+
+.acSelect {
+ background-color: Highlight;
+ color: HighlightText;
+}
\ No newline at end of file
diff --git a/src/main/resources/res/ui/jquery.autocomplete.min.js b/src/main/resources/res/ui/jquery.autocomplete.min.js
new file mode 100644
index 0000000..4bb8d58
--- /dev/null
+++ b/src/main/resources/res/ui/jquery.autocomplete.min.js
@@ -0,0 +1,9 @@
+/**
+ * @fileOverview jquery-autocomplete, the jQuery Autocompleter
+ * @author <a href="mailto:dylan@dyve.net">Dylan Verheul</a>
+ * @version 2.4.4
+ * @requires jQuery 1.6+
+ * @license MIT | GPL | Apache 2.0, see LICENSE.txt
+ * @see https://github.com/dyve/jquery-autocomplete
+ */
+(function($){"use strict";$.fn.autocomplete=function(options){var url;if(arguments.length>1){url=options;options=arguments[1];options.url=url}else if(typeof options==="string"){url=options;options={url:url}}var opts=$.extend({},$.fn.autocomplete.defaults,options);return this.each(function(){var $this=$(this);$this.data("autocompleter",new $.Autocompleter($this,$.meta?$.extend({},opts,$this.data()):opts))})};$.fn.autocomplete.defaults={inputClass:"acInput",loadingClass:"acLoading",resultsClass:"acResults",selectClass:"acSelect",queryParamName:"q",extraParams:{},remoteDataType:false,lineSeparator:"\n",cellSeparator:"|",minChars:2,maxItemsToShow:10,delay:400,useCache:true,maxCacheLength:10,matchSubset:true,matchCase:false,matchInside:true,mustMatch:false,selectFirst:false,selectOnly:false,showResult:null,preventDefaultReturn:1,preventDefaultTab:0,autoFill:false,filterResults:true,filter:true,sortResults:true,sortFunction:null,onItemSelect:null,onNoMatch:null,onFinish:null,matchStringConverter:null,beforeUseConverter:null,autoWidth:"min-width",useDelimiter:false,delimiterChar:",",delimiterKeyCode:188,processData:null,onError:null,enabled:true};var sanitizeResult=function(result){var value,data;var type=typeof result;if(type==="string"){value=result;data={}}else if($.isArray(result)){value=result[0];data=result.slice(1)}else if(type==="object"){value=result.value;data=result.data}value=String(value);if(typeof data!=="object"){data={}}return{value:value,data:data}};var sanitizeInteger=function(value,stdValue,options){var num=parseInt(value,10);options=options||{};if(isNaN(num)||options.min&&num<options.min){num=stdValue}return num};var makeUrlParam=function(name,value){return[name,encodeURIComponent(value)].join("=")};var makeUrl=function(url,params){var urlAppend=[];$.each(params,function(index,value){urlAppend.push(makeUrlParam(index,value))});if(urlAppend.length){url+=url.indexOf("?")===-1?"?":"&";url+=urlAppend.join("&")}return url};var sortValueAlpha=function(a,b,matchCase){a=String(a.value);b=String(b.value);if(!matchCase){a=a.toLowerCase();b=b.toLowerCase()}if(a>b){return 1}if(a<b){return-1}return 0};var plainTextParser=function(text,lineSeparator,cellSeparator){var results=[];var i,j,data,line,value,lines;lines=String(text).replace("\r\n","\n").split(lineSeparator);for(i=0;i<lines.length;i++){line=lines[i].split(cellSeparator);data=[];for(j=0;j<line.length;j++){data.push(decodeURIComponent(line[j]))}value=data.shift();results.push({value:value,data:data})}return results};$.Autocompleter=function($elem,options){if(!$elem||!($elem instanceof $)||$elem.length!==1||$elem.get(0).tagName.toUpperCase()!=="INPUT"){throw new Error("Invalid parameter for jquery.Autocompleter, jQuery object with one element with INPUT tag expected.")}var self=this;this.options=options;this.cacheData_={};this.cacheLength_=0;this.selectClass_="jquery-autocomplete-selected-item";this.keyTimeout_=null;this.finishTimeout_=null;this.lastKeyPressed_=null;this.lastProcessedValue_=null;this.lastSelectedValue_=null;this.active_=false;this.finishOnBlur_=true;this.options.minChars=sanitizeInteger(this.options.minChars,$.fn.autocomplete.defaults.minChars,{min:0});this.options.maxItemsToShow=sanitizeInteger(this.options.maxItemsToShow,$.fn.autocomplete.defaults.maxItemsToShow,{min:0});this.options.maxCacheLength=sanitizeInteger(this.options.maxCacheLength,$.fn.autocomplete.defaults.maxCacheLength,{min:1});this.options.delay=sanitizeInteger(this.options.delay,$.fn.autocomplete.defaults.delay,{min:0});if(this.options.preventDefaultReturn!=2){this.options.preventDefaultReturn=this.options.preventDefaultReturn?1:0}if(this.options.preventDefaultTab!=2){this.options.preventDefaultTab=this.options.preventDefaultTab?1:0}this.dom={};this.dom.$elem=$elem;this.dom.$elem.attr("autocomplete","off").addClass(this.options.inputClass);this.dom.$results=$("<div></div>").hide().addClass(this.options.resultsClass).css({position:"absolute"});$("body").append(this.dom.$results);$elem.keydown(function(e){self.lastKeyPressed_=e.keyCode;switch(self.lastKeyPressed_){case self.options.delimiterKeyCode:if(self.options.useDelimiter&&self.active_){self.selectCurrent()}break;case 35:case 36:case 16:case 17:case 18:case 37:case 39:break;case 38:e.preventDefault();if(self.active_){self.focusPrev()}else{self.activate()}return false;case 40:e.preventDefault();if(self.active_){self.focusNext()}else{self.activate()}return false;case 9:if(self.active_){self.selectCurrent();if(self.options.preventDefaultTab){e.preventDefault();return false}}if(self.options.preventDefaultTab===2){e.preventDefault();return false}break;case 13:if(self.active_){self.selectCurrent();if(self.options.preventDefaultReturn){e.preventDefault();return false}}if(self.options.preventDefaultReturn===2){e.preventDefault();return false}break;case 27:if(self.active_){e.preventDefault();self.deactivate(true);return false}break;default:self.activate()}});$elem.on("paste",function(){self.activate()});var onBlurFunction=function(){self.deactivate(true)};$elem.blur(function(){if(self.finishOnBlur_){self.finishTimeout_=setTimeout(onBlurFunction,200)}});$elem.parents("form").on("submit",onBlurFunction)};$.Autocompleter.prototype.position=function(){var offset=this.dom.$elem.offset();var height=this.dom.$results.outerHeight();var totalHeight=$(window).outerHeight();var inputBottom=offset.top+this.dom.$elem.outerHeight();var bottomIfDown=inputBottom+height;var position={top:inputBottom,left:offset.left};if(bottomIfDown>totalHeight){var topIfUp=offset.top-height;if(topIfUp>=0){position.top=topIfUp}}this.dom.$results.css(position)};$.Autocompleter.prototype.cacheRead=function(filter){var filterLength,searchLength,search,maxPos,pos;if(this.options.useCache){filter=String(filter);filterLength=filter.length;if(this.options.matchSubset){searchLength=1}else{searchLength=filterLength}while(searchLength<=filterLength){if(this.options.matchInside){maxPos=filterLength-searchLength}else{maxPos=0}pos=0;while(pos<=maxPos){search=filter.substr(0,searchLength);if(this.cacheData_[search]!==undefined){return this.cacheData_[search]}pos++}searchLength++}}return false};$.Autocompleter.prototype.cacheWrite=function(filter,data){if(this.options.useCache){if(this.cacheLength_>=this.options.maxCacheLength){this.cacheFlush()}filter=String(filter);if(this.cacheData_[filter]!==undefined){this.cacheLength_++}this.cacheData_[filter]=data;return this.cacheData_[filter]}return false};$.Autocompleter.prototype.cacheFlush=function(){this.cacheData_={};this.cacheLength_=0};$.Autocompleter.prototype.callHook=function(hook,data){var f=this.options[hook];if(f&&$.isFunction(f)){return f(data,this)}return false};$.Autocompleter.prototype.activate=function(){if(!this.options.enabled)return;var self=this;if(this.keyTimeout_){clearTimeout(this.keyTimeout_)}this.keyTimeout_=setTimeout(function(){self.activateNow()},this.options.delay)};$.Autocompleter.prototype.activateNow=function(){var value=this.beforeUseConverter(this.dom.$elem.val());if(value!==this.lastProcessedValue_&&value!==this.lastSelectedValue_){this.fetchData(value)}};$.Autocompleter.prototype.fetchData=function(value){var self=this;var processResults=function(results,filter){if(self.options.processData){results=self.options.processData(results)}self.showResults(self.filterResults(results,filter),filter)};this.lastProcessedValue_=value;if(value.length<this.options.minChars){processResults([],value)}else if(this.options.data){processResults(this.options.data,value)}else{this.fetchRemoteData(value,function(remoteData){processResults(remoteData,value)})}};$.Autocompleter.prototype.fetchRemoteData=function(filter,callback){var data=this.cacheRead(filter);if(data){callback(data)}else{var self=this;var dataType=self.options.remoteDataType==="json"?"json":"text";var ajaxCallback=function(data){var parsed=false;if(data!==false){parsed=self.parseRemoteData(data);self.cacheWrite(filter,parsed)}self.dom.$elem.removeClass(self.options.loadingClass);callback(parsed)};this.dom.$elem.addClass(this.options.loadingClass);$.ajax({url:this.makeUrl(filter),success:ajaxCallback,error:function(jqXHR,textStatus,errorThrown){if($.isFunction(self.options.onError)){self.options.onError(jqXHR,textStatus,errorThrown)}else{ajaxCallback(false)}},dataType:dataType})}};$.Autocompleter.prototype.setExtraParam=function(name,value){var index=$.trim(String(name));if(index){if(!this.options.extraParams){this.options.extraParams={}}if(this.options.extraParams[index]!==value){this.options.extraParams[index]=value;this.cacheFlush()}}};$.Autocompleter.prototype.makeUrl=function(param){var self=this;var url=this.options.url;var params=$.extend({},this.options.extraParams);if(this.options.queryParamName===false){url+=encodeURIComponent(param)}else{params[this.options.queryParamName]=param}return makeUrl(url,params)};$.Autocompleter.prototype.parseRemoteData=function(remoteData){var remoteDataType;var data=remoteData;if(this.options.remoteDataType==="json"){remoteDataType=typeof remoteData;switch(remoteDataType){case"object":data=remoteData;break;case"string":data=$.parseJSON(remoteData);break;default:throw new Error("Unexpected remote data type: "+remoteDataType)}return data}return plainTextParser(data,this.options.lineSeparator,this.options.cellSeparator)};$.Autocompleter.prototype.defaultFilter=function(result,filter){if(!result.value){return false}if(this.options.filterResults){var pattern=this.matchStringConverter(filter);var testValue=this.matchStringConverter(result.value);if(!this.options.matchCase){pattern=pattern.toLowerCase();testValue=testValue.toLowerCase()}var patternIndex=testValue.indexOf(pattern);if(this.options.matchInside){return patternIndex>-1}else{return patternIndex===0}}return true};$.Autocompleter.prototype.filterResult=function(result,filter){if(this.options.filter===false){return true}if($.isFunction(this.options.filter)){return this.options.filter(result,filter)}return this.defaultFilter(result,filter)};$.Autocompleter.prototype.filterResults=function(results,filter){var filtered=[];var i,result;for(i=0;i<results.length;i++){result=sanitizeResult(results[i]);if(this.filterResult(result,filter)){filtered.push(result)}}if(this.options.sortResults){filtered=this.sortResults(filtered,filter)}if(this.options.maxItemsToShow>0&&this.options.maxItemsToShow<filtered.length){filtered.length=this.options.maxItemsToShow}return filtered};$.Autocompleter.prototype.sortResults=function(results,filter){var self=this;var sortFunction=this.options.sortFunction;if(!$.isFunction(sortFunction)){sortFunction=function(a,b,f){return sortValueAlpha(a,b,self.options.matchCase)}}results.sort(function(a,b){return sortFunction(a,b,filter,self.options)});return results};$.Autocompleter.prototype.matchStringConverter=function(s,a,b){var converter=this.options.matchStringConverter;if($.isFunction(converter)){s=converter(s,a,b)}return s};$.Autocompleter.prototype.beforeUseConverter=function(s){s=this.getValue(s);var converter=this.options.beforeUseConverter;if($.isFunction(converter)){s=converter(s)}return s};$.Autocompleter.prototype.enableFinishOnBlur=function(){this.finishOnBlur_=true};$.Autocompleter.prototype.disableFinishOnBlur=function(){this.finishOnBlur_=false};$.Autocompleter.prototype.createItemFromResult=function(result){var self=this;var $li=$("<li/>");$li.text(this.showResult(result.value,result.data));$li.data({value:result.value,data:result.data}).click(function(){self.selectItem($li)}).mousedown(self.disableFinishOnBlur).mouseup(self.enableFinishOnBlur);return $li};$.Autocompleter.prototype.getItems=function(){return $(">ul>li",this.dom.$results)};$.Autocompleter.prototype.showResults=function(results,filter){var numResults=results.length;var self=this;var $ul=$("<ul></ul>");var i,result,$li,autoWidth,first=false,$first=false;if(numResults){for(i=0;i<numResults;i++){result=results[i];$li=this.createItemFromResult(result);$ul.append($li);if(first===false){first=String(result.value);$first=$li;$li.addClass(this.options.firstItemClass)}if(i===numResults-1){$li.addClass(this.options.lastItemClass)}}this.dom.$results.html($ul).show();this.position();if(this.options.autoWidth){autoWidth=this.dom.$elem.outerWidth()-this.dom.$results.outerWidth()+this.dom.$results.width();this.dom.$results.css(this.options.autoWidth,autoWidth)}this.getItems().hover(function(){self.focusItem(this)},function(){});if(this.autoFill(first,filter)||this.options.selectFirst||this.options.selectOnly&&numResults===1){this.focusItem($first)}this.active_=true}else{this.hideResults();this.active_=false}};$.Autocompleter.prototype.showResult=function(value,data){if($.isFunction(this.options.showResult)){return this.options.showResult(value,data)}else{return value}};$.Autocompleter.prototype.autoFill=function(value,filter){var lcValue,lcFilter,valueLength,filterLength;if(this.options.autoFill&&this.lastKeyPressed_!==8){lcValue=String(value).toLowerCase();lcFilter=String(filter).toLowerCase();valueLength=value.length;filterLength=filter.length;if(lcValue.substr(0,filterLength)===lcFilter){var d=this.getDelimiterOffsets();var pad=d.start?" ":"";this.setValue(pad+value);var start=filterLength+d.start+pad.length;var end=valueLength+d.start+pad.length;this.selectRange(start,end);return true}}return false};$.Autocompleter.prototype.focusNext=function(){this.focusMove(+1)};$.Autocompleter.prototype.focusPrev=function(){this.focusMove(-1)};$.Autocompleter.prototype.focusMove=function(modifier){var $items=this.getItems();modifier=sanitizeInteger(modifier,0);if(modifier){for(var i=0;i<$items.length;i++){if($($items[i]).hasClass(this.selectClass_)){this.focusItem(i+modifier);return}}}this.focusItem(0)};$.Autocompleter.prototype.focusItem=function(item){var $item,$items=this.getItems();if($items.length){$items.removeClass(this.selectClass_).removeClass(this.options.selectClass);if(typeof item==="number"){if(item<0){item=0}else if(item>=$items.length){item=$items.length-1}$item=$($items[item])}else{$item=$(item)}if($item){$item.addClass(this.selectClass_).addClass(this.options.selectClass)}}};$.Autocompleter.prototype.selectCurrent=function(){var $item=$("li."+this.selectClass_,this.dom.$results);if($item.length===1){this.selectItem($item)}else{this.deactivate(false)}};$.Autocompleter.prototype.selectItem=function($li){var value=$li.data("value");var data=$li.data("data");var displayValue=this.displayValue(value,data);var processedDisplayValue=this.beforeUseConverter(displayValue);this.lastProcessedValue_=processedDisplayValue;this.lastSelectedValue_=processedDisplayValue;var d=this.getDelimiterOffsets();var delimiter=this.options.delimiterChar;var elem=this.dom.$elem;var extraCaretPos=0;if(this.options.useDelimiter){if(elem.val().substring(d.start-1,d.start)==delimiter&&delimiter!=" "){displayValue=" "+displayValue}if(elem.val().substring(d.end,d.end+1)!=delimiter&&this.lastKeyPressed_!=this.options.delimiterKeyCode){displayValue=displayValue+delimiter}else{extraCaretPos=1}}this.setValue(displayValue);this.setCaret(d.start+displayValue.length+extraCaretPos);this.callHook("onItemSelect",{value:value,data:data});this.deactivate(true);elem.focus()};$.Autocompleter.prototype.displayValue=function(value,data){if($.isFunction(this.options.displayValue)){return this.options.displayValue(value,data)}return value};$.Autocompleter.prototype.hideResults=function(){this.dom.$results.hide()};$.Autocompleter.prototype.deactivate=function(finish){if(this.finishTimeout_){clearTimeout(this.finishTimeout_)}if(this.keyTimeout_){clearTimeout(this.keyTimeout_)}if(finish){if(this.lastProcessedValue_!==this.lastSelectedValue_){if(this.options.mustMatch){this.setValue("")}this.callHook("onNoMatch")}if(this.active_){this.callHook("onFinish")}this.lastKeyPressed_=null;this.lastProcessedValue_=null;this.lastSelectedValue_=null;this.active_=false}this.hideResults()};$.Autocompleter.prototype.selectRange=function(start,end){var input=this.dom.$elem.get(0);if(input.setSelectionRange){input.focus();input.setSelectionRange(start,end)}else if(input.createTextRange){var range=input.createTextRange();range.collapse(true);range.moveEnd("character",end);range.moveStart("character",start);range.select()}};$.Autocompleter.prototype.setCaret=function(pos){this.selectRange(pos,pos)};$.Autocompleter.prototype.getCaret=function(){var $elem=this.dom.$elem;var elem=$elem[0];var val,selection,range,start,end,stored_range;if(elem.createTextRange){selection=document.selection;if(elem.tagName.toLowerCase()!="textarea"){val=$elem.val();range=selection.createRange().duplicate();range.moveEnd("character",val.length);if(range.text===""){start=val.length}else{start=val.lastIndexOf(range.text)}range=selection.createRange().duplicate();range.moveStart("character",-val.length);end=range.text.length}else{range=selection.createRange();stored_range=range.duplicate();stored_range.moveToElementText(elem);stored_range.setEndPoint("EndToEnd",range);start=stored_range.text.length-range.text.length;end=start+range.text.length}}else{start=$elem[0].selectionStart;end=$elem[0].selectionEnd}return{start:start,end:end}};$.Autocompleter.prototype.setValue=function(value){if(this.options.useDelimiter){var val=this.dom.$elem.val();var d=this.getDelimiterOffsets();var preVal=val.substring(0,d.start);var postVal=val.substring(d.end);value=preVal+value+postVal}this.dom.$elem.val(value)};$.Autocompleter.prototype.getValue=function(value){if(this.options.useDelimiter){var d=this.getDelimiterOffsets();return value.substring(d.start,d.end).trim()}else{return value}};$.Autocompleter.prototype.getDelimiterOffsets=function(){var val=this.dom.$elem.val();if(this.options.useDelimiter){var preCaretVal=val.substring(0,this.getCaret().start);var start=preCaretVal.lastIndexOf(this.options.delimiterChar)+1;var postCaretVal=val.substring(this.getCaret().start);var end=postCaretVal.indexOf(this.options.delimiterChar);if(end==-1)end=val.length;end+=this.getCaret().start}else{start=0;end=val.length}return{start:start,end:end}}})(jQuery);
\ No newline at end of file
diff --git a/src/main/resources/res/ui/slinglog.js b/src/main/resources/res/ui/slinglog.js
new file mode 100644
index 0000000..47bcae6
--- /dev/null
+++ b/src/main/resources/res/ui/slinglog.js
@@ -0,0 +1,133 @@
+/**
+ * Removes the editor (toggles all displayfields/editables).
+ */
+function removeEditor(row) {
+ $(row).find(".loggers").toggle();
+ $(row).find(".logLevels").toggle();
+ $(row).find(".logFile").toggle();
+ $(row).find(".configureLink").toggle();
+ $(row).find(".editElement").remove();
+ $(row).removeClass("currentEditor");
+}
+
+/**
+ * Turns the loglevel element into an selectfield (current loglevel is selected).
+ */
+function addLogLevelSelect(row) {
+ var logLevelElement = $(row).find(".logLevels");
+ // get the current loglevel
+ var currentLogLevel = logLevelElement.attr("data-currentloglevel");
+ if(!currentLogLevel) {
+ // convenience default for new loggers
+ currentLogLevel = "INFO";
+ }
+ // get all available loglevels (present in the "newlogger" element)
+ var allLogLevels = $("#allLogLevels").attr("data-loglevels").split(",");
+ var select = $('<select class="editElement" name="loglevel"></select>');
+ $.each(allLogLevels, function(index, logLevel) {
+ select.append('<option'+(logLevel == currentLogLevel ? ' selected="selected"' : '')+'>'+logLevel+'</option>');
+ });
+ logLevelElement.after(select);
+ logLevelElement.toggle();
+}
+
+/**
+ * Adds a new editable logger for the given loggerelement (with controls for adding/removing).
+ * @param loggersElement logger element
+ * @param loggerName name of the logger
+ */
+function addLogger(loggersElement, loggerName) {
+ var addButton = $('<input type="submit" name="add" class="ui-state-default ui-corner-all" value="+" style="width:5%;" />');
+ addButton.bind("click", function() {
+ addLogger($(this).parent(), "");
+ return false;
+ });
+ var removeButton = $('<input type="submit" class="ui-state-default ui-corner-all" name="remove" value="-" style="width:5%;" />');
+ removeButton.bind("click", function() {
+ $(this).parent().remove();
+ return false;
+ });
+ var loggerField = $('<input type="text" name="logger" class="loggerField ui-state-default ui-corner-all inputText" value="'+loggerName+'" autocomplete="off" style="width:89%;" />');
+ // add the autocomplete with the array of all loggers
+ loggerField.autocomplete({
+ data: loggers
+ });
+ var logger = $('<div class="editElement"></div>').append(loggerField, addButton, removeButton);
+ loggersElement.after(logger);
+}
+
+/**
+ * Turns the logger elements into inputfields (with controls).
+ */
+function addLoggers(row) {
+ var loggersElement = $(row).find(".loggers");
+ var loggers = loggersElement.find(".logger");
+ if(loggers.length == 0) {
+ addLogger(loggersElement, "");
+ }
+ $.each(loggers, function(index, logger) {
+ addLogger(loggersElement, $(logger).html());
+ });
+ loggersElement.toggle();
+}
+
+/**
+ * Turns the logfile element into an inputfield.
+ */
+function addLogFile(row) {
+ var logFileElement = $(row).find(".logFile");
+ var logFile = "";
+ if(logFileElement.length > 0) {
+ logFile = $(logFileElement).html();
+ }
+ if (logFile.length == 0) {
+ // no logfile -> new logger -> take default
+ logFile = $("#defaultLogfile").attr("data-defaultlogfile");
+ }
+ logFileElement.after('<input style="width:100%" class="editElement ui-state-default ui-corner-all inputText" type="text" name="logfile" value="'+logFile+'" />');
+ logFileElement.toggle();
+}
+
+/**
+ * Activates the logger configurator (called by clicking the configure link).
+ * Turns all display fields in the logger row containing the configure link into edit fields.
+ * @param button configure link
+ */
+function configureLogger(button) {
+ var configureLink = $(button.currentTarget);
+ var row = configureLink.parent().parent();
+ var rowId = $(row).attr("id");
+ // remove the current editor, since we have only one form only one editor can be active the same time
+ removeEditor($(".currentEditor"));
+ // add class as marker (id is already used for pid)
+ row.addClass("currentEditor");
+ // add the editables
+ addLogLevelSelect(row);
+ addLoggers(row);
+ addLogFile(row);
+ // add controls
+ var hiddenField = $('<input class="editElement" type="hidden" name="pid" value="'+(rowId != 'newlogger' ? rowId : '')+'" />');
+ var saveButton = $('<input class="editElement" type="submit" name="save" value="Save" />');
+ var cancelButton=$('<input class="editElement" type="submit" value="Cancel" />');
+ cancelButton.bind("click", function() {
+ var row = $(this).parent().parent();
+ removeEditor(row);
+ return false;
+ });
+ var deleteButton = $('<input class="editElement" type="submit" name="delete" value="Remove Logger" />');
+ configureLink.after(saveButton, cancelButton, hiddenField);
+ if (rowId !== "newlogger") {
+ // add a delete buttons for existing loggers
+ cancelButton.after(deleteButton);
+ }
+ configureLink.toggle();
+ // prevent click on link
+ return false;
+}
+
+/**
+ * Initializes the log panel.
+ */
+function initializeSlingLogPanel() {
+ $("#loggerConfig").find(".configureLink").bind("click", configureLogger);
+}
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/ITAppenderServices.java b/src/test/java/org/apache/sling/commons/log/logback/integration/ITAppenderServices.java
index 4e2cd0c..f4b3f65 100644
--- a/src/test/java/org/apache/sling/commons/log/logback/integration/ITAppenderServices.java
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/ITAppenderServices.java
@@ -28,9 +28,7 @@
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.spi.ClassPackagingData;
import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AppenderBase;
import org.junit.After;
@@ -46,9 +44,7 @@
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.LoggerFactory;
-import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.ops4j.pax.exam.CoreOptions.composite;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
@@ -123,26 +119,6 @@
assertEquals(2, ta.events.size());
}
- @Test
- public void testPackagingData() throws Exception {
- TestAppender ta = registerAppender("foo.bar.packaging");
- delay();
-
- Logger foo = (Logger)LoggerFactory.getLogger("foo.bar.packaging");
- foo.warn("This is a test", new Exception());
-
- // One event should be logged.
- assertEquals(1, ta.events.size());
- ILoggingEvent e = ta.events.get(0);
- StackTraceElementProxy[] stProxies = e.getThrowableProxy().getStackTraceElementProxyArray();
-
- ClassPackagingData cpd1 = stProxies[0].getClassPackagingData();
-
- //For pax exam the bundle is created with name starting with PAXEXAM-PROBE
- //As codeLocation is OSGi env is bundle symbolic name we check for that
- assertThat(cpd1.getCodeLocation(), is(bundleContext.getBundle().getSymbolicName()));
- }
-
@After
public void unregisterAppender(){
sr.unregister();
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/ITConfigAdminSupport.java b/src/test/java/org/apache/sling/commons/log/logback/integration/ITConfigAdminSupport.java
index 4024517..39dd48b 100644
--- a/src/test/java/org/apache/sling/commons/log/logback/integration/ITConfigAdminSupport.java
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/ITConfigAdminSupport.java
@@ -25,6 +25,7 @@
import javax.inject.Inject;
+import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import org.junit.Test;
@@ -77,6 +78,8 @@
public static final String FACTORY_PID_CONFIGS = PID + ".factory.config";
+ public static final String LOG_PACKAGING_DATA = "org.apache.sling.commons.log.packagingDataEnabled";
+
@Inject
private ConfigurationAdmin ca;
@@ -138,6 +141,20 @@
}
@Test
+ public void testPackagingDataConfig() throws Exception {
+ // Set log level to debug for Root logger
+ Configuration config = ca.getConfiguration(PID, null);
+ Dictionary<String, Object> p = new Hashtable<String, Object>();
+ p.put(LOG_PACKAGING_DATA, Boolean.FALSE);
+ p.put(LOG_LEVEL, "INFO");
+ config.update(p);
+
+ delay();
+
+ assertFalse(((LoggerContext)LoggerFactory.getILoggerFactory()).isPackagingDataEnabled());
+ }
+
+ @Test
public void testExternalConfig() throws Exception {
Configuration config = ca.getConfiguration(PID, null);
Dictionary<String, Object> p = new Hashtable<String, Object>();
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/ITWebConsoleRemote.java b/src/test/java/org/apache/sling/commons/log/logback/integration/ITWebConsoleRemote.java
index 831441d..25e76b5 100644
--- a/src/test/java/org/apache/sling/commons/log/logback/integration/ITWebConsoleRemote.java
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/ITWebConsoleRemote.java
@@ -25,7 +25,6 @@
import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
-
import org.apache.commons.io.FilenameUtils;
import org.apache.sling.commons.log.logback.integration.remote.WebConsoleTestActivator;
import org.junit.AfterClass;
@@ -74,6 +73,7 @@
mavenBundle("commons-io", "commons-io").versionAsInProject(),
wrappedBundle(mavenBundle("commons-fileupload", "commons-fileupload").versionAsInProject()),
wrappedBundle(mavenBundle("org.json", "json").versionAsInProject()),
+ configAdmin(),
frameworkProperty("org.apache.sling.commons.log.configurationFile").value(
FilenameUtils.concat(new File(".").getAbsolutePath(), "src/test/resources/test-webconsole-remote.xml")),
createWebConsoleTestBundle()
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/remote/WebConsoleTestActivator.java b/src/test/java/org/apache/sling/commons/log/logback/integration/remote/WebConsoleTestActivator.java
index a185212..c6eb08b 100644
--- a/src/test/java/org/apache/sling/commons/log/logback/integration/remote/WebConsoleTestActivator.java
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/remote/WebConsoleTestActivator.java
@@ -18,6 +18,12 @@
*/
package org.apache.sling.commons.log.logback.integration.remote;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
@@ -33,12 +39,6 @@
import org.slf4j.Marker;
import org.xml.sax.InputSource;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
/**
* Test bundle activator which registers all type of extension point supported by bundle
* Used by ITWebConsoleRemote to assert output of the WebConsole Plugin
@@ -61,8 +61,8 @@
Dictionary<String, Object> props = new Hashtable<String, Object>();
String prefix = "WebConsoleTest";
String[] loggers = {
- prefix + ".foo.bar:DEBUG",
- prefix + ".foo.baz:INFO",
+ prefix + ".foo.bar",
+ prefix + ".foo.baz",
};
props.put("loggers", loggers);
diff --git a/src/test/resources/test-jul-config.xml b/src/test/resources/test-jul-config.xml
index bceeac2..9e3a3f0 100755
--- a/src/test/resources/test-jul-config.xml
+++ b/src/test/resources/test-jul-config.xml
@@ -18,7 +18,9 @@
-->
<configuration>
- <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
+ <newRule pattern="*/configuration/osgi"
+ actionClass="org.apache.sling.commons.log.logback.OsgiAction"/>
+ <osgi/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>