You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ch...@apache.org on 2017/11/20 12:53:38 UTC

[sling-org-apache-sling-commons-log] 01/03: SLING-3049 - Make Logback Stacktrace Packaging data support OSGi aware

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

chetanm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-log.git

commit 7365377ae3fd6beaaeaf63575fd52f61a82012bb
Author: Chetan Mehrotra <ch...@apache.org>
AuthorDate: Sat Oct 28 16:20:16 2017 +0530

    SLING-3049 - Make Logback Stacktrace Packaging data support OSGi aware
---
 pom.xml                                            |  12 ++
 .../commons/log/logback/internal/LogConfig.java    |  13 ++
 .../log/logback/internal/LogConfigManager.java     |   4 +
 .../log/logback/internal/LogbackManager.java       |  33 +++-
 .../log/logback/internal/SlingLogPanel.java        |   4 +-
 .../stacktrace/OSGiAwareExceptionHandling.java     |  61 +++++++
 .../internal/stacktrace/PackageInfoCollector.java  |  60 +++++++
 .../logback/integration/ITConfigAdminSupport.java  |   2 +-
 .../log/logback/integration/ITPackagingData.java   | 191 +++++++++++++++++++++
 .../logback/integration/PackagingDataTestUtil.java |  49 ++++++
 .../integration/bundle/PackageDataActivator.java   |  43 +++++
 .../logback/integration/bundle/TestRunnable.java   |  32 ++++
 .../stacktrace/PackageInfoCollectorTest.java       |  33 ++++
 13 files changed, 531 insertions(+), 6 deletions(-)

diff --git a/pom.xml b/pom.xml
index d9da7b1..0b5fca3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -326,6 +326,18 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>biz.aQute.bnd</groupId>
+      <artifactId>bndlib</artifactId>
+      <version>2.4.0</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.osgi</groupId>
+          <artifactId>org.osgi.core</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
       <groupId>javax.inject</groupId>
       <artifactId>javax.inject</artifactId>
       <version>1</version>
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 909c963..ea13ce9 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
@@ -27,6 +27,8 @@ import java.util.regex.Pattern;
 import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.pattern.PostCompileProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,6 +57,8 @@ public class LogConfig {
 
     private final boolean resetToDefault;
 
+    private PostCompileProcessor<ILoggingEvent> postProcessor;
+
     LogConfig(LogWriterProvider logWriterProvider, final String pattern, Set<String> categories, Level logLevel,
               String logWriterName, final boolean isAdditiv, String configPid, LoggerContext loggerContext, boolean resetToDefault) {
         this.logWriterProvider = logWriterProvider;
@@ -145,6 +149,11 @@ public class LogConfig {
         pl.setPattern(logBackPattern);
         pl.setOutputPatternAsHeader(false);
         pl.setContext(loggerContext);
+
+        if (postProcessor != null) {
+            pl.setPostCompileProcessor(postProcessor);
+        }
+
         pl.start();
         return pl;
     }
@@ -155,6 +164,10 @@ public class LogConfig {
             + logLevel + ", logWriterName='" + logWriterName + '\'' + '}';
     }
 
+    public void setPostProcessor(PostCompileProcessor<ILoggingEvent> postProcessor) {
+        this.postProcessor = postProcessor;
+    }
+
     public interface LogWriterProvider {
         LogWriter getLogWriter(String writerName);
     }
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 61c3295..a001a53 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
@@ -42,6 +42,7 @@ 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.stacktrace.OSGiAwareExceptionHandling;
 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;
@@ -566,6 +567,9 @@ public class LogConfigManager implements LogbackResetListener, LogConfig.LogWrit
             // create or modify existing configuration object
             final LogConfig newConfig = new LogConfig(this, pattern, categories, logLevel, fileName, additiv,
                     pid, loggerContext, resetToDefault);
+            if (isPackagingDataEnabled()) {
+                newConfig.setPostProcessor(new OSGiAwareExceptionHandling(logbackManager.getPackageInfoCollector()));
+            }
             LogConfig oldConfig = configByPid.get(pid);
             if (oldConfig != null) {
                 configByCategory.keySet().removeAll(oldConfig.getCategories());
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 a0e7346..9f357c3 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
@@ -34,6 +34,7 @@ import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sling.commons.log.logback.internal.AppenderTracker.AppenderInfo;
+import org.apache.sling.commons.log.logback.internal.stacktrace.PackageInfoCollector;
 import org.apache.sling.commons.log.logback.internal.util.SlingRollingFileAppender;
 import org.apache.sling.commons.log.logback.internal.util.SlingStatusPrinter;
 import org.apache.sling.commons.log.logback.webconsole.LogPanel;
@@ -44,6 +45,7 @@ import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.weaving.WeavingHook;
 import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.LoggerFactory;
 import org.slf4j.bridge.SLF4JBridgeHandler;
@@ -85,6 +87,8 @@ public class LogbackManager extends LoggerContextAwareBase {
      */
     private static final String SLING_LOG_ROOT = "sling.log.root";
 
+    public static final String PACKAGE_INFO_COLLECTOR_DESC = "Sling Log Package Info Collector";
+
     private final BundleContext bundleContext;
 
     private final String rootDir;
@@ -126,6 +130,8 @@ public class LogbackManager extends LoggerContextAwareBase {
 
     private final boolean bridgeHandlerInstalled;
 
+    private final PackageInfoCollector packageInfoCollector = new PackageInfoCollector();
+
     /**
      * Time at which reset started. Used as the threshold for logging error
      * messages from status printer
@@ -307,6 +313,10 @@ public class LogbackManager extends LoggerContextAwareBase {
         ic.addSubstitutionProperty("sling.home", rootDir);
     }
 
+    public PackageInfoCollector getPackageInfoCollector() {
+        return packageInfoCollector;
+    }
+
     public URL getDefaultConfig() {
         return getClass().getClassLoader().getResource("logback-empty.xml");
     }
@@ -489,8 +499,9 @@ public class LogbackManager extends LoggerContextAwareBase {
             addInfo("OsgiIntegrationListener : context reset detected. Adding LogManager to context map and firing"
                 + " listeners");
 
-            context.setPackagingDataEnabled(logConfigManager.isPackagingDataEnabled());
+            context.setPackagingDataEnabled(false);
             context.setMaxCallerDataDepth(logConfigManager.getMaxCallerDataDepth());
+            registerPackageInfoCollector();
 
             // Attach a console appender to handle logging untill we configure
             // one. This would be removed in RootLoggerListener.reset
@@ -646,7 +657,7 @@ public class LogbackManager extends LoggerContextAwareBase {
 
     public LoggerStateContext determineLoggerState() {
         final List<Logger> loggers = getLoggerContext().getLoggerList();
-        final LoggerStateContext ctx = new LoggerStateContext(loggers);
+        final LoggerStateContext ctx = new LoggerStateContext(loggers, packageInfoCollector);
 
         //Distinguish between Logger configured via
         //1. OSGi Config - The ones configured via ConfigAdmin
@@ -708,8 +719,11 @@ public class LogbackManager extends LoggerContextAwareBase {
 
         final Map<ServiceReference,TurboFilter> turboFilters;
 
-        LoggerStateContext(List<Logger> allLoggers) {
+        final PackageInfoCollector packageInfoCollector;
+
+        LoggerStateContext(List<Logger> allLoggers, PackageInfoCollector packageInfoCollector) {
             this.allLoggers = allLoggers;
+            this.packageInfoCollector = packageInfoCollector;
             for (AppenderTracker.AppenderInfo ai : getAppenderTracker().getAppenderInfos()) {
                 dynamicAppenders.put(ai.appender, ai);
             }
@@ -796,4 +810,17 @@ public class LogbackManager extends LoggerContextAwareBase {
         }, props));
     }
 
+    private void registerPackageInfoCollector() {
+        //Weaving hook once registered would not be removed upon config changed
+        if (logConfigManager.isPackagingDataEnabled()) {
+            Properties props = new Properties();
+            props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+            props.put(Constants.SERVICE_DESCRIPTION, PACKAGE_INFO_COLLECTOR_DESC);
+
+            registrations.add(bundleContext.registerService(WeavingHook.class.getName(),
+                    packageInfoCollector, props));
+        }
+    }
+
+
 }
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 0d6975d..84a2728 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
@@ -176,8 +176,8 @@ public class SlingLogPanel implements LogPanel {
 
     private void appendLoggerStatus(PrintWriter pw, LoggerStateContext ctx) {
         pw.printf(
-                "<p class='statline'>Log Service Stats: %d categories, %d appender, %d Dynamic appenders</p>%n",
-                ctx.getNumberOfLoggers(), ctx.getNumOfAppenders(), ctx.getNumOfDynamicAppenders());
+                "<p class='statline'>Log Service Stats: %d categories, %d appender, %d Dynamic appenders, %d Packages</p>%n",
+                ctx.getNumberOfLoggers(), ctx.getNumOfAppenders(), ctx.getNumOfDynamicAppenders(), ctx.packageInfoCollector.size());
     }
 
     private void appendOsgiConfiguredLoggerData(final PrintWriter pw, final String consoleAppRoot) {
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/stacktrace/OSGiAwareExceptionHandling.java b/src/main/java/org/apache/sling/commons/log/logback/internal/stacktrace/OSGiAwareExceptionHandling.java
new file mode 100644
index 0000000..45c8aeb
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/stacktrace/OSGiAwareExceptionHandling.java
@@ -0,0 +1,61 @@
+/*
+ * 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.stacktrace;
+
+import ch.qos.logback.classic.pattern.EnsureExceptionHandling;
+import ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverter;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.StackTraceElementProxy;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.pattern.Converter;
+import ch.qos.logback.core.pattern.ConverterUtil;
+
+public class OSGiAwareExceptionHandling extends EnsureExceptionHandling {
+    private final PackageInfoCollector collector;
+
+    public OSGiAwareExceptionHandling(PackageInfoCollector collector) {
+        this.collector = collector;
+    }
+
+    @Override
+    public void process(Context context, Converter<ILoggingEvent> head) {
+        if (head == null) {
+            // this should never happen
+            throw new IllegalArgumentException("cannot process empty chain");
+        }
+        if (!chainHandlesThrowable(head)) {
+            Converter<ILoggingEvent> tail = ConverterUtil.findTail(head);
+            Converter<ILoggingEvent> exConverter = new OSGiAwareConverter();
+            tail.setNext(exConverter);
+        }
+    }
+
+    private class OSGiAwareConverter extends ExtendedThrowableProxyConverter {
+        @Override
+        protected void extraData(StringBuilder builder, StackTraceElementProxy step) {
+            if (step != null) {
+                String version = collector.getVersion(step.getStackTraceElement().getClassName());
+                if (version != null) {
+                    builder.append(" [").append(version).append(']');
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/log/logback/internal/stacktrace/PackageInfoCollector.java b/src/main/java/org/apache/sling/commons/log/logback/internal/stacktrace/PackageInfoCollector.java
new file mode 100644
index 0000000..36cde17
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/log/logback/internal/stacktrace/PackageInfoCollector.java
@@ -0,0 +1,60 @@
+/*
+ * 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.stacktrace;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.hooks.weaving.WovenClass;
+
+public class PackageInfoCollector implements WeavingHook{
+    private final Map<String, String> pkgVersionMapping = new ConcurrentHashMap<>();
+
+    @Override
+    public void weave(WovenClass wovenClass) {
+        add(wovenClass.getBundleWiring().getBundle(), wovenClass.getClassName());
+    }
+
+    public int size() {
+        return pkgVersionMapping.size();
+    }
+
+    void add(Bundle bundle, String className) {
+        pkgVersionMapping.put(getPackageName(className), bundle.getVersion().toString());
+    }
+
+    String getVersion(String className) {
+        if (className == null) {
+            return null;
+        }
+        String packageName = getPackageName(className);
+        return pkgVersionMapping.get(packageName);
+    }
+
+    static String getPackageName(String className) {
+        int lastIndexOfDot = className.lastIndexOf('.');
+        String result = "";
+        if (lastIndexOfDot > 0){
+            result = className.substring(0, lastIndexOfDot);
+        }
+        return result;
+    }
+}
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 b4c1dc6..c039fec 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
@@ -172,7 +172,7 @@ public class ITConfigAdminSupport extends LogTestBase {
         // 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_PACKAGING_DATA, Boolean.TRUE);
         p.put(LOG_LEVEL, "INFO");
         config.update(p);
 
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/ITPackagingData.java b/src/test/java/org/apache/sling/commons/log/logback/integration/ITPackagingData.java
new file mode 100644
index 0000000..adc44ca
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/ITPackagingData.java
@@ -0,0 +1,191 @@
+/*
+ * 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.integration;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import ch.qos.logback.classic.LoggerContext;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.sling.commons.log.logback.integration.bundle.PackageDataActivator;
+import org.apache.sling.commons.log.logback.internal.LogbackManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerMethod;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.sling.commons.log.logback.integration.ITConfigAdminSupport.PID;
+import static org.apache.sling.commons.log.logback.internal.LogConfigManager.FACTORY_PID_CONFIGS;
+import static org.apache.sling.commons.log.logback.internal.LogConfigManager.LOGBACK_FILE;
+import static org.apache.sling.commons.log.logback.internal.LogConfigManager.LOG_FILE;
+import static org.apache.sling.commons.log.logback.internal.LogConfigManager.LOG_LEVEL;
+import static org.apache.sling.commons.log.logback.internal.LogConfigManager.LOG_LOGGERS;
+import static org.apache.sling.commons.log.logback.internal.LogConfigManager.LOG_PACKAGING_DATA;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerMethod.class)
+public class ITPackagingData extends LogTestBase {
+
+    @Inject
+    private BundleContext bundleContext;
+
+    @Inject
+    private ConfigurationAdmin ca;
+
+    @Override
+    protected Option addExtraOptions() {
+        return composite(
+                configAdmin(),
+                mavenBundle("org.ops4j.pax.tinybundles", "tinybundles").versionAsInProject(),
+                mavenBundle("biz.aQute.bnd", "bndlib").versionAsInProject(),
+                mavenBundle("commons-io", "commons-io").versionAsInProject()
+        );
+    }
+
+    @Test
+    public void defaultWorking() throws Exception{
+        ServiceTracker<WeavingHook, WeavingHook> tracker = createWeavingHookTracker();
+        tracker.open();
+
+        assertNull(tracker.getService());
+        tracker.close();
+    }
+
+    @Test
+    public void packagingEnabled() 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.TRUE);
+        p.put(LOG_LEVEL, "INFO");
+        config.update(p);
+
+        delay();
+
+        ServiceTracker<WeavingHook, WeavingHook> tracker = createWeavingHookTracker();
+        tracker.open();
+
+        assertNotNull(tracker.getService());
+        tracker.close();
+    }
+
+    @Test
+    public void packageDataWorking() throws Exception{
+        // Enable packaging
+        Configuration config = ca.getConfiguration(PID, null);
+        Dictionary<String, Object> p = new Hashtable<String, Object>();
+        p.put(LOG_PACKAGING_DATA, Boolean.TRUE);
+        p.put(LOG_LEVEL, "INFO");
+        config.update(p);
+        delay();
+
+        //Configure a file logger for test class
+        Configuration config2 = ca.createFactoryConfiguration(FACTORY_PID_CONFIGS, null);
+        Dictionary<String, Object> p2 = new Hashtable<String, Object>();
+        p2.put(LOG_LOGGERS, new String[] {
+                PackageDataActivator.LOGGER_NAME
+        });
+        String logFileName = "logs/package-test.log";
+        p2.put(LOG_FILE, logFileName);
+        p2.put(LOG_LEVEL, "INFO");
+        config2.update(p2);
+        delay();
+
+
+        //Ensure that weaving hook comes up
+        ServiceTracker<WeavingHook, WeavingHook> tracker = createWeavingHookTracker();
+        tracker.open();
+        tracker.waitForService(60*1000);
+
+        //Now install the test bundle such that hook picks it up
+        InputStream inp = PackagingDataTestUtil.createTestBundle();
+        Bundle b = bundleContext.installBundle("test", inp);
+        b.start();
+
+        //Now wait for runnable registered by the test bundle
+        ServiceTracker<Runnable, Runnable> runableTracker = createTracker(Runnable.class, PackageDataActivator.LOGGER_NAME);
+        runableTracker.open();
+
+        //Now invoke the method to trigger logger call with exception
+        Runnable r = runableTracker.waitForService(60*1000);
+        r.run();
+
+        //Now read the log file content to assert that stacktrace has version present
+        String slingHome = System.getProperty("sling.home");
+        File logFile = new File(FilenameUtils.concat(slingHome, logFileName));
+        String logFileContent = FileUtils.readFileToString(logFile);
+
+        System.out.println("--------------------");
+        System.out.println(logFileContent);
+        System.out.println("--------------------");
+
+        List<String> lines = FileUtils.readLines(logFile);
+        String testLine = null;
+        for (String l : lines) {
+            if (l.contains("org.apache.sling.commons.log.logback.integration.bundle.TestRunnable.run(")){
+                testLine = l;
+                break;
+            }
+        }
+        assertNotNull(testLine);
+        assertThat(testLine, containsString("["+PackagingDataTestUtil.TEST_BUNDLE_VERSION+"]"));
+
+        //Check that default logback support is still disabled
+        assertFalse(((LoggerContext) LoggerFactory.getILoggerFactory()).isPackagingDataEnabled());
+    }
+
+    private ServiceTracker<WeavingHook, WeavingHook> createWeavingHookTracker() throws InvalidSyntaxException {
+        return createTracker(WeavingHook.class, LogbackManager.PACKAGE_INFO_COLLECTOR_DESC);
+    }
+
+    private <T> ServiceTracker<T,T> createTracker(Class<T> clazz, String desc) throws InvalidSyntaxException {
+        String filter = String.format("(&(%s=%s)(%s=%s))", Constants.OBJECTCLASS, clazz.getName(),
+                Constants.SERVICE_DESCRIPTION, desc);
+        Filter f = FrameworkUtil.createFilter(filter);
+        return new ServiceTracker<>(bundleContext, f, null);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/PackagingDataTestUtil.java b/src/test/java/org/apache/sling/commons/log/logback/integration/PackagingDataTestUtil.java
new file mode 100644
index 0000000..db5c5ed
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/PackagingDataTestUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.commons.log.logback.integration;
+
+import java.io.InputStream;
+
+import org.apache.sling.commons.log.logback.integration.bundle.PackageDataActivator;
+import org.apache.sling.commons.log.logback.integration.bundle.TestRunnable;
+import org.ops4j.pax.tinybundles.core.InnerClassStrategy;
+import org.osgi.framework.Constants;
+
+import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+/**
+ * Have to use a separate class due to TinyBundles having issue and recommends put bundle
+ * creation logic in separate class
+ */
+public class PackagingDataTestUtil {
+
+    public static final String TEST_BUNDLE_VERSION = "0.0.7";
+
+    public static InputStream createTestBundle() {
+        return bundle()
+                .set(Constants.BUNDLE_ACTIVATOR, PackageDataActivator.class.getName())
+                .set(Constants.BUNDLE_SYMBOLICNAME, "packagedatatest")
+                .set(Constants.BUNDLE_VERSION, TEST_BUNDLE_VERSION)
+                .add(PackageDataActivator.class, InnerClassStrategy.NONE)
+                .add(TestRunnable.class, InnerClassStrategy.NONE)
+                .build(withBnd());
+    }
+}
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/bundle/PackageDataActivator.java b/src/test/java/org/apache/sling/commons/log/logback/integration/bundle/PackageDataActivator.java
new file mode 100644
index 0000000..18155a2
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/bundle/PackageDataActivator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.integration.bundle;
+
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+public class PackageDataActivator implements BundleActivator {
+    public static final String LOGGER_NAME = "test.PackageDataActivator";
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_DESCRIPTION, LOGGER_NAME);
+        context.registerService(Runnable.class.getName(), new TestRunnable(), props);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/commons/log/logback/integration/bundle/TestRunnable.java b/src/test/java/org/apache/sling/commons/log/logback/integration/bundle/TestRunnable.java
new file mode 100644
index 0000000..ede6f69
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/log/logback/integration/bundle/TestRunnable.java
@@ -0,0 +1,32 @@
+/*
+ * 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.integration.bundle;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TestRunnable implements Runnable {
+    private final Logger log = LoggerFactory.getLogger(PackageDataActivator.LOGGER_NAME);
+
+    @Override
+    public void run() {
+        log.warn("==PackageDataActivator==", new Exception());
+    }
+}
diff --git a/src/test/java/org/apache/sling/commons/log/logback/internal/stacktrace/PackageInfoCollectorTest.java b/src/test/java/org/apache/sling/commons/log/logback/internal/stacktrace/PackageInfoCollectorTest.java
new file mode 100644
index 0000000..f3ec3f5
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/log/logback/internal/stacktrace/PackageInfoCollectorTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.stacktrace;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class PackageInfoCollectorTest {
+    @Test
+    public void packageName() throws Exception{
+        assertEquals("com.foo", PackageInfoCollector.getPackageName("com.foo.TestClass"));
+        assertEquals("", PackageInfoCollector.getPackageName("packageInDefaultClass"));
+    }
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.