You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2022/02/03 21:31:35 UTC

[sling-whiteboard] branch master updated (edf8b20 -> 959b52a)

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

dklco pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git.


    from edf8b20  Updating site documentation, fixing javadoc and fixing config file name
     new 3fcab98  SLING-11104 - POC of OSGi configurable JSON logging implementation based on Logstash, Felix Logback but still supporting OSGi configurations for log levels
     new 959b52a  Adding to whiteboard reactor

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org.apache.sling.commons.log.json/pom.xml      | 144 +++++++++++++
 .../log/json/impl/LogFileSourceJsonProvider.java   |  64 ++++++
 .../sling/commons/log/json/impl/LogManager.java    |  60 ++++++
 .../commons/log/json/impl/LoggerConfiguration.java |  77 +++++++
 .../sling/commons/log/json/impl/LoggerPrinter.java |  97 +++++++++
 .../commons/log/json/impl/OsgiConfigurator.java    | 239 +++++++++++++++++++++
 .../resources/OSGI-INF/l10n/metatype.properties    |  71 ++++++
 .../log/json/impl/LoggerConfigurationTest.java     |  69 ++++++
 .../commons/log/json/impl/LoggerPrinterTest.java   |  73 +++++++
 .../log/json/impl/OsgiConfiguratorTest.java        |  92 ++++++++
 .../sling/commons/log/json/impl/TestHelper.java    | 148 +++++++++++++
 .../src/test/resources/logback.xml                 |  11 +-
 .../bnd.bnd                                        |   2 +
 .../pom.xml                                        |  98 +++++++++
 .../commons/log/compat/StartupConfigurator.java    |  65 ++++++
 .../ch.qos.logback.classic.spi.Configurator        |   1 +
 pom.xml                                            |   2 +
 17 files changed, 1312 insertions(+), 1 deletion(-)
 create mode 100644 json-logs/org.apache.sling.commons.log.json/pom.xml
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogFileSourceJsonProvider.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogManager.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerConfiguration.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerPrinter.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/OsgiConfigurator.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/main/resources/OSGI-INF/l10n/metatype.properties
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerConfigurationTest.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerPrinterTest.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/OsgiConfiguratorTest.java
 create mode 100644 json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/TestHelper.java
 copy org.apache.sling.ddr/sample.after/src/main/resources/SLING-CONTENT/apps/ddr-static2/button/button.html => json-logs/org.apache.sling.commons.log.json/src/test/resources/logback.xml (69%)
 create mode 100644 json-logs/org.apache.sling.commons.logback.configurator/bnd.bnd
 create mode 100644 json-logs/org.apache.sling.commons.logback.configurator/pom.xml
 create mode 100644 json-logs/org.apache.sling.commons.logback.configurator/src/main/java/org/apache/sling/commons/log/compat/StartupConfigurator.java
 create mode 100644 json-logs/org.apache.sling.commons.logback.configurator/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator

[sling-whiteboard] 02/02: Adding to whiteboard reactor

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 959b52ae7db8a2ea5a10bf78cb773c8ef85d8786
Author: Dan Klco <kl...@adobe.com>
AuthorDate: Thu Feb 3 16:31:23 2022 -0500

    Adding to whiteboard reactor
---
 pom.xml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/pom.xml b/pom.xml
index b85f5ed..0359b47 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,6 +51,8 @@
           <module>instance-stopper</module>
           <module>org.apache.sling.ddr</module>
           <module>sling-repoinit-maven-plugin</module>
+          <module>json-logs/org.apache.sling.commons.logback.configurator</module>
+          <module>json-logs/org.apache.sling.commons.log.json</module>
   </modules>
 
 </project>

[sling-whiteboard] 01/02: SLING-11104 - POC of OSGi configurable JSON logging implementation based on Logstash, Felix Logback but still supporting OSGi configurations for log levels

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3fcab98f829919b4854b12420e2ebfd2b629787b
Author: Dan Klco <kl...@adobe.com>
AuthorDate: Thu Feb 3 16:28:02 2022 -0500

    SLING-11104 - POC of OSGi configurable JSON logging implementation based on Logstash, Felix Logback but still supporting OSGi configurations for log levels
---
 .../org.apache.sling.commons.log.json/pom.xml      | 144 +++++++++++++
 .../log/json/impl/LogFileSourceJsonProvider.java   |  64 ++++++
 .../sling/commons/log/json/impl/LogManager.java    |  60 ++++++
 .../commons/log/json/impl/LoggerConfiguration.java |  77 +++++++
 .../sling/commons/log/json/impl/LoggerPrinter.java |  97 +++++++++
 .../commons/log/json/impl/OsgiConfigurator.java    | 239 +++++++++++++++++++++
 .../resources/OSGI-INF/l10n/metatype.properties    |  71 ++++++
 .../log/json/impl/LoggerConfigurationTest.java     |  69 ++++++
 .../commons/log/json/impl/LoggerPrinterTest.java   |  73 +++++++
 .../log/json/impl/OsgiConfiguratorTest.java        |  92 ++++++++
 .../sling/commons/log/json/impl/TestHelper.java    | 148 +++++++++++++
 .../src/test/resources/logback.xml                 |  28 +++
 .../bnd.bnd                                        |   2 +
 .../pom.xml                                        |  98 +++++++++
 .../commons/log/compat/StartupConfigurator.java    |  65 ++++++
 .../ch.qos.logback.classic.spi.Configurator        |   1 +
 16 files changed, 1328 insertions(+)

diff --git a/json-logs/org.apache.sling.commons.log.json/pom.xml b/json-logs/org.apache.sling.commons.log.json/pom.xml
new file mode 100644
index 0000000..4beeac1
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/pom.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>46</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.commons.log.json</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>Apache Sling - Commons Log JSON</name>
+    <description>Supports generating JSON logs to the console and reading Sling Commons Logging configurations for configurable logger levels</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.build.outputTimestamp>10</project.build.outputTimestamp>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-baseline-maven-plugin</artifactId>
+                <configuration>
+                    <failOnMissing>false</failOnMissing>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.10</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.logstash.logback</groupId>
+            <artifactId>logstash-logback-encoder</artifactId>
+            <version>7.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>23.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+            <version>7.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
+            <version>7.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.annotation</artifactId>
+            <version>8.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <version>1.4.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <version>1.4.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.cm</artifactId>
+            <version>1.6.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.10.19</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId>
+            <version>3.2.2</version>
+            <scope>test</scope>
+        </dependency>
+
+
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogFileSourceJsonProvider.java b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogFileSourceJsonProvider.java
new file mode 100644
index 0000000..2598a55
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogFileSourceJsonProvider.java
@@ -0,0 +1,64 @@
+/*
+ * 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.json.impl;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import net.logstash.logback.composite.AbstractJsonProvider;
+
+/**
+ * Appends the property org.apache.sling.commons.log.file to loggers based on
+ * the custom logger mappings.
+ */
+public class LogFileSourceJsonProvider extends AbstractJsonProvider<ILoggingEvent> {
+
+    private Map<String, String> loggersToFiles = Collections.emptyMap();
+
+    public void setAttachedLoggers(Map<LoggerConfiguration, List<Logger>> attachedLoggers) {
+        addInfo("Updating attached providers");
+        Map<String, String> newMapping = new HashMap<>();
+        attachedLoggers.entrySet().forEach(al -> al.getValue().stream().map(Logger::getName)
+                .forEach(l -> newMapping.put(l, al.getKey().getConfig().org_apache_sling_commons_log_file())));
+        loggersToFiles = Collections.unmodifiableMap(newMapping);
+        addInfo("Attached providers updated");
+    }
+
+    @Override
+    public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {
+        if (isStarted()) {
+            Optional<String> logFile = loggersToFiles.entrySet().stream()
+                    .filter(e -> event.getLoggerName().startsWith(e.getKey())).findAny()
+                    .map(Entry::getValue);
+            if (logFile.isPresent()) {
+                generator.writeStringField("org.apache.sling.commons.log.file", logFile.get());
+            }
+        }
+    }
+
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogManager.java b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogManager.java
new file mode 100644
index 0000000..0a44899
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LogManager.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.json.impl;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.Option;
+
+@Component(immediate = true, service = LogManager.class, name = "org.apache.sling.commons.log.LogManager")
+@Designate(ocd = OsgiConfigurator.Config.class)
+public class LogManager {
+
+    private final Config config;
+
+    @Activate
+    public LogManager(Config config) {
+        this.config = config;
+    }
+
+    public Config getConfig() {
+        return config;
+    }
+
+    @ObjectClassDefinition(name = "%log.name", description = "%log.description", localization = "OSGI-INF/l10n/metatype")
+    public @interface Config {
+        @AttributeDefinition(name = "%log.level.name", description = "%log.level.description", options = {
+                @Option(value = "off", label = "Off"),
+                @Option(value = "trace", label = "Trace"),
+                @Option(value = "debug", label = "Debug"),
+                @Option(value = "info", label = "Information"),
+                @Option(value = "warn", label = "Warnings"),
+                @Option(value = "error", label = "Error") })
+        String org_apache_sling_commons_log_level() default "info";
+
+        @AttributeDefinition(name = "%log.config.maxCallerDataDepth.name", description = "%log.config.maxCallerDataDepth.description")
+        int org_apache_sling_commons_log_maxCallerDataDepth() default 7;
+
+        @AttributeDefinition(name = "%log.config.packagingData.name", description = "%log.config.packagingData.description")
+        boolean org_apache_sling_commons_log_packagingDataEnabled() default true;
+    }
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerConfiguration.java b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerConfiguration.java
new file mode 100644
index 0000000..a3b9fae
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerConfiguration.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.commons.log.json.impl;
+
+import java.util.Optional;
+
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.Option;
+
+/**
+ * OSGi Service for loading logger-specific configurations
+ */
+@Component(name = "org.apache.sling.commons.log.LogManager.factory.config", service = LoggerConfiguration.class)
+@Designate(ocd = LoggerConfiguration.Config.class, factory = true)
+public class LoggerConfiguration {
+
+    private final Config config;
+    private final String pid;
+
+    @Activate
+    public LoggerConfiguration(Config config, ComponentContext componentContext) {
+        this.config = config;
+        pid = Optional.ofNullable(componentContext.getProperties().get(Constants.SERVICE_PID)).map(String.class::cast)
+                .orElse(getClass().getName() + "@" + System.identityHashCode(this));
+    }
+
+    public Config getConfig() {
+        return config;
+    }
+
+    public String getPid() {
+        return pid;
+    }
+
+    @ObjectClassDefinition(name = "%log.factory.config.name", description = "%log.factory.config.description", localization = "OSGI-INF/l10n/metatype")
+    public @interface Config {
+
+        @AttributeDefinition(name = "%log.file.name", description = "%log.file.description")
+        public String org_apache_sling_commons_log_file() default "logs/errorlog";
+
+        @AttributeDefinition(name = "%log.level.name", description = "%log.level.description", options = {
+                @Option(value = "off", label = "Off"),
+                @Option(value = "trace", label = "Trace"),
+                @Option(value = "debug", label = "Debug"),
+                @Option(value = "info", label = "Information"),
+                @Option(value = "warn", label = "Warnings"),
+                @Option(value = "error", label = "Error") })
+        public String org_apache_sling_commons_log_level() default "info";
+
+        @AttributeDefinition(name = "%log.loggers.name", description = "%log.loggers.description")
+        public String[] org_apache_sling_commons_log_names();
+
+    }
+
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerPrinter.java b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerPrinter.java
new file mode 100644
index 0000000..eb71851
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/LoggerPrinter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.json.impl;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.core.BasicStatusManager;
+import ch.qos.logback.core.status.Status;
+import ch.qos.logback.core.util.StatusPrinter;
+
+/**
+ * Console plugin for printing the logback status and registered custom loggers
+ */
+@Component(property = {
+        "felix.webconsole.label=log_json",
+        "felix.webconsole.title=" + LoggerPrinter.HEADLINE,
+        "felix.webconsole.configprinter.modes=always",
+
+}, service = LoggerPrinter.class)
+public class LoggerPrinter extends BasicStatusManager {
+
+    private final LoggerContext loggerContext;
+    private final OsgiConfigurator configurator;
+
+    static final String HEADLINE = "Sling Commons Log - JSON";
+
+    @Activate
+    public LoggerPrinter(@Reference OsgiConfigurator configurator) {
+        loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        loggerContext.getStatusManager().getCopyOfStatusList().forEach(this::add);
+        loggerContext.setStatusManager(this);
+        this.configurator = configurator;
+    }
+
+    private void renderHeader(PrintWriter pw, String header) {
+        pw.println("\n\n" + header + "\n-------------------\n");
+    }
+
+    private String statusToString(Status status) {
+        StringBuilder sb = new StringBuilder();
+        StatusPrinter.buildStr(sb, "", status);
+        return sb.toString();
+    }
+
+    /**
+     * Print out the logging status
+     * 
+     * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
+     */
+    public void printConfiguration(PrintWriter pw) {
+
+        pw.println(HEADLINE + "\n===========================");
+
+        renderHeader(pw, "Status");
+        this.getCopyOfStatusList().stream().map(this::statusToString).forEach(pw::println);
+
+        renderHeader(pw, "Attached Loggers");
+        configurator.getAttachedLoggers().entrySet().forEach(e -> {
+            renderHeader(pw, e.getKey().getPid());
+            pw.println("Log File: " + e.getKey().getConfig().org_apache_sling_commons_log_file());
+            pw.println("Log Log Level: " + e.getKey().getConfig().org_apache_sling_commons_log_level());
+            pw.println("Defined Logger Names: "
+                    + Arrays.stream(e.getKey().getConfig().org_apache_sling_commons_log_names())
+                            .collect(Collectors.joining(",")));
+            pw.println(
+                    "Attached Logger Names: "
+                            + e.getValue().stream().map(Logger::getName).collect(Collectors.joining(",")));
+        });
+
+    }
+
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/OsgiConfigurator.java b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/OsgiConfigurator.java
new file mode 100644
index 0000000..45a6268
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/main/java/org/apache/sling/commons/log/json/impl/OsgiConfigurator.java
@@ -0,0 +1,239 @@
+/*
+ * 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.json.impl;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
+import ch.qos.logback.core.OutputStreamAppender;
+import ch.qos.logback.core.encoder.Encoder;
+import ch.qos.logback.core.spi.ContextAwareBase;
+import net.logstash.logback.encoder.LogstashEncoder;
+
+/**
+ * OSGi service for loading the root Sling Commons Log configuration and
+ * registering the custom logger configurations.
+ * 
+ * Note this requires the root logger of the Logaback context to have an
+ * appender of type {@link ConsoleAppender} with the name "console" which has
+ * it's encoder set as an {@link LogstashEncoder}
+ */
+@Component(immediate = true, service = OsgiConfigurator.class)
+@Designate(ocd = OsgiConfigurator.Config.class)
+public class OsgiConfigurator extends ContextAwareBase {
+
+    private final List<LoggerConfiguration> queuedConfigurations = new CopyOnWriteArrayList<>();
+    private final Set<String> loggerNames = Collections
+            .newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+    private final Map<LoggerConfiguration, List<Logger>> attachedLoggers = new ConcurrentHashMap<>();
+
+    private Config config;
+    private LoggerContext loggerContext;
+    private OutputStreamAppender<ILoggingEvent> appender;
+    private Instant start;
+    private ScheduledFuture<?> future;
+    private boolean initialized = false;
+    private LogManager logManager;
+
+    private LogFileSourceJsonProvider logFileSourceProvider = new LogFileSourceJsonProvider();
+
+    @Activate
+    public void activate(Config config) {
+        this.config = config;
+        start = Instant.now();
+        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+        future = scheduler.scheduleWithFixedDelay(this::initialize, 1, 1, TimeUnit.MILLISECONDS);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        if (loggerContext != null) {
+            loggerContext.stop();
+        }
+    }
+
+    private void initialize() {
+        if (isSlf4jInitialized()) {
+            future.cancel(false);
+
+            loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+            this.setContext(loggerContext);
+
+            addInfo("OsgiConfigurator started at " + start.toEpochMilli());
+            addInfo("Initialization started after " + (Instant.now().toEpochMilli() - start.toEpochMilli()) + "ms");
+
+            addInfo("Initializing OSGi Configuration");
+            Logger rootLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
+            applyLogManagerConfiguration(rootLogger);
+
+            Appender<ILoggingEvent> foundAppender = rootLogger.getAppender(config.appenderName());
+            if (!(foundAppender instanceof OutputStreamAppender<?>)) {
+                addError("Did not find expected appender: " + config.appenderName());
+                return;
+            }
+            this.appender = (OutputStreamAppender<ILoggingEvent>) foundAppender;
+
+            Encoder<?> foundEncoder = appender.getEncoder();
+            if (!(foundEncoder instanceof LogstashEncoder)) {
+                addWarn("Did not find expected logstash encoder");
+            } else {
+                LogstashEncoder encoder = (LogstashEncoder) foundEncoder;
+                logFileSourceProvider.setContext(context);
+                logFileSourceProvider.start();
+                encoder.addProvider(logFileSourceProvider);
+            }
+            addInfo("OSGi initialization complete");
+            initialized = true;
+
+            addInfo("Attaching " + queuedConfigurations.size() + " queued configurations");
+            queuedConfigurations.forEach(this::attachLogger);
+            queuedConfigurations.clear();
+        }
+    }
+
+    private void applyLogManagerConfiguration(Logger rootLogger) {
+        addInfo("Applying log manager configuration");
+        loggerContext.setMaxCallerDataDepth(logManager.getConfig().org_apache_sling_commons_log_maxCallerDataDepth());
+        loggerContext
+                .setPackagingDataEnabled(logManager.getConfig().org_apache_sling_commons_log_packagingDataEnabled());
+        rootLogger.setLevel(Level.valueOf(logManager.getConfig().org_apache_sling_commons_log_level()));
+        addInfo("Log manager configuration applied");
+    }
+
+    private static boolean isSlf4jInitialized() {
+        return LoggerFactory.getILoggerFactory() instanceof LoggerContext;
+    }
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+    protected void bindLogManager(LogManager logManager) {
+        if (!isInitialized()) {
+            this.logManager = logManager;
+        } else {
+            this.logManager = logManager;
+            applyLogManagerConfiguration(loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME));
+        }
+    }
+
+    protected void unbindLogManager(LogManager logManager) {
+        // nothing needs done, we'll wait until a new one is provided
+    }
+
+    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+    protected void bindLoggerConfiguration(LoggerConfiguration config) {
+        if (!isInitialized()) {
+            queuedConfigurations.add(config);
+        } else {
+            attachLogger(config);
+        }
+    }
+
+    private void attachLogger(LoggerConfiguration config) {
+        List<Logger> loggers = Arrays.stream(config.getConfig().org_apache_sling_commons_log_names())
+                .filter(n -> !loggerNames.contains(n)).map(loggerName -> {
+                    addInfo("Adding logger: " + loggerName + " at level "
+                            + config.getConfig().org_apache_sling_commons_log_level() + " from config: "
+                            + config.getPid());
+                    Logger logger = loggerContext.getLogger(loggerName);
+                    logger.setLevel(Level.valueOf(config.getConfig().org_apache_sling_commons_log_level()));
+                    logger.addAppender(appender);
+                    loggerNames.add(loggerName);
+                    return logger;
+                }).collect(Collectors.toList());
+        attachedLoggers.put(config, loggers);
+        logFileSourceProvider.setAttachedLoggers(attachedLoggers);
+    }
+
+    protected void unbindLoggerConfiguration(LoggerConfiguration config) {
+        if (!isInitialized()) {
+            queuedConfigurations.remove(config);
+        } else {
+            Optional.ofNullable(attachedLoggers.remove(config)).orElse(Collections.emptyList()).forEach(logger -> {
+                addInfo("Removing logger: " + logger + " provided by config: "
+                        + config.getConfig().getClass().getName());
+                loggerNames.remove(logger.getName());
+                logger.detachAndStopAllAppenders();
+            });
+            logFileSourceProvider.setAttachedLoggers(attachedLoggers);
+        }
+    }
+
+    /**
+     * @return the initialized
+     */
+    public boolean isInitialized() {
+        return initialized;
+    }
+
+    /**
+     * @return the attachedLoggers
+     */
+    public Map<LoggerConfiguration, List<Logger>> getAttachedLoggers() {
+        return attachedLoggers;
+    }
+
+    /**
+     * @return the config
+     */
+    public Config getConfig() {
+        return config;
+    }
+
+    /**
+     * @param config the config to set
+     */
+    public void setConfig(Config config) {
+        this.config = config;
+    }
+
+    @ObjectClassDefinition(name = "%log.json.name", description = "%log.json.description", localization = "OSGI-INF/l10n/metatype")
+    public @interface Config {
+        @AttributeDefinition(name = "%appenderName.name", description = "%appenderName.description")
+        String appenderName() default "console";
+    }
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/main/resources/OSGI-INF/l10n/metatype.properties b/json-logs/org.apache.sling.commons.log.json/src/main/resources/OSGI-INF/l10n/metatype.properties
new file mode 100644
index 0000000..32eb37a
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/main/resources/OSGI-INF/l10n/metatype.properties
@@ -0,0 +1,71 @@
+#
+#  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.
+#
+
+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 \
+ detailed documentation and description.
+
+
+log.json.name = Apache Sling JSON Logging Configuration
+log.json.description = Configuration for the Sling JSON OSGi LogService \
+ See http://sling.apache.org/site/logging.html for more \
+ detailed documentation and description.
+
+log.level.name = Log Level
+log.level.description = Root Logger log level setting.
+log.file.name = Log File
+log.file.description = The name and path of the log file. If this is empty, \
+ logging goes to standard output (the console). If this path is relative it \
+ is resolved below ${sling.home}.
+ 
+log.factory.config.name = Apache Sling Logging Logger Configuration
+log.factory.config.description = Configure Loggers with levels, pattern and \
+ destination. See http://sling.apache.org/site/logging.html for more detailed \
+ documentation and description.
+ 
+log.loggers.name = Logger
+log.loggers.description = The logger names applicable for this logger \
+ 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.
+
+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
+
+log.additiv.name = Additivity
+log.additiv.description = If set to false then logs from these loggers would not be sent \
+   to any appender attached higher in the hierarchy
+
+
+appenderName.name=Appender Name
+appenderName.description=The name of the Logback appender to which to attach the configurations
\ No newline at end of file
diff --git a/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerConfigurationTest.java b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerConfigurationTest.java
new file mode 100644
index 0000000..371bf52
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerConfigurationTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.json.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+public class LoggerConfigurationTest {
+
+    private ComponentContext componentContext;
+    private Dictionary<String, Object> properties;
+
+    @Before
+    public void init() {
+        componentContext = mock(ComponentContext.class);
+        properties = new Hashtable<>();
+        when(componentContext.getProperties()).thenReturn(properties);
+    }
+
+    @Test
+    public void testLoggerConfiguration() {
+        String expectedPid = "test";
+        properties.put(Constants.SERVICE_PID, expectedPid);
+        LoggerConfiguration.Config config = TestHelper.createLoggerConfigurationConfig("logs/test.log", "info",
+                new String[] { "com.text" });
+        LoggerConfiguration configuration = new LoggerConfiguration(config, componentContext);
+
+        assertEquals(expectedPid, configuration.getPid());
+        assertEquals(config, configuration.getConfig());
+    }
+
+    @Test
+    public void testNoPid() {
+        LoggerConfiguration.Config config = TestHelper.createLoggerConfigurationConfig("logs/test.log", "info",
+                new String[] { "com.text" });
+        LoggerConfiguration configuration = new LoggerConfiguration(config, componentContext);
+
+        assertNotNull(configuration.getPid());
+        assertTrue(configuration.getPid().startsWith(LoggerConfiguration.class.getName() + "@"));
+        assertEquals(config, configuration.getConfig());
+    }
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerPrinterTest.java b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerPrinterTest.java
new file mode 100644
index 0000000..4c403f8
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/LoggerPrinterTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.json.impl;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+
+public class LoggerPrinterTest {
+
+    private OsgiConfigurator osgiConfig;
+
+    @Before
+    public void init() {
+        osgiConfig = TestHelper.mockOsgiConfigurator("info");
+        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        loggerContext.start();
+    }
+
+    @After
+    public void reset() {
+        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        loggerContext.reset();
+    }
+
+    @Test
+    public void testPrinter() {
+        LoggerConfiguration loggerConfig = TestHelper.mockLoggerConfiguration("logs/test.log", "debug",
+                new String[] { "com.text" });
+        osgiConfig.bindLoggerConfiguration(loggerConfig);
+
+        LoggerPrinter printer = new LoggerPrinter(osgiConfig);
+
+        StringWriter sw = new StringWriter();
+        printer.printConfiguration(new PrintWriter(sw));
+
+        String written = sw.toString();
+
+        assertTrue("Does not contain console header",
+                written.contains(String.format("Sling Commons Log - JSON%n===========================")));
+        assertTrue("Does not contain status header",
+                written.contains(String.format("Status%n-------------------")));
+        assertTrue("Does not contain attached loggers header",
+                written.contains(String.format("Attached Loggers%n-------------------")));
+        assertTrue("Does not contain attached loggers",
+                written.contains(String.format(
+                        "org.apache.sling.commons.log.json.impl.LoggerConfiguration~logs/test.log%n-------------------%n%nLog File: logs/test.log%nLog Log Level: debug%nDefined Logger Names: com.text%nAttached Logger Names: com.text")));
+    }
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/OsgiConfiguratorTest.java b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/OsgiConfiguratorTest.java
new file mode 100644
index 0000000..4ca0c13
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/OsgiConfiguratorTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.json.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+
+public class OsgiConfiguratorTest {
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    @Before
+    public void init() {
+        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        loggerContext.start();
+    }
+
+    @After
+    public void reset() {
+        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        loggerContext.reset();
+    }
+
+    @Test
+    public void tes() throws InterruptedException {
+
+        Map<String, Object> loggerConfiguration = new HashMap<>();
+        loggerConfiguration.put("org.apache.sling.commons.log.level", "debug");
+        loggerConfiguration.put("org.apache.sling.commons.log.file", "logs/test.log");
+        loggerConfiguration.put("org.apache.sling.commons.log.names", new String[] { "org.apache.sling" });
+        context.registerInjectActivateService(LoggerConfiguration.class,
+                loggerConfiguration);
+
+        context.registerInjectActivateService(LogManager.class,
+                Collections.singletonMap("org.apache.sling.commons.log.level", "warn"));
+
+        context.registerInjectActivateService(OsgiConfigurator.class, new OsgiConfigurator(),
+                Collections.singletonMap("appenderName", "console"));
+
+        OsgiConfigurator configurator = context.getService(OsgiConfigurator.class);
+
+        TimeUnit.SECONDS.sleep(1);
+
+        Map<String, Object> loggerConfiguration2 = new HashMap<>();
+        loggerConfiguration.put("org.apache.sling.commons.log.level", "debug");
+        loggerConfiguration.put("org.apache.sling.commons.log.file", "logs/another.log");
+        loggerConfiguration.put("org.apache.sling.commons.log.names", new String[] { "org.apache.felix" });
+        context.registerInjectActivateService(LoggerConfiguration.class,
+                loggerConfiguration2);
+
+        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+        Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
+        assertEquals(Level.WARN, rootLogger.getLevel());
+
+        assertTrue(configurator.isInitialized());
+
+        assertEquals(2, configurator.getAttachedLoggers().size());
+    }
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/TestHelper.java b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/TestHelper.java
new file mode 100644
index 0000000..ff5762c
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/test/java/org/apache/sling/commons/log/json/impl/TestHelper.java
@@ -0,0 +1,148 @@
+/*
+ * 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.json.impl;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Logger;
+
+public class TestHelper {
+
+    private TestHelper() {
+    };
+
+    public static OsgiConfigurator mockOsgiConfigurator(String appenderName) {
+
+        OsgiConfigurator osgiConfigurator = mock(OsgiConfigurator.class);
+        when(osgiConfigurator.isInitialized()).thenReturn(true);
+
+        OsgiConfigurator.Config config = new OsgiConfigurator.Config() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return null;
+            }
+
+            @Override
+            public String appenderName() {
+                return appenderName;
+            }
+
+        };
+
+        when(osgiConfigurator.getConfig()).thenReturn(config);
+
+        Map<LoggerConfiguration, List<Logger>> attachedLoggers = new ConcurrentHashMap<>();
+        doAnswer((inv) -> {
+            LoggerConfiguration lc = inv.getArgumentAt(0, LoggerConfiguration.class);
+            attachedLoggers.put(lc,
+                    Arrays.stream(lc.getConfig().org_apache_sling_commons_log_names()).map(TestHelper::getLogger)
+                            .collect(Collectors.toList()));
+            return null;
+        }).when(osgiConfigurator).bindLoggerConfiguration(any(LoggerConfiguration.class));
+        when(osgiConfigurator.getAttachedLoggers()).thenReturn(attachedLoggers);
+
+        return osgiConfigurator;
+    }
+
+    public static LogManager mockLogManager(String level) {
+
+        LogManager logManager = mock(LogManager.class);
+
+        LogManager.Config config = new LogManager.Config() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return null;
+            }
+
+            @Override
+            public String org_apache_sling_commons_log_level() {
+                return level;
+            }
+
+            @Override
+            public int org_apache_sling_commons_log_maxCallerDataDepth() {
+                return 7;
+            }
+
+            @Override
+            public boolean org_apache_sling_commons_log_packagingDataEnabled() {
+                return true;
+            }
+        };
+
+        when(logManager.getConfig()).thenReturn(config);
+        return logManager;
+    }
+
+    public static Logger getLogger(String name) {
+        return (Logger) LoggerFactory.getLogger(name);
+    }
+
+    public static LoggerConfiguration mockLoggerConfiguration(String file, String level, String[] names) {
+
+        LoggerConfiguration loggerConfiguration = mock(LoggerConfiguration.class);
+        when(loggerConfiguration.getPid()).thenReturn(LoggerConfiguration.class.getName() + "~" + file);
+
+        LoggerConfiguration.Config config = createLoggerConfigurationConfig(file, level, names);
+        when(loggerConfiguration.getConfig()).thenReturn(config);
+
+        return loggerConfiguration;
+    }
+
+    public static LoggerConfiguration.Config createLoggerConfigurationConfig(String file, String level,
+            String[] names) {
+        return new LoggerConfiguration.Config() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return null;
+            }
+
+            @Override
+            public String org_apache_sling_commons_log_file() {
+                return file;
+            }
+
+            @Override
+            public String org_apache_sling_commons_log_level() {
+                return level;
+            }
+
+            @Override
+            public String[] org_apache_sling_commons_log_names() {
+                return names;
+            }
+
+        };
+    }
+}
diff --git a/json-logs/org.apache.sling.commons.log.json/src/test/resources/logback.xml b/json-logs/org.apache.sling.commons.log.json/src/test/resources/logback.xml
new file mode 100644
index 0000000..c94124f
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.log.json/src/test/resources/logback.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<configuration>
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"></encoder>
+    </appender>
+
+    <logger name="ROOT">
+        <appender-ref ref="console" />
+    </logger>
+</configuration>
\ No newline at end of file
diff --git a/json-logs/org.apache.sling.commons.logback.configurator/bnd.bnd b/json-logs/org.apache.sling.commons.logback.configurator/bnd.bnd
new file mode 100644
index 0000000..0267af0
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.logback.configurator/bnd.bnd
@@ -0,0 +1,2 @@
+Fragment-Host: ch.qos.logback.classic
+Bundle-ClassPath: .
\ No newline at end of file
diff --git a/json-logs/org.apache.sling.commons.logback.configurator/pom.xml b/json-logs/org.apache.sling.commons.logback.configurator/pom.xml
new file mode 100644
index 0000000..594d132
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.logback.configurator/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>46</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.commons.log.logback.configurator</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>Apache Sling - Commons Logback Configurator</name>
+    <description>A fragment to configure logback when the repository starts</description>
+
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.build.outputTimestamp>10</project.build.outputTimestamp>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-baseline-maven-plugin</artifactId>
+                <configuration>
+                    <failOnMissing>false</failOnMissing>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.10</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.logstash.logback</groupId>
+            <artifactId>logstash-logback-encoder</artifactId>
+            <version>7.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>23.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/json-logs/org.apache.sling.commons.logback.configurator/src/main/java/org/apache/sling/commons/log/compat/StartupConfigurator.java b/json-logs/org.apache.sling.commons.logback.configurator/src/main/java/org/apache/sling/commons/log/compat/StartupConfigurator.java
new file mode 100644
index 0000000..17ae425
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.logback.configurator/src/main/java/org/apache/sling/commons/log/compat/StartupConfigurator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.compat;
+
+import java.util.Optional;
+
+import org.jetbrains.annotations.NotNull;
+
+import ch.qos.logback.classic.BasicConfigurator;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.ConsoleAppender;
+import ch.qos.logback.core.encoder.Encoder;
+import net.logstash.logback.encoder.LogstashEncoder;
+
+public class StartupConfigurator extends BasicConfigurator {
+
+    public static final String APPENDER_NAME = "console";
+
+    @Override
+    public void configure(LoggerContext context) {
+        addInfo("Setting up default configuration.");
+
+        ConsoleAppender<ILoggingEvent> ca = new ConsoleAppender<>();
+        ca.setContext(context);
+        ca.setName(APPENDER_NAME);
+        ca.setEncoder(getEncoder(context));
+        ca.start();
+
+        Logger rootLogger = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
+        rootLogger.setLevel(Level.INFO);
+        rootLogger.addAppender(ca);
+
+        addInfo("Default configuration complete.");
+    }
+
+    @NotNull
+    private Encoder<ILoggingEvent> getEncoder(@NotNull LoggerContext context) {
+        LogstashEncoder logstashEncoder = new LogstashEncoder();
+        logstashEncoder.setContext(context);
+        logstashEncoder.setCustomFields(APPENDER_NAME);
+        logstashEncoder.start();
+
+        return logstashEncoder;
+    }
+
+}
diff --git a/json-logs/org.apache.sling.commons.logback.configurator/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator b/json-logs/org.apache.sling.commons.logback.configurator/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator
new file mode 100644
index 0000000..e3a055a
--- /dev/null
+++ b/json-logs/org.apache.sling.commons.logback.configurator/src/main/resources/META-INF/services/ch.qos.logback.classic.spi.Configurator
@@ -0,0 +1 @@
+org.apache.sling.commons.log.compat.StartupConfigurator
\ No newline at end of file