You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by cs...@apache.org on 2016/05/30 15:06:54 UTC

karaf-decanter git commit: [KARAF-4546] initial Spring boot support

Repository: karaf-decanter
Updated Branches:
  refs/heads/master af1a5f8a9 -> 8801a3bce


[KARAF-4546] initial Spring boot support


Project: http://git-wip-us.apache.org/repos/asf/karaf-decanter/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf-decanter/commit/8801a3bc
Tree: http://git-wip-us.apache.org/repos/asf/karaf-decanter/tree/8801a3bc
Diff: http://git-wip-us.apache.org/repos/asf/karaf-decanter/diff/8801a3bc

Branch: refs/heads/master
Commit: 8801a3bcefeb8dac9f07e7ab16de98eecb736720
Parents: af1a5f8
Author: Christian Schneider <ch...@die-schneider.net>
Authored: Mon May 30 17:06:41 2016 +0200
Committer: Christian Schneider <ch...@die-schneider.net>
Committed: Mon May 30 17:06:41 2016 +0200

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 spring-boot-starter-decanter/.gitignore         |   1 +
 spring-boot-starter-decanter/pom.xml            |  62 +++++++++++
 .../karaf/decanter/boot/DecanterAutoConfig.java |  31 ++++++
 .../karaf/decanter/boot/DecanterConnect.java    |  74 +++++++++++++
 .../decanter/boot/DecanterRegistryFactory.java  |  95 ++++++++++++++++
 .../decanter/boot/LogbackDecanterAppender.java  | 110 +++++++++++++++++++
 .../resources/META-INF/decanter.bundles.default |   8 ++
 .../main/resources/META-INF/spring.factories    |   2 +
 .../boot/DecanterRegistryFactoryTest.java       |  41 +++++++
 .../src/test/resources/logback.xml              |  21 ++++
 11 files changed, 446 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 107334e..656b0c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@
         <module>elasticsearch-head-2.x</module>
         <module>kibana-3.x</module>
         <module>kibana-4.x</module>
+        <module>spring-boot-starter-decanter</module>
         <module>assembly</module>
     </modules>
 

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/.gitignore
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/.gitignore b/spring-boot-starter-decanter/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/spring-boot-starter-decanter/.gitignore
@@ -0,0 +1 @@
+/target/

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/pom.xml
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/pom.xml b/spring-boot-starter-decanter/pom.xml
new file mode 100644
index 0000000..f0f9c07
--- /dev/null
+++ b/spring-boot-starter-decanter/pom.xml
@@ -0,0 +1,62 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf</groupId>
+        <artifactId>decanter</artifactId>
+        <version>1.1.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.decanter</groupId>
+    <artifactId>spring-boot-starter-decanter</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.connect</artifactId>
+            <version>0.1.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr</artifactId>
+            <version>2.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.decanter.appender</groupId>
+            <artifactId>
+                org.apache.karaf.decanter.appender.kafka
+            </artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.decanter.marshaller</groupId>
+            <artifactId>
+                org.apache.karaf.decanter.marshaller.json
+            </artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <version>1.4.6</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+            <version>1.8.8</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+            <version>1.3.3.RELEASE</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterAutoConfig.java
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterAutoConfig.java b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterAutoConfig.java
new file mode 100644
index 0000000..ac29f0c
--- /dev/null
+++ b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterAutoConfig.java
@@ -0,0 +1,31 @@
+/**
+ * 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.karaf.decanter.boot;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class DecanterAutoConfig {
+
+    @Bean
+    DecanterConnect createDecanterConnect() throws Exception {
+        return new DecanterConnect();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterConnect.java
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterConnect.java b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterConnect.java
new file mode 100644
index 0000000..dd2dba8
--- /dev/null
+++ b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterConnect.java
@@ -0,0 +1,74 @@
+/**
+ * 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.karaf.decanter.boot;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.annotation.PreDestroy;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+@org.springframework.context.annotation.Configuration
+public class DecanterConnect {
+    private BundleContext registryContext;
+
+    public DecanterConnect() throws Exception {
+        registryContext = new DecanterRegistryFactory().create();
+        Dictionary<String, String> kafka = new Hashtable<>();
+        kafka.put("bootstrap.servers", "kafka:9092");
+        configure(registryContext, "org.apache.karaf.decanter.appender.kafka", kafka);
+        injectEventAdmin(registryContext);
+    }
+
+    private static void configure(BundleContext context, String pid, Dictionary<String, String> properties)
+        throws IOException {
+        ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> tracker = new ServiceTracker<>(context,
+                                                                                              ConfigurationAdmin.class,
+                                                                                              null);
+        tracker.open();
+        ConfigurationAdmin configAdmin = tracker.getService();
+        Configuration config = configAdmin.getConfiguration(pid);
+        config.update(properties);
+        tracker.close();
+    }
+
+    private static void injectEventAdmin(BundleContext context) {
+        ServiceTracker<EventAdmin, EventAdmin> tracker = new ServiceTracker<>(context, EventAdmin.class,
+                                                                              null);
+        tracker.open();
+        EventAdmin eventAdmin = tracker.getService();
+        LogbackDecanterAppender.setDispatcher(eventAdmin);
+        tracker.close();
+    }
+
+    @PreDestroy
+    public void close() {
+        try {
+            registryContext.getBundle(0).stop();
+        } catch (BundleException e) {
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterRegistryFactory.java
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterRegistryFactory.java b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterRegistryFactory.java
new file mode 100644
index 0000000..b0a9561
--- /dev/null
+++ b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/DecanterRegistryFactory.java
@@ -0,0 +1,95 @@
+/**
+ * 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.karaf.decanter.boot;
+
+import static org.apache.felix.connect.launch.PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import org.apache.felix.connect.launch.BundleDescriptor;
+import org.apache.felix.connect.launch.ClasspathScanner;
+import org.apache.felix.connect.launch.PojoServiceRegistryFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+
+public class DecanterRegistryFactory {
+    private static final String BUNDLES_CONFIG = "META-INF/decanter.bundles";
+    private static final String BUNDLES_CONFIG_DEFAULT = "META-INF/decanter.bundles.default";
+
+    public BundleContext create() throws Exception {
+        ServiceLoader<PojoServiceRegistryFactory> loader = ServiceLoader
+            .load(PojoServiceRegistryFactory.class);
+        PojoServiceRegistryFactory srFactory = loader.iterator().next();
+        HashMap<String, Object> pojoSrConfig = new HashMap<>();
+        pojoSrConfig.put(BUNDLE_DESCRIPTORS, getBundles());
+        return srFactory.newPojoServiceRegistry(pojoSrConfig).getBundleContext();
+    }
+
+    List<BundleDescriptor> getBundles() throws URISyntaxException, IOException, Exception {
+        URI bundleURL = getURI(BUNDLES_CONFIG);
+        if (bundleURL == null) {
+            bundleURL = getURI(BUNDLES_CONFIG_DEFAULT);
+        }
+        List<String> bundleNames = Files.readAllLines(Paths.get(bundleURL), Charset.forName("utf-8"));
+        String filter = getBundleFilter(bundleNames);
+        FrameworkUtil.createFilter(filter);
+        List<BundleDescriptor> bundles = new ClasspathScanner().scanForBundles(filter);
+        //printNames(bundles);
+        return bundles;
+    }
+
+    private URI getURI(String path) throws URISyntaxException {
+        ClassLoader loader = this.getClass().getClassLoader();
+        URL url = loader.getResource(path); 
+        return url == null ? null : url.toURI();
+    }
+    
+    
+
+    static String getBundleFilter(List<String> bundles) {
+        StringBuilder joined = new StringBuilder();
+        joined.append("(|(Bundle-SymbolicName=");
+        boolean first = true;
+        for (String bundle : bundles) {
+            if (!first) {
+                joined.append(")(Bundle-SymbolicName=");
+            }
+            first = false;
+            joined.append(bundle);
+        }
+        joined.append("))");
+        return joined.toString();
+    }
+
+    private void printNames(List<BundleDescriptor> bundles) {
+        for (BundleDescriptor desc : bundles) {
+            System.out.println(desc.getHeaders().get(Constants.BUNDLE_SYMBOLICNAME));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/LogbackDecanterAppender.java
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/LogbackDecanterAppender.java b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/LogbackDecanterAppender.java
new file mode 100644
index 0000000..b3f4836
--- /dev/null
+++ b/spring-boot-starter-decanter/src/main/java/org/apache/karaf/decanter/boot/LogbackDecanterAppender.java
@@ -0,0 +1,110 @@
+/**
+ * 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.karaf.decanter.boot;
+
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+/**
+ * Logback appender for usage in non OSGi environments like spring boot.
+ * Forwards all log messages to an EventAdmin instance that needs to be injected.
+ */
+public class LogbackDecanterAppender extends AppenderBase<ILoggingEvent> {
+    private static final String MDC_IN_LOG_APPENDER = "inLogAppender";
+    private final static String[] ignoredCategories = {"org.apache.karaf.decanter", "org.apache.kafka", "org.talend.decanter.connect"};
+    private final static Logger LOGGER = LoggerFactory.getLogger(LogbackDecanterAppender.class);
+
+    private static EventAdmin dispatcher;
+    
+    public static void setDispatcher(EventAdmin dispatcher) {
+        LogbackDecanterAppender.dispatcher = dispatcher;
+    }
+
+    protected void append(ILoggingEvent event) {
+        try {
+            if (MDC.get(MDC_IN_LOG_APPENDER) != null) {
+                // Avoid recursion
+                return;
+            }
+            MDC.put(MDC_IN_LOG_APPENDER, "true");
+            appendInternal(event);
+        } catch (Exception e) {
+            LOGGER.warn("Error while appending event", e);
+        } finally {
+            MDC.remove(MDC_IN_LOG_APPENDER);
+        }
+    }
+
+    private void appendInternal(ILoggingEvent event) throws Exception {
+        if (isIgnored(event.getLoggerName())) {
+            LOGGER.trace("{} logger is ignored by the log collector", event.getLoggerName());
+            return;
+        }
+
+        LOGGER.debug("Publishing log event to the appenders ...");
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("type", "log");
+        String karafName = System.getProperty("karaf.name");
+        if (karafName != null) {
+            data.put("karafName", karafName);
+        }
+
+        data.put("hostAddress", InetAddress.getLocalHost().getHostAddress());
+        data.put("hostName", InetAddress.getLocalHost().getHostName());
+
+        data.put("timestamp", event.getTimeStamp());
+        data.put("loggerName", event.getLoggerName());
+        data.put("threadName", event.getThreadName());
+        data.put("message", event.getMessage());
+        data.put("level", event.getLevel().toString());
+        data.put("MDC", event.getMDCPropertyMap());
+        String loggerName = event.getLoggerName();
+        if (loggerName == null || loggerName.isEmpty()) {
+            loggerName = "default";
+        }
+        String topic = "decanter/collect/log/" + loggerName.replace(".", "/").replace(" ", "_").replace("{", "_").replace("}", "_").replace("$", "_");
+        if (dispatcher != null) {
+            dispatcher.postEvent(new Event(topic, data));
+        }
+    }
+
+    private boolean isIgnored(String loggerName) {
+        if (loggerName == null) {
+            return true;
+        }
+        for (String cat : ignoredCategories) {
+            if (loggerName.startsWith(cat)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/main/resources/META-INF/decanter.bundles.default
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/main/resources/META-INF/decanter.bundles.default b/spring-boot-starter-decanter/src/main/resources/META-INF/decanter.bundles.default
new file mode 100644
index 0000000..473ee88
--- /dev/null
+++ b/spring-boot-starter-decanter/src/main/resources/META-INF/decanter.bundles.default
@@ -0,0 +1,8 @@
+org.apache.felix.connect
+org.apache.felix.scr
+org.apache.felix.eventadmin
+org.apache.felix.configadmin
+org.apache.karaf.decanter.api
+org.apache.karaf.decanter.marshaller.json
+org.apache.karaf.decanter.appender.log
+org.apache.karaf.decanter.appender.kafka

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/main/resources/META-INF/spring.factories
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/main/resources/META-INF/spring.factories b/spring-boot-starter-decanter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..51d03ea
--- /dev/null
+++ b/spring-boot-starter-decanter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apache.karaf.decanter.boot.DecanterAutoConfig
+

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/test/java/org/apache/karaf/decanter/boot/DecanterRegistryFactoryTest.java
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/test/java/org/apache/karaf/decanter/boot/DecanterRegistryFactoryTest.java b/spring-boot-starter-decanter/src/test/java/org/apache/karaf/decanter/boot/DecanterRegistryFactoryTest.java
new file mode 100644
index 0000000..27c4f5b
--- /dev/null
+++ b/spring-boot-starter-decanter/src/test/java/org/apache/karaf/decanter/boot/DecanterRegistryFactoryTest.java
@@ -0,0 +1,41 @@
+/**
+ * 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.karaf.decanter.boot;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+public class DecanterRegistryFactoryTest {
+
+    @Test
+    public void testBundleFilter() throws Exception {
+        String bundleFilter = DecanterRegistryFactory.getBundleFilter(Arrays.asList("test", "test2"));
+        FrameworkUtil.createFilter(bundleFilter);
+    }
+    
+    @Test
+    public void testCreate() throws Exception {
+        DecanterRegistryFactory factory = new DecanterRegistryFactory();
+        BundleContext context = factory.create();
+        context.getBundle().stop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/8801a3bc/spring-boot-starter-decanter/src/test/resources/logback.xml
----------------------------------------------------------------------
diff --git a/spring-boot-starter-decanter/src/test/resources/logback.xml b/spring-boot-starter-decanter/src/test/resources/logback.xml
new file mode 100644
index 0000000..e6740be
--- /dev/null
+++ b/spring-boot-starter-decanter/src/test/resources/logback.xml
@@ -0,0 +1,21 @@
+<configuration>
+
+  <appender name="DECANTER" class="org.apache.karaf.decanter.boot.LogbackDecanterAppender">
+    <encoder>
+      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
+    </encoder>
+  </appender>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <!-- encoders are assigned the type
+         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+    <encoder>
+      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="DEBUG">
+    <appender-ref ref="STDOUT" />
+    <appender-ref ref="DECANTER" />
+  </root>
+</configuration>
\ No newline at end of file