You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2020/08/22 23:50:37 UTC

[qpid-broker-j] branch master updated (734fb39 -> 0324354)

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

orudyy pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git.


    from 734fb39  QPID-8460 [Broker-j] Do not expose private information to exception message (#55)
     new c4d4632  QPID-8368: [Broker-J] Graylog support
     new 4f6de18  QPID-8368: [Broker-J] Move ManagedObject annotations from interfaces to implementations
     new e899810  QPID-8368: [Broker-J] Make GrayLogger implementations conditionally available
     new 7efcddd  QPID-8368: [Broker-J] Add graylog module to the project module list and include it as a dependecy of qpid-broker
     new 0324354  QPID-8368: [Broker-J] Move graylog loggers into graylog package

The 5 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:
 .../apache/qpid/server/logging/EventLogger.java    |  16 +-
 .../pom.xml                                        |  34 +-
 .../qpid/server/logging/CallerDataFilter.java      |  84 ++
 .../server/logging/logback/event/LoggingEvent.java | 149 ++++
 .../logback/graylog/BrokerGraylogLogger.java       |   5 +-
 .../logback/graylog/BrokerGraylogLoggerImpl.java   | 231 +++++
 .../logback/graylog/GelfAppenderConfiguration.java |  37 +-
 .../logback/graylog/GelfAppenderDefaults.java      |  64 ++
 .../server/logging/logback/graylog/GelfCheck.java  |   8 +-
 .../logback/graylog/GelfEncoderConfiguration.java  |  47 +-
 .../logback/graylog/GelfEncoderDefaults.java       |  62 ++
 .../logging/logback/graylog/GraylogAppender.java   | 124 +++
 .../logging/logback/graylog/GraylogLogger.java     | 128 +++
 .../logback/graylog/VirtualHostGraylogLogger.java  |   7 +-
 .../graylog/VirtualHostGraylogLoggerImpl.java      | 233 ++++++
 .../server/logging/logback/validator/AtLeast.java  |  47 +-
 .../logging/logback/validator/AtLeastOne.java}     |  21 +-
 .../logging/logback/validator/AtLeastZero.java     |  22 +-
 .../validator/GelfConfigurationValidator.java      | 120 +++
 .../logback/validator/GelfMessageStaticFields.java | 143 ++++
 .../server/logging/logback/validator/Port.java     |  70 ++
 .../logging/logback/validator/Validator.java       |   8 +-
 .../org/apache/qpid/server/util/ArrayUtils.java    |  23 +-
 .../management/logger/brokerlogger/graylog}/add.js |  35 +-
 .../management/logger/brokerlogger/graylog/show.js |  56 ++
 .../logger/virtualhostlogger/graylog}/add.js       |  35 +-
 .../logger/virtualhostlogger/graylog/show.js       |  56 ++
 .../main/java/resources/logger/graylog/add.html    | 305 +++++++
 .../main/java/resources/logger/graylog/show.html   | 135 +++
 .../resources/logger/graylog/showStaticField.html  |   4 +
 .../qpid/server/logging/CallerDataFilterTest.java  | 167 ++++
 .../logging/logback/event/LoggingEventTest.java    | 298 +++++++
 .../logging/logback/event/TestLoggingEvent.java    | 224 +++++
 .../logback/graylog/GraylogAppenderTest.java       | 932 +++++++++++++++++++++
 .../logging/logback/validator/AtLeastOneTest.java  |  93 ++
 .../logging/logback/validator/AtLeastTest.java     |  91 ++
 .../logging/logback/validator/AtLeastZeroTest.java |  93 ++
 .../validator/GelfConfigurationValidatorTest.java  | 788 +++++++++++++++++
 .../validator/GelfMessageStaticFieldsTest.java     | 142 ++++
 .../server/logging/logback/validator/PortTest.java |  91 ++
 .../logback/validator/TestConfiguredObject.java    | 486 +++++++++++
 .../validator/TestConfiguredObjectFactory.java     | 100 +++
 .../validator/TestConfiguredObjectTypeFactory.java |  88 ++
 .../logging/logback/validator}/TestModel.java      |  86 +-
 .../apache/qpid/server/util/ArrayUtilsTest.java    |  73 ++
 .../src/main/java/resources/addLogger.html         |   4 +-
 .../src/main/java/resources/css/common.css         |  19 +
 .../resources/js/qpid/common/MapInputWidget.js     | 450 ++++++++++
 .../src/main/java/resources/js/qpid/common/util.js |  54 +-
 .../resources/js/qpid/common/widgetconfigurer.js   |   2 +-
 .../java/resources/js/qpid/management/addLogger.js | 103 ++-
 broker/pom.xml                                     |   6 +
 .../runtime/Java-Broker-Runtime-Log-Files.xml      |   9 +
 pom.xml                                            |  17 +
 54 files changed, 6499 insertions(+), 226 deletions(-)
 copy broker-plugins/{jdbc-logging-logback => graylog-logging-logback}/pom.xml (82%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/CallerDataFilter.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/event/LoggingEvent.java
 copy broker-core/src/main/java/org/apache/qpid/server/exchange/DestinationReferrer.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLogger.java (78%)
 mode change 100755 => 100644
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLoggerImpl.java
 copy perftests/src/main/java/org/apache/qpid/disttest/message/CreateSessionCommand.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderConfiguration.java (50%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderDefaults.java
 copy bdbstore/src/main/java/org/apache/qpid/server/JECheck.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfCheck.java (87%)
 copy perftests/src/main/java/org/apache/qpid/disttest/controller/config/QueueConfig.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderConfiguration.java (50%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderDefaults.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppender.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogLogger.java
 copy bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacadeFactory.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLogger.java (77%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLoggerImpl.java
 copy broker-core/src/main/java/org/apache/qpid/server/store/preferences/ProvidedPreferenceStoreFactoryService.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeast.java (50%)
 copy broker-plugins/{memory-store/src/main/java/org/apache/qpid/server/store/MemoryConfigurationStore.java => graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastOne.java} (65%)
 copy bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastZero.java (67%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFields.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Port.java
 copy bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/EnvironmentFacadeFactory.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Validator.java (82%)
 copy broker-core/src/main/java/org/apache/qpid/server/util/RandomUUIDGen.java => broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/util/ArrayUtils.java (75%)
 copy broker-plugins/{jdbc-provider-bone/src/main/java/resources/js/qpid/management/store/pool/bonecp => graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog}/add.js (57%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/show.js
 copy broker-plugins/{jdbc-provider-bone/src/main/java/resources/js/qpid/management/store/pool/bonecp => graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog}/add.js (57%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/show.js
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/add.html
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/show.html
 create mode 100644 broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/showStaticField.html
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/CallerDataFilterTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/LoggingEventTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/TestLoggingEvent.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppenderTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastOneTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastZeroTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFieldsTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/PortTest.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObject.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectFactory.java
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectTypeFactory.java
 copy {broker-core/src/test/java/org/apache/qpid/server/model/testmodels/singleton => broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator}/TestModel.java (51%)
 create mode 100644 broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/util/ArrayUtilsTest.java
 create mode 100644 broker-plugins/management-http/src/main/java/resources/js/qpid/common/MapInputWidget.js


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 01/05: QPID-8368: [Broker-J] Graylog support

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

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit c4d463282dd506aff8dd150792be980b48339a9e
Author: Marek Laca <ma...@deutsche-boerse.com>
AuthorDate: Thu Jul 30 13:37:22 2020 +0200

    QPID-8368: [Broker-J] Graylog support
---
 .../apache/qpid/server/logging/EventLogger.java    |  16 +-
 broker-plugins/graylog-logging-logback/pom.xml     |  96 +++
 .../qpid/server/logging/CallerDataFilter.java      |  84 ++
 .../logging/logback/BrokerGraylogLogger.java       |  31 +
 .../logging/logback/BrokerGraylogLoggerImpl.java   | 223 +++++
 .../logging/logback/GelfAppenderConfiguration.java |  61 ++
 .../logging/logback/GelfAppenderDefaults.java      |  64 ++
 .../logging/logback/GelfEncoderConfiguration.java  |  64 ++
 .../logging/logback/GelfEncoderDefaults.java       |  62 ++
 .../server/logging/logback/GraylogAppender.java    | 124 +++
 .../qpid/server/logging/logback/GraylogLogger.java | 128 +++
 .../logging/logback/VirtualHostGraylogLogger.java  |  32 +
 .../logback/VirtualHostGraylogLoggerImpl.java      | 224 +++++
 .../server/logging/logback/event/LoggingEvent.java | 149 ++++
 .../server/logging/logback/validator/AtLeast.java  |  66 ++
 .../logging/logback/validator/AtLeastOne.java      |  44 +
 .../logging/logback/validator/AtLeastZero.java     |  44 +
 .../validator/GelfConfigurationValidator.java      | 120 +++
 .../logback/validator/GelfMessageStaticFields.java | 143 ++++
 .../server/logging/logback/validator/Port.java     |  70 ++
 .../logging/logback/validator/Validator.java       |  30 +
 .../org/apache/qpid/server/util/ArrayUtils.java    |  37 +
 .../management/logger/brokerlogger/graylog/add.js  |  49 ++
 .../management/logger/brokerlogger/graylog/show.js |  56 ++
 .../logger/virtualhostlogger/graylog/add.js        |  49 ++
 .../logger/virtualhostlogger/graylog/show.js       |  56 ++
 .../main/java/resources/logger/graylog/add.html    | 305 +++++++
 .../main/java/resources/logger/graylog/show.html   | 135 +++
 .../resources/logger/graylog/showStaticField.html  |   4 +
 .../qpid/server/logging/CallerDataFilterTest.java  | 167 ++++
 .../logging/logback/GraylogAppenderTest.java       | 932 +++++++++++++++++++++
 .../logging/logback/event/LoggingEventTest.java    | 298 +++++++
 .../logging/logback/event/TestLoggingEvent.java    | 224 +++++
 .../logging/logback/validator/AtLeastOneTest.java  |  93 ++
 .../logging/logback/validator/AtLeastTest.java     |  91 ++
 .../logging/logback/validator/AtLeastZeroTest.java |  93 ++
 .../validator/GelfConfigurationValidatorTest.java  | 788 +++++++++++++++++
 .../validator/GelfMessageStaticFieldsTest.java     | 142 ++++
 .../server/logging/logback/validator/PortTest.java |  91 ++
 .../logback/validator/TestConfiguredObject.java    | 486 +++++++++++
 .../validator/TestConfiguredObjectFactory.java     | 100 +++
 .../validator/TestConfiguredObjectTypeFactory.java |  88 ++
 .../logging/logback/validator/TestModel.java       | 118 +++
 .../apache/qpid/server/util/ArrayUtilsTest.java    |  73 ++
 .../src/main/java/resources/addLogger.html         |   4 +-
 .../src/main/java/resources/css/common.css         |  19 +
 .../resources/js/qpid/common/MapInputWidget.js     | 450 ++++++++++
 .../src/main/java/resources/js/qpid/common/util.js |  54 +-
 .../resources/js/qpid/common/widgetconfigurer.js   |   2 +-
 .../java/resources/js/qpid/management/addLogger.js | 103 ++-
 broker/pom.xml                                     |  19 +
 .../runtime/Java-Broker-Runtime-Log-Files.xml      |   9 +
 pom.xml                                            |  28 +
 53 files changed, 7005 insertions(+), 33 deletions(-)

diff --git a/broker-core/src/main/java/org/apache/qpid/server/logging/EventLogger.java b/broker-core/src/main/java/org/apache/qpid/server/logging/EventLogger.java
index ed97e14..40f3ad5 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/logging/EventLogger.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/logging/EventLogger.java
@@ -20,7 +20,7 @@
  */
 package org.apache.qpid.server.logging;
 
-public class EventLogger
+public class EventLogger implements MessageLogger
 {
     private MessageLogger _messageLogger;
 
@@ -40,6 +40,7 @@ public class EventLogger
      * @param subject The subject that is being logged
      * @param message The message to log
      */
+    @Override
     public void message(LogSubject subject, LogMessage message)
     {
         _messageLogger.message(subject, message);
@@ -50,11 +51,24 @@ public class EventLogger
      *
      * @param message The message to log
      */
+    @Override
     public void message(LogMessage message)
     {
         _messageLogger.message((message));
     }
 
+    @Override
+    public boolean isEnabled()
+    {
+        return _messageLogger.isEnabled();
+    }
+
+    @Override
+    public boolean isMessageEnabled(String logHierarchy)
+    {
+        return _messageLogger.isMessageEnabled(logHierarchy);
+    }
+
     public void setMessageLogger(final MessageLogger messageLogger)
     {
         _messageLogger = messageLogger;
diff --git a/broker-plugins/graylog-logging-logback/pom.xml b/broker-plugins/graylog-logging-logback/pom.xml
new file mode 100644
index 0000000..7371dfa
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.qpid</groupId>
+    <artifactId>qpid-broker-parent</artifactId>
+    <version>9.0.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>qpid-broker-plugins-graylog-logging-logback</artifactId>
+  <name>Apache Qpid Broker-J LogBack GrayLog Logging Plug-in</name>
+  <description>LogBack GrayLog Logging broker plug-in</description>
+
+  <properties>
+    <logback-gelf-version>3.0.0</logback-gelf-version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-broker-plugins-logging-logback</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-broker-core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-broker-codegen</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>de.siegmar</groupId>
+      <artifactId>logback-gelf</artifactId>
+      <version>${logback-gelf-version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+    </dependency>
+
+    <!-- test dependencies -->
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-test-utils</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-broker-core</artifactId>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+      </resource>
+      <resource>
+        <directory>src/main/java</directory>
+        <includes>
+          <include>resources/</include>
+        </includes>
+      </resource>
+    </resources>
+  </build>
+
+</project>
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/CallerDataFilter.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/CallerDataFilter.java
new file mode 100644
index 0000000..b16284e
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/CallerDataFilter.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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.qpid.server.logging;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CallerDataFilter
+{
+    private static final Set<String> METHOD_NAMES = buildMethodNames();
+
+    private final ClassLoader _classLoader = Thread.currentThread().getContextClassLoader();
+
+    public StackTraceElement[] filter(StackTraceElement[] elements)
+    {
+        if (elements == null)
+        {
+            return new StackTraceElement[0];
+        }
+
+        for (int depth = elements.length - 1; depth >= 0; --depth)
+        {
+            final StackTraceElement element = elements[depth];
+            if (isMessageMethod(element.getMethodName()) && isMessageLogger(element.getClassName()))
+            {
+                final int length = elements.length - (depth + 1);
+                if (length > 0)
+                {
+                    final StackTraceElement[] stackTrace = new StackTraceElement[length];
+                    System.arraycopy(elements, depth + 1, stackTrace, 0, length);
+                    return stackTrace;
+                }
+                return elements;
+            }
+        }
+        return elements;
+    }
+
+    private boolean isMessageMethod(String method)
+    {
+        return METHOD_NAMES.contains(method);
+    }
+
+    private boolean isMessageLogger(String className)
+    {
+        try
+        {
+            return MessageLogger.class.isAssignableFrom(Class.forName(className, false, _classLoader));
+        }
+        catch (ClassNotFoundException ignored)
+        {
+            return false;
+        }
+    }
+
+    private static Set<String> buildMethodNames()
+    {
+        return Arrays.stream(MessageLogger.class.getDeclaredMethods())
+                .filter(method -> Void.TYPE.equals(method.getReturnType()))
+                .map(Method::getName)
+                .collect(Collectors.toSet());
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java
new file mode 100644
index 0000000..4617672
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.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.qpid.server.logging.logback;
+
+import org.apache.qpid.server.model.BrokerLogger;
+import org.apache.qpid.server.model.ManagedObject;
+
+@ManagedObject(category = false, type = GraylogLogger.TYPE,
+        description = "Logger implementation that writes log events to a remote graylog server",
+        validChildTypes = "org.apache.qpid.server.logging.logback.AbstractLogger#getSupportedBrokerLoggerChildTypes()")
+public interface BrokerGraylogLogger<X extends BrokerGraylogLogger<X>> extends BrokerLogger<X>, GraylogLogger<X>
+{
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
new file mode 100644
index 0000000..26e4e26
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
@@ -0,0 +1,223 @@
+/*
+ *
+ * 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.qpid.server.logging.logback;
+
+import ch.qos.logback.classic.AsyncAppender;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.Context;
+import org.apache.qpid.server.logging.logback.validator.GelfConfigurationValidator;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ManagedAttributeField;
+import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public class BrokerGraylogLoggerImpl extends AbstractBrokerLogger<BrokerGraylogLoggerImpl> implements BrokerGraylogLogger<BrokerGraylogLoggerImpl>
+{
+    @ManagedObjectFactoryConstructor
+    public BrokerGraylogLoggerImpl(Map<String, Object> attributes, Broker<?> broker)
+    {
+        super(attributes, broker);
+    }
+
+    @ManagedAttributeField
+    private String _remoteHost;
+
+    @ManagedAttributeField
+    private int _port = GelfAppenderDefaults.PORT.value();
+
+    @ManagedAttributeField
+    private int _reconnectionInterval = GelfAppenderDefaults.RECONNECTION_INTERVAL.value();
+
+    @ManagedAttributeField
+    private int _connectionTimeout = GelfAppenderDefaults.CONNECTION_TIMEOUT.value();
+
+    @ManagedAttributeField
+    private int _maximumReconnectionAttempts = GelfAppenderDefaults.MAXIMUM_RECONNECTION_ATTEMPTS.value();
+
+    @ManagedAttributeField
+    private int _retryDelay = GelfAppenderDefaults.RETRY_DELAY.value();
+
+    @ManagedAttributeField
+    private int _messagesFlushTimeOut = GelfAppenderDefaults.MESSAGES_FLUSH_TIMEOUT.value();
+
+    @ManagedAttributeField
+    private int _messageBufferCapacity = GelfAppenderDefaults.MESSAGE_BUFFER_CAPACITY.value();
+
+    @ManagedAttributeField
+    private boolean _rawMessageIncluded = GelfEncoderDefaults.RAW_MESSAGE_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _eventMarkerIncluded = GelfEncoderDefaults.EVENT_MARKER_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _mdcPropertiesIncluded = GelfEncoderDefaults.MDC_PROPERTIES_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _callerDataIncluded = GelfEncoderDefaults.CALLER_DATA_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _rootExceptionDataIncluded = GelfEncoderDefaults.ROOT_EXCEPTION_DATA_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _logLevelNameIncluded = GelfEncoderDefaults.LOG_LEVEL_NAME_INCLUDED.value();
+
+    @ManagedAttributeField
+    private Map<String, Object> _staticFields = Collections.emptyMap();
+
+    @ManagedAttributeField
+    private String _messageOriginHost;
+
+    private AsyncAppender _appender;
+
+    @Override
+    public String getRemoteHost()
+    {
+        return _remoteHost;
+    }
+
+    @Override
+    public int getPort()
+    {
+        return _port;
+    }
+
+    @Override
+    public int getReconnectionInterval()
+    {
+        return _reconnectionInterval;
+    }
+
+    @Override
+    public int getConnectionTimeout()
+    {
+        return _connectionTimeout;
+    }
+
+    @Override
+    public int getMaximumReconnectionAttempts()
+    {
+        return _maximumReconnectionAttempts;
+    }
+
+    @Override
+    public int getRetryDelay()
+    {
+        return _retryDelay;
+    }
+
+    @Override
+    public int getMessagesFlushTimeOut()
+    {
+        return _messagesFlushTimeOut;
+    }
+
+    @Override
+    public int getMessageBufferCapacity()
+    {
+        return _messageBufferCapacity;
+    }
+
+    @Override
+    public boolean isRawMessageIncluded()
+    {
+        return _rawMessageIncluded;
+    }
+
+    @Override
+    public boolean isEventMarkerIncluded()
+    {
+        return _eventMarkerIncluded;
+    }
+
+    @Override
+    public boolean hasMdcPropertiesIncluded()
+    {
+        return _mdcPropertiesIncluded;
+    }
+
+    @Override
+    public boolean isCallerDataIncluded()
+    {
+        return _callerDataIncluded;
+    }
+
+    @Override
+    public boolean hasRootExceptionDataIncluded()
+    {
+        return _rootExceptionDataIncluded;
+    }
+
+    @Override
+    public boolean isLogLevelNameIncluded()
+    {
+        return _logLevelNameIncluded;
+    }
+
+    @Override
+    public Map<String, Object> getStaticFields()
+    {
+        return _staticFields;
+    }
+
+    @Override
+    public String getMessageOriginHost()
+    {
+        return _messageOriginHost;
+    }
+
+    @Override
+    public AsyncAppender appender()
+    {
+        return _appender;
+    }
+
+    @Override
+    protected Appender<ILoggingEvent> createAppenderInstance(Context context)
+    {
+        _appender = GraylogAppender.newInstance(context, this);
+        return _appender;
+    }
+
+    @Override
+    protected void validateOnCreate()
+    {
+        super.validateOnCreate();
+        GelfConfigurationValidator.validateConfiguration(this, this);
+    }
+
+    @Override
+    protected void onOpen()
+    {
+        super.onOpen();
+        GelfConfigurationValidator.validateConfiguration(this, this);
+    }
+
+    @Override
+    protected void postSetAttributes(Set<String> actualUpdatedAttributes)
+    {
+        super.postSetAttributes(actualUpdatedAttributes);
+        GelfConfigurationValidator.validateConfiguration(this, this, actualUpdatedAttributes);
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderConfiguration.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderConfiguration.java
new file mode 100644
index 0000000..7d2fc24
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderConfiguration.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.qpid.server.logging.logback;
+
+public interface GelfAppenderConfiguration extends GelfEncoderConfiguration
+{
+    String getRemoteHost();
+
+    default int getPort()
+    {
+        return GelfAppenderDefaults.PORT.value();
+    }
+
+    default int getReconnectionInterval()
+    {
+        return GelfAppenderDefaults.RECONNECTION_INTERVAL.value();
+    }
+
+    default int getConnectionTimeout()
+    {
+        return GelfAppenderDefaults.CONNECTION_TIMEOUT.value();
+    }
+
+    default int getMaximumReconnectionAttempts()
+    {
+        return GelfAppenderDefaults.MAXIMUM_RECONNECTION_ATTEMPTS.value();
+    }
+
+    default int getRetryDelay()
+    {
+        return GelfAppenderDefaults.RETRY_DELAY.value();
+    }
+
+    default int getMessagesFlushTimeOut()
+    {
+        return GelfAppenderDefaults.MESSAGES_FLUSH_TIMEOUT.value();
+    }
+
+    default int getMessageBufferCapacity()
+    {
+        return GelfAppenderDefaults.MESSAGE_BUFFER_CAPACITY.value();
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderDefaults.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderDefaults.java
new file mode 100644
index 0000000..6749af9
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderDefaults.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.qpid.server.logging.logback;
+
+public enum GelfAppenderDefaults
+{
+    PORT(GelfAppenderDefaults.PORT_AS_STRING),
+
+    RECONNECTION_INTERVAL(GelfAppenderDefaults.RECONNECTION_INTERVAL_AS_STRING),
+
+    CONNECTION_TIMEOUT(GelfAppenderDefaults.CONNECTION_TIMEOUT_AS_STRING),
+
+    MAXIMUM_RECONNECTION_ATTEMPTS(GelfAppenderDefaults.MAXIMUM_RECONNECTION_ATTEMPTS_AS_STRING),
+
+    RETRY_DELAY(GelfAppenderDefaults.RETRY_DELAY_AS_STRING),
+
+    MESSAGES_FLUSH_TIMEOUT(GelfAppenderDefaults.MESSAGES_FLUSH_TIMEOUT_AS_STRING),
+
+    MESSAGE_BUFFER_CAPACITY(GelfAppenderDefaults.MESSAGE_BUFFER_CAPACITY_AS_STRING);
+
+    public static final String PORT_AS_STRING = "12201";
+
+    public static final String RECONNECTION_INTERVAL_AS_STRING = "60000";
+
+    public static final String CONNECTION_TIMEOUT_AS_STRING = "15000";
+
+    public static final String MAXIMUM_RECONNECTION_ATTEMPTS_AS_STRING = "2";
+
+    public static final String RETRY_DELAY_AS_STRING = "3000";
+
+    public static final String MESSAGES_FLUSH_TIMEOUT_AS_STRING = "1000";
+
+    public static final String MESSAGE_BUFFER_CAPACITY_AS_STRING = "256";
+
+    private final int _value;
+
+    GelfAppenderDefaults(String value)
+    {
+        this._value = Integer.parseInt(value);
+    }
+
+    public int value()
+    {
+        return _value;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderConfiguration.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderConfiguration.java
new file mode 100644
index 0000000..500c571
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderConfiguration.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.qpid.server.logging.logback;
+
+import java.util.Collections;
+import java.util.Map;
+
+public interface GelfEncoderConfiguration
+{
+    String getMessageOriginHost();
+
+    default boolean isRawMessageIncluded()
+    {
+        return GelfEncoderDefaults.RAW_MESSAGE_INCLUDED.value();
+    }
+
+    default boolean isEventMarkerIncluded()
+    {
+        return GelfEncoderDefaults.EVENT_MARKER_INCLUDED.value();
+    }
+
+    default boolean hasMdcPropertiesIncluded()
+    {
+        return GelfEncoderDefaults.MDC_PROPERTIES_INCLUDED.value();
+    }
+
+    default boolean isCallerDataIncluded()
+    {
+        return GelfEncoderDefaults.CALLER_DATA_INCLUDED.value();
+    }
+
+    default boolean hasRootExceptionDataIncluded()
+    {
+        return GelfEncoderDefaults.ROOT_EXCEPTION_DATA_INCLUDED.value();
+    }
+
+    default boolean isLogLevelNameIncluded()
+    {
+        return GelfEncoderDefaults.LOG_LEVEL_NAME_INCLUDED.value();
+    }
+
+    default Map<String, Object> getStaticFields()
+    {
+        return Collections.emptyMap();
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderDefaults.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderDefaults.java
new file mode 100644
index 0000000..62890ec
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderDefaults.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.qpid.server.logging.logback;
+
+public enum GelfEncoderDefaults
+{
+    RAW_MESSAGE_INCLUDED(GelfEncoderDefaults.RAW_MESSAGE_INCLUDED_AS_STRING),
+
+    EVENT_MARKER_INCLUDED(GelfEncoderDefaults.EVENT_MARKER_INCLUDED_AS_STRING),
+
+    MDC_PROPERTIES_INCLUDED(GelfEncoderDefaults.MDC_PROPERTIES_INCLUDED_AS_STRING),
+
+    CALLER_DATA_INCLUDED(GelfEncoderDefaults.CALLER_DATA_INCLUDED_AS_STRING),
+
+    ROOT_EXCEPTION_DATA_INCLUDED(GelfEncoderDefaults.ROOT_EXCEPTION_DATA_INCLUDED_AS_STRING),
+
+    LOG_LEVEL_NAME_INCLUDED(GelfEncoderDefaults.LOG_LEVEL_NAME_INCLUDED_AS_STRING);
+
+    public static final String FALSE = "false";
+    public static final String TRUE = "true";
+
+    public static final String RAW_MESSAGE_INCLUDED_AS_STRING = FALSE;
+
+    public static final String EVENT_MARKER_INCLUDED_AS_STRING = TRUE;
+
+    public static final String MDC_PROPERTIES_INCLUDED_AS_STRING = TRUE;
+
+    public static final String CALLER_DATA_INCLUDED_AS_STRING = FALSE;
+
+    public static final String ROOT_EXCEPTION_DATA_INCLUDED_AS_STRING = FALSE;
+
+    public static final String LOG_LEVEL_NAME_INCLUDED_AS_STRING = FALSE;
+
+    private final boolean _value;
+
+    GelfEncoderDefaults(String value)
+    {
+        _value = Boolean.parseBoolean(value);
+    }
+
+    public boolean value() {
+        return _value;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogAppender.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogAppender.java
new file mode 100644
index 0000000..841bb8b
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogAppender.java
@@ -0,0 +1,124 @@
+/*
+ *
+ * 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.qpid.server.logging.logback;
+
+import ch.qos.logback.classic.AsyncAppender;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Context;
+import de.siegmar.logbackgelf.GelfEncoder;
+import de.siegmar.logbackgelf.GelfTcpAppender;
+import org.apache.qpid.server.logging.logback.event.LoggingEvent;
+
+import java.util.Objects;
+
+final class GraylogAppender extends AsyncAppender
+{
+    private final GelfAppenderConfiguration _configuration;
+
+    static GraylogAppender newInstance(Context context, GelfAppenderConfiguration config)
+    {
+        final GraylogAppender appender = new GraylogAppender(config);
+        appender.setContext(context);
+        appender.setQueueSize(config.getMessageBufferCapacity());
+        appender.setNeverBlock(true);
+        appender.setMaxFlushTime(config.getMessagesFlushTimeOut());
+        appender.setIncludeCallerData(config.isCallerDataIncluded());
+        return appender;
+    }
+
+    private GraylogAppender(GelfAppenderConfiguration configuration)
+    {
+        super();
+        this._configuration = Objects.requireNonNull(configuration);
+    }
+
+    @Override
+    public void start()
+    {
+        if (!isStarted())
+        {
+            final GelfEncoder encoder = buildEncoder(getContext(), _configuration);
+            encoder.start();
+
+            final GelfTcpAppender appender = newGelfTcpAppender(getContext(), _configuration);
+            appender.setEncoder(encoder);
+            appender.setName(getName());
+            appender.start();
+            addAppender(appender);
+        }
+        super.start();
+    }
+
+    @Override
+    public void setName(final String name)
+    {
+        super.setName(name);
+        iteratorForAppenders().forEachRemaining(item -> item.setName(name));
+    }
+
+    @Override
+    public void setContext(final Context context)
+    {
+        super.setContext(context);
+        iteratorForAppenders().forEachRemaining(item -> item.setContext(context));
+    }
+
+    @Override
+    public void doAppend(ILoggingEvent eventObject)
+    {
+        super.doAppend(LoggingEvent.wrap(eventObject));
+    }
+
+    private GelfTcpAppender newGelfTcpAppender(Context context, GelfAppenderConfiguration logger)
+    {
+        final GelfTcpAppender appender = new GelfTcpAppender();
+        appender.setContext(context);
+        appender.setGraylogHost(logger.getRemoteHost());
+        appender.setGraylogPort(logger.getPort());
+        appender.setReconnectInterval(calculateReconnectionInterval(logger));
+        appender.setConnectTimeout(logger.getConnectionTimeout());
+        appender.setMaxRetries(logger.getMaximumReconnectionAttempts());
+        appender.setRetryDelay(logger.getRetryDelay());
+        return appender;
+    }
+
+    private int calculateReconnectionInterval(GelfAppenderConfiguration logger)
+    {
+        final int modulo = logger.getReconnectionInterval() % 1000;
+        final int auxiliary = logger.getReconnectionInterval() / 1000;
+        return modulo > 0 ? auxiliary + 1 : auxiliary;
+    }
+
+    private GelfEncoder buildEncoder(Context context, GelfEncoderConfiguration settings)
+    {
+        final GelfEncoder encoder = new GelfEncoder();
+        encoder.setContext(context);
+        encoder.setOriginHost(settings.getMessageOriginHost());
+        encoder.setIncludeRawMessage(settings.isRawMessageIncluded());
+        encoder.setIncludeMarker(settings.isEventMarkerIncluded());
+        encoder.setIncludeMdcData(settings.hasMdcPropertiesIncluded());
+        encoder.setIncludeCallerData(settings.isCallerDataIncluded());
+        encoder.setIncludeRootCauseData(settings.hasRootExceptionDataIncluded());
+        encoder.setIncludeLevelName(settings.isLogLevelNameIncluded());
+        encoder.setStaticFields(settings.getStaticFields());
+        return encoder;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogLogger.java
new file mode 100644
index 0000000..f47230e
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogLogger.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * 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.qpid.server.logging.logback;
+
+import ch.qos.logback.classic.AsyncAppender;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ManagedAttribute;
+import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.ManagedStatistic;
+import org.apache.qpid.server.model.StatisticType;
+import org.apache.qpid.server.model.StatisticUnit;
+
+import java.util.Map;
+
+@ManagedObject
+public interface GraylogLogger<X extends GraylogLogger<X>> extends GelfAppenderConfiguration, ConfiguredObject<X>
+{
+    String TYPE = "Graylog";
+
+    @Override
+    @ManagedAttribute(mandatory = true, description = "The graylog server remote host.")
+    String getRemoteHost();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.PORT_AS_STRING,
+            description = "The graylog server port number.")
+    int getPort();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.RECONNECTION_INTERVAL_AS_STRING,
+            description = "The reconnection interval.")
+    int getReconnectionInterval();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.CONNECTION_TIMEOUT_AS_STRING,
+            description = "The connection timeout.")
+    int getConnectionTimeout();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.MAXIMUM_RECONNECTION_ATTEMPTS_AS_STRING,
+            description = "The maximum reconnection attempts.")
+    int getMaximumReconnectionAttempts();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.RETRY_DELAY_AS_STRING,
+            description = "The retry delay.")
+    int getRetryDelay();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.MESSAGES_FLUSH_TIMEOUT_AS_STRING,
+            description = "The messages flush time out at logger stop.")
+    int getMessagesFlushTimeOut();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfAppenderDefaults.MESSAGE_BUFFER_CAPACITY_AS_STRING,
+            description = "The capacity of the message buffer.")
+    int getMessageBufferCapacity();
+
+    @Override
+    @ManagedAttribute(mandatory = true, description = "The origin host that is included in the GELF log message.")
+    String getMessageOriginHost();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfEncoderDefaults.RAW_MESSAGE_INCLUDED_AS_STRING,
+            description = "Include the raw error in the GELF log message.")
+    boolean isRawMessageIncluded();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfEncoderDefaults.EVENT_MARKER_INCLUDED_AS_STRING,
+            description = "Include the event marker in the GELF log message.")
+    boolean isEventMarkerIncluded();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfEncoderDefaults.MDC_PROPERTIES_INCLUDED_AS_STRING,
+            description = "Include the MDC properties in the GELF log message.")
+    boolean hasMdcPropertiesIncluded();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfEncoderDefaults.CALLER_DATA_INCLUDED_AS_STRING,
+            description = "Include the caller data in the GELF log message.")
+    boolean isCallerDataIncluded();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfEncoderDefaults.ROOT_EXCEPTION_DATA_INCLUDED_AS_STRING,
+            description = "Include the root cause of error in the GELF log message.")
+    boolean hasRootExceptionDataIncluded();
+
+    @Override
+    @ManagedAttribute(defaultValue = GelfEncoderDefaults.LOG_LEVEL_NAME_INCLUDED_AS_STRING,
+            description = "Include the log level in the GELF log message.")
+    boolean isLogLevelNameIncluded();
+
+    @Override
+    @ManagedAttribute(description = "Additional static fields for the GELF log message.")
+    Map<String, Object> getStaticFields();
+
+    AsyncAppender appender();
+
+    @ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Graylog Appender Buffer Size",
+            description = "The buffer size of the Broker Graylog appender.")
+    default int getAppenderBufferUsage()
+    {
+        final AsyncAppender appender = appender();
+        if (appender != null)
+        {
+            return appender.getQueueSize() - appender.getRemainingCapacity();
+        }
+        return 0;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java
new file mode 100644
index 0000000..8582cde
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.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.qpid.server.logging.logback;
+
+import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.VirtualHostLogger;
+
+@ManagedObject(category = false,
+        type = GraylogLogger.TYPE,
+        validChildTypes = "org.apache.qpid.server.logging.logback.AbstractLogger#getSupportedVirtualHostLoggerChildTypes()",
+        amqpName = "org.apache.qpid.VirtualHostGraylogLogger")
+public interface VirtualHostGraylogLogger<X extends VirtualHostGraylogLogger<X>> extends VirtualHostLogger<X>, GraylogLogger<X>
+{
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
new file mode 100644
index 0000000..2c07913
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
@@ -0,0 +1,224 @@
+/*
+ *
+ * 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.qpid.server.logging.logback;
+
+import ch.qos.logback.classic.AsyncAppender;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.Context;
+import org.apache.qpid.server.logging.logback.validator.GelfConfigurationValidator;
+import org.apache.qpid.server.model.ManagedAttributeField;
+import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
+import org.apache.qpid.server.model.VirtualHost;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public class VirtualHostGraylogLoggerImpl extends AbstractVirtualHostLogger<VirtualHostGraylogLoggerImpl> implements VirtualHostGraylogLogger<VirtualHostGraylogLoggerImpl>
+{
+    @ManagedAttributeField
+    private String _remoteHost;
+
+    @ManagedAttributeField
+    private int _port = GelfAppenderDefaults.PORT.value();
+
+    @ManagedAttributeField
+    private int _reconnectionInterval = GelfAppenderDefaults.RECONNECTION_INTERVAL.value();
+
+    @ManagedAttributeField
+    private int _connectionTimeout = GelfAppenderDefaults.CONNECTION_TIMEOUT.value();
+
+    @ManagedAttributeField
+    private int _maximumReconnectionAttempts = GelfAppenderDefaults.MAXIMUM_RECONNECTION_ATTEMPTS.value();
+
+    @ManagedAttributeField
+    private int _retryDelay = GelfAppenderDefaults.RETRY_DELAY.value();
+
+    @ManagedAttributeField
+    private int _messagesFlushTimeOut = GelfAppenderDefaults.MESSAGES_FLUSH_TIMEOUT.value();
+
+    @ManagedAttributeField
+    private int _messageBufferCapacity = GelfAppenderDefaults.MESSAGE_BUFFER_CAPACITY.value();
+
+    @ManagedAttributeField
+    private boolean _rawMessageIncluded = GelfEncoderDefaults.RAW_MESSAGE_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _eventMarkerIncluded = GelfEncoderDefaults.EVENT_MARKER_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _mdcPropertiesIncluded = GelfEncoderDefaults.MDC_PROPERTIES_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _callerDataIncluded = GelfEncoderDefaults.CALLER_DATA_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _rootExceptionDataIncluded = GelfEncoderDefaults.ROOT_EXCEPTION_DATA_INCLUDED.value();
+
+    @ManagedAttributeField
+    private boolean _logLevelNameIncluded = GelfEncoderDefaults.LOG_LEVEL_NAME_INCLUDED.value();
+
+    @ManagedAttributeField
+    private Map<String, Object> _staticFields = Collections.emptyMap();
+
+    @ManagedAttributeField
+    private String _messageOriginHost;
+
+    private AsyncAppender _appender;
+
+    @ManagedObjectFactoryConstructor
+    public VirtualHostGraylogLoggerImpl(Map<String, Object> attributes, VirtualHost<?> virtualHost)
+    {
+        super(attributes, virtualHost);
+    }
+
+    @Override
+    public String getRemoteHost()
+    {
+        return _remoteHost;
+    }
+
+    @Override
+    public int getPort()
+    {
+        return _port;
+    }
+
+    @Override
+    public int getReconnectionInterval()
+    {
+        return _reconnectionInterval;
+    }
+
+    @Override
+    public int getConnectionTimeout()
+    {
+        return _connectionTimeout;
+    }
+
+    @Override
+    public int getMaximumReconnectionAttempts()
+    {
+        return _maximumReconnectionAttempts;
+    }
+
+    @Override
+    public int getRetryDelay()
+    {
+        return _retryDelay;
+    }
+
+    @Override
+    public int getMessagesFlushTimeOut()
+    {
+        return _messagesFlushTimeOut;
+    }
+
+    @Override
+    public int getMessageBufferCapacity()
+    {
+        return _messageBufferCapacity;
+    }
+
+    @Override
+    public boolean isRawMessageIncluded()
+    {
+        return _rawMessageIncluded;
+    }
+
+    @Override
+    public boolean isEventMarkerIncluded()
+    {
+        return _eventMarkerIncluded;
+    }
+
+    @Override
+    public boolean hasMdcPropertiesIncluded()
+    {
+        return _mdcPropertiesIncluded;
+    }
+
+    @Override
+    public boolean isCallerDataIncluded()
+    {
+        return _callerDataIncluded;
+    }
+
+    @Override
+    public boolean hasRootExceptionDataIncluded()
+    {
+        return _rootExceptionDataIncluded;
+    }
+
+    @Override
+    public boolean isLogLevelNameIncluded()
+    {
+        return _logLevelNameIncluded;
+    }
+
+    @Override
+    public Map<String, Object> getStaticFields()
+    {
+        return _staticFields;
+    }
+
+    @Override
+    public String getMessageOriginHost()
+    {
+        return _messageOriginHost;
+    }
+
+    @Override
+    public AsyncAppender appender()
+    {
+        return _appender;
+    }
+
+    @Override
+    protected Appender<ILoggingEvent> createAppenderInstance(Context context)
+    {
+        _appender = GraylogAppender.newInstance(context, this);
+        return _appender;
+    }
+
+    @Override
+    protected void validateOnCreate()
+    {
+        super.validateOnCreate();
+        GelfConfigurationValidator.validateConfiguration(this, this);
+
+    }
+
+    @Override
+    protected void onOpen()
+    {
+        super.onOpen();
+        GelfConfigurationValidator.validateConfiguration(this, this);
+    }
+
+    @Override
+    protected void postSetAttributes(Set<String> actualUpdatedAttributes)
+    {
+        super.postSetAttributes(actualUpdatedAttributes);
+        GelfConfigurationValidator.validateConfiguration(this, this, actualUpdatedAttributes);
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/event/LoggingEvent.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/event/LoggingEvent.java
new file mode 100644
index 0000000..5bbfaff
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/event/LoggingEvent.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.event;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.LoggerContextVO;
+import org.apache.qpid.server.logging.CallerDataFilter;
+import org.apache.qpid.server.util.ArrayUtils;
+import org.slf4j.Marker;
+
+import java.util.Map;
+import java.util.Objects;
+
+public final class LoggingEvent implements ILoggingEvent
+{
+    private static final CallerDataFilter FILTER = new CallerDataFilter();
+
+    private final ILoggingEvent _event;
+
+    private StackTraceElement[] _callerData = null;
+
+    public static ILoggingEvent wrap(ILoggingEvent event)
+    {
+        return event != null ? new LoggingEvent(event) : null;
+    }
+
+    private LoggingEvent(ILoggingEvent event)
+    {
+        _event = Objects.requireNonNull(event);
+    }
+
+    @Override
+    public String getThreadName()
+    {
+        return _event.getThreadName();
+    }
+
+    @Override
+    public Level getLevel()
+    {
+        return _event.getLevel();
+    }
+
+    @Override
+    public String getMessage()
+    {
+        return _event.getMessage();
+    }
+
+    @Override
+    public Object[] getArgumentArray()
+    {
+        return _event.getArgumentArray();
+    }
+
+    @Override
+    public String getFormattedMessage()
+    {
+        return _event.getFormattedMessage();
+    }
+
+    @Override
+    public String getLoggerName()
+    {
+        return _event.getLoggerName();
+    }
+
+    @Override
+    public LoggerContextVO getLoggerContextVO()
+    {
+        return _event.getLoggerContextVO();
+    }
+
+    @Override
+    public IThrowableProxy getThrowableProxy()
+    {
+        return _event.getThrowableProxy();
+    }
+
+    @Override
+    public StackTraceElement[] getCallerData()
+    {
+        if (_callerData == null)
+        {
+            _callerData = FILTER.filter(_event.getCallerData());
+        }
+        return ArrayUtils.clone(_callerData);
+    }
+
+    @Override
+    public boolean hasCallerData()
+    {
+        return !ArrayUtils.isEmpty(getCallerData());
+    }
+
+    @Override
+    public Marker getMarker()
+    {
+        return _event.getMarker();
+    }
+
+    @Override
+    public Map<String, String> getMDCPropertyMap()
+    {
+        return _event.getMDCPropertyMap();
+    }
+
+    /**
+     * @deprecated getMDCPropertyMap method should be used instead.
+     */
+    @Deprecated
+    @Override
+    public Map<String, String> getMdc()
+    {
+        return _event.getMdc();
+    }
+
+    @Override
+    public long getTimeStamp()
+    {
+        return _event.getTimeStamp();
+    }
+
+    @Override
+    public void prepareForDeferredProcessing()
+    {
+        _event.prepareForDeferredProcessing();
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeast.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeast.java
new file mode 100644
index 0000000..4db18bd
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeast.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+import java.util.function.Predicate;
+
+public class AtLeast implements Validator<Integer>, Predicate<Integer>
+{
+    private final int _min;
+
+    AtLeast(int min)
+    {
+        this._min = min;
+    }
+
+    @Override
+    public boolean test(Integer value)
+    {
+        return value != null && value >= _min;
+    }
+
+    @Override
+    public void validate(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        if (!test(value))
+        {
+            throw new IllegalConfigurationException(errorMessage(value, object, attribute));
+        }
+    }
+
+    private String errorMessage(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        return "Attribute '" + attribute
+                + "' instance of " + object.getClass().getName()
+                + " named '" + object.getName() + "'"
+                + " cannot have value '" + value + "'"
+                + " as it has to be at least " + minimum();
+    }
+
+    int minimum()
+    {
+        return _min;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastOne.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastOne.java
new file mode 100644
index 0000000..8f4da2d
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastOne.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public final class AtLeastOne extends AtLeast
+{
+    private static final AtLeastOne VALIDATOR = new AtLeastOne();
+
+    public static Validator<Integer> validator()
+    {
+        return VALIDATOR;
+    }
+
+    public static void validateValue(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        validator().validate(value, object, attribute);
+    }
+
+    private AtLeastOne()
+    {
+        super(1);
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastZero.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastZero.java
new file mode 100644
index 0000000..d4ee704
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/AtLeastZero.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public final class AtLeastZero extends AtLeast
+{
+    private static final AtLeastZero VALIDATOR = new AtLeastZero();
+
+    public static Validator<Integer> validator()
+    {
+        return VALIDATOR;
+    }
+
+    public static void validateValue(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        validator().validate(value, object, attribute);
+    }
+
+    private AtLeastZero()
+    {
+        super(0);
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java
new file mode 100644
index 0000000..2eb0d37
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.logging.logback.GelfAppenderConfiguration;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+import java.util.Arrays;
+import java.util.Set;
+
+public enum GelfConfigurationValidator
+{
+    PORT("port")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    Port.validatePort(configuration.getPort(), object, attributeName());
+                }
+            },
+    RECONNECTION_INTERVAL("reconnectionInterval")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    AtLeastZero.validateValue(configuration.getReconnectionInterval(), object, attributeName());
+                }
+            },
+    CONNECTION_TIMEOUT("connectionTimeout")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    AtLeastZero.validateValue(configuration.getConnectionTimeout(), object, attributeName());
+                }
+            },
+    MAXIMUM_RECONNECTION_ATTEMPTS("maximumReconnectionAttempts")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    AtLeastZero.validateValue(configuration.getMaximumReconnectionAttempts(), object, attributeName());
+                }
+            },
+    RETRY_DELAY("retryDelay")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    AtLeastZero.validateValue(configuration.getRetryDelay(), object, attributeName());
+                }
+            },
+    BUFFER_CAPACITY("messageBufferCapacity")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    AtLeastOne.validateValue(configuration.getMessageBufferCapacity(), object, attributeName());
+                }
+            },
+    FLUSH_TIME_OUT("messagesFlushTimeOut")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    AtLeastZero.validateValue(configuration.getMessagesFlushTimeOut(), object, attributeName());
+                }
+            },
+    STATIC_FIELDS("staticFields")
+            {
+                @Override
+                public void validate(GelfAppenderConfiguration configuration, ConfiguredObject<?> object)
+                {
+                    GelfMessageStaticFields.validateStaticFields(configuration.getStaticFields(), object, attributeName());
+                }
+            };
+
+    private final String _attributeName;
+
+    public abstract void validate(GelfAppenderConfiguration logger, ConfiguredObject<?> object);
+
+    public static void validateConfiguration(final GelfAppenderConfiguration logger, final ConfiguredObject<?> object)
+    {
+        Arrays.asList(values()).forEach(validator -> validator.validate(logger, object));
+    }
+
+    public static void validateConfiguration(final GelfAppenderConfiguration logger, final ConfiguredObject<?> object, Set<String> changedAttributes)
+    {
+        Arrays.stream(values()).filter(validator -> changedAttributes.contains(validator.attributeName())).forEach(validator -> validator.validate(logger, object));
+    }
+
+    GelfConfigurationValidator(String name)
+    {
+        _attributeName = name;
+    }
+
+    public String attributeName()
+    {
+        return _attributeName;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFields.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFields.java
new file mode 100644
index 0000000..1ba7c56
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFields.java
@@ -0,0 +1,143 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+
+public final class GelfMessageStaticFields implements Validator<Map<String, Object>>
+{
+    private static class Key implements Validator<String>, Predicate<String>
+    {
+        private static final Pattern PATTERN = Pattern.compile("[\\w\\.\\-]+");
+
+        Key()
+        {
+            super();
+        }
+
+        @Override
+        public boolean test(String value)
+        {
+            return value != null && PATTERN.matcher(value).matches();
+        }
+
+        @Override
+        public void validate(String value, ConfiguredObject<?> object, String attribute)
+        {
+            if (!test(value))
+            {
+                throw new IllegalConfigurationException(errorMessage(value, object, attribute));
+            }
+        }
+
+        private String errorMessage(String value, ConfiguredObject<?> object, String attribute)
+        {
+            return "Key of '" + attribute + " attribute"
+                    + " instance of " + object.getClass().getName()
+                    + " named '" + object.getName() + "'"
+                    + " cannot be '" + value + "'."
+                    + " Key pattern is: " + PATTERN.pattern();
+        }
+    }
+
+    private static final class Value implements Validator<Object>, Predicate<Object>
+    {
+        Value()
+        {
+            super();
+        }
+
+        @Override
+        public boolean test(Object value)
+        {
+            return value instanceof String || value instanceof Number;
+        }
+
+        @Override
+        public void validate(Object value, ConfiguredObject<?> object, String attribute)
+        {
+            if (!test(value))
+            {
+                throw new IllegalConfigurationException(errorMessage(value, object, attribute));
+            }
+        }
+
+        private String errorMessage(Object value, ConfiguredObject<?> object, String attribute)
+        {
+            return "Value of '" + attribute + " attribute"
+                    + " instance of " + object.getClass().getName()
+                    + " named '" + object.getName() + "'"
+                    + " cannot be '" + value + "',"
+                    + " as it has to be a string or number";
+        }
+    }
+
+    private static final Key KEY = new Key();
+
+    private static final Value VALUE = new Value();
+
+    private static final GelfMessageStaticFields VALIDATOR = new GelfMessageStaticFields();
+
+    public static Validator<Map<String, Object>> validator()
+    {
+        return VALIDATOR;
+    }
+
+    public static void validateStaticFields(Map<String, Object> value, ConfiguredObject<?> object, String attribute)
+    {
+        validator().validate(value, object, attribute);
+    }
+
+    private GelfMessageStaticFields()
+    {
+        super();
+    }
+
+    @Override
+    public void validate(Map<String, Object> map, final ConfiguredObject<?> object, final String attribute)
+    {
+        if (map == null) {
+            throw new IllegalConfigurationException(nullErrorMessage(object, attribute));
+        }
+        map.entrySet().forEach(entry -> validateMapEntry(entry, object, attribute));
+    }
+
+    private String nullErrorMessage(ConfiguredObject<?> object, String attribute)
+    {
+        return "Attribute '" + attribute
+                + " instance of " + object.getClass().getName()
+                + " named '" + object.getName() + "'"
+                + " cannot be 'null'";
+    }
+
+    private void validateMapEntry(Entry<String, Object> entry, ConfiguredObject<?> object, String attribute)
+    {
+        KEY.validate(entry.getKey(), object, attribute);
+        VALUE.validate(entry.getValue(), object, attribute);
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Port.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Port.java
new file mode 100644
index 0000000..4a30b7b
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Port.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+import java.util.function.Predicate;
+
+public final class Port implements Validator<Integer>, Predicate<Integer>
+{
+    private static final Port VALIDATOR = new Port();
+
+    public static Validator<Integer> validator()
+    {
+        return VALIDATOR;
+    }
+
+    public static void validatePort(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        validator().validate(value, object, attribute);
+    }
+
+    private Port()
+    {
+        super();
+    }
+
+    @Override
+    public boolean test(Integer value)
+    {
+        return value != null && value >= 1 && value <= 65535;
+    }
+
+    @Override
+    public void validate(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        if (!test(value))
+        {
+            throw new IllegalConfigurationException(errorMessage(value, object, attribute));
+        }
+    }
+
+    private String errorMessage(Integer value, ConfiguredObject<?> object, String attribute)
+    {
+        return "Attribute '" + attribute + "' instance of " + object.getClass().getName()
+                + " named '" + object.getName() + "'"
+                + " cannot have value '" + value + "'"
+                + " as it has to be in range [1, 65535]";
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Validator.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Validator.java
new file mode 100644
index 0000000..89e09ac
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/Validator.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+@FunctionalInterface
+public interface Validator<T>
+{
+    void validate(T value, ConfiguredObject<?> object, String attribute);
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/util/ArrayUtils.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/util/ArrayUtils.java
new file mode 100644
index 0000000..b26e439
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/util/ArrayUtils.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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.qpid.server.util;
+
+public final class ArrayUtils
+{
+    private ArrayUtils()
+    {
+        super();
+    }
+
+    public static <T> T[] clone(T[] array) {
+        return array != null ? array.clone() : null;
+    }
+
+    public static boolean isEmpty(Object[] array) {
+        return array == null || array.length == 0;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/add.js b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/add.js
new file mode 100644
index 0000000..a9015f8
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/add.js
@@ -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.
+ *
+ */
+define(["dojo/dom",
+        "dojo/query",
+        "dojo/_base/array",
+        "dijit/registry",
+        "qpid/common/util",
+        "dojo/parser",
+        "dojo/text!logger/graylog/add.html",
+        "dojo/text!logger/graylog/showStaticField.html",
+        "qpid/common/MapInputWidget",
+        "dojo/domReady!"],
+    function (dom, query, array, registry, util, parser, template)
+    {
+        return {
+            show: function (data)
+            {
+                data.containerNode.innerHTML = template;
+                return parser.parse(data.containerNode);
+            },
+            doNotScroll: function (containerNode)
+            {
+                const classNameToRemove = "mapList-scroll-y";
+                util.findNode(classNameToRemove, containerNode).forEach(function (node)
+                {
+                    if (node.classList) {
+                        node.classList.remove(classNameToRemove);
+                    }
+                });
+            }
+        };
+    });
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/show.js b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/show.js
new file mode 100644
index 0000000..fa42eef
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/brokerlogger/graylog/show.js
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+define(["qpid/common/util",
+        "dojo/text!logger/graylog/show.html",
+        "dojo/text!logger/graylog/showStaticField.html",
+        "qpid/common/TypeTabExtension",
+        "dojo/domReady!"],
+    function (util, template, fieldTemplate, TypeTabExtension)
+    {
+        function Graylog(params)
+        {
+            const type = "Graylog";
+            const category = "BrokerLogger";
+
+            this.containerNode = params.containerNode;
+
+            TypeTabExtension.call(this,
+                params.containerNode,
+                template,
+                category,
+                type,
+                params.metadata,
+                params.data);
+
+            this.appenderBufferUsage = util.findNode("appenderBufferUsage", params.containerNode);
+        }
+
+        util.extend(Graylog, TypeTabExtension);
+
+        Graylog.prototype.update = function (restData)
+        {
+            util.updateAttributeNodes(this.attributeContainers, restData, util.updateBooleanAttributeNode,
+                (containerObject, data, utl) => util.updateMapAttributeNode(containerObject, data, utl, fieldTemplate));
+            const bufferUsage = String(restData["statistics"]["appenderBufferUsage"]);
+            this.appenderBufferUsage.forEach(node => node.innerHTML = bufferUsage);
+        }
+
+        return Graylog;
+    });
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/add.js b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/add.js
new file mode 100644
index 0000000..a9015f8
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/add.js
@@ -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.
+ *
+ */
+define(["dojo/dom",
+        "dojo/query",
+        "dojo/_base/array",
+        "dijit/registry",
+        "qpid/common/util",
+        "dojo/parser",
+        "dojo/text!logger/graylog/add.html",
+        "dojo/text!logger/graylog/showStaticField.html",
+        "qpid/common/MapInputWidget",
+        "dojo/domReady!"],
+    function (dom, query, array, registry, util, parser, template)
+    {
+        return {
+            show: function (data)
+            {
+                data.containerNode.innerHTML = template;
+                return parser.parse(data.containerNode);
+            },
+            doNotScroll: function (containerNode)
+            {
+                const classNameToRemove = "mapList-scroll-y";
+                util.findNode(classNameToRemove, containerNode).forEach(function (node)
+                {
+                    if (node.classList) {
+                        node.classList.remove(classNameToRemove);
+                    }
+                });
+            }
+        };
+    });
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/show.js b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/show.js
new file mode 100644
index 0000000..cdfef05
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/js/qpid/management/logger/virtualhostlogger/graylog/show.js
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+define(["qpid/common/util",
+        "dojo/text!logger/graylog/show.html",
+        "dojo/text!logger/graylog/showStaticField.html",
+        "qpid/common/TypeTabExtension",
+        "dojo/domReady!"],
+    function (util, template, fieldTemplate, TypeTabExtension)
+    {
+        function Graylog(params)
+        {
+            const type = "Graylog";
+            const category = "VirtualHostLogger";
+
+            this.containerNode = params.containerNode;
+
+            TypeTabExtension.call(this,
+                params.containerNode,
+                template,
+                category,
+                type,
+                params.metadata,
+                params.data);
+
+            this.appenderBufferUsage = util.findNode("appenderBufferUsage", params.containerNode);
+        }
+
+        util.extend(Graylog, TypeTabExtension);
+
+        Graylog.prototype.update = function (restData)
+        {
+            util.updateAttributeNodes(this.attributeContainers, restData, util.updateBooleanAttributeNode,
+                (containerObject, data, utl) => util.updateMapAttributeNode(containerObject, data, utl, fieldTemplate));
+            const bufferUsage = String(restData["statistics"]["appenderBufferUsage"]);
+            this.appenderBufferUsage.forEach(node => node.innerHTML = bufferUsage);
+        }
+
+        return Graylog;
+    });
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/add.html b/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/add.html
new file mode 100644
index 0000000..b63a001
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/add.html
@@ -0,0 +1,305 @@
+<!--
+  ~ 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.
+  ~
+  -->
+
+<div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>Connection Options</legend>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Remote Host*:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.remoteHost"
+                           data-dojo-type="dijit/form/ValidationTextBox"
+                           data-dojo-props="
+                              name: 'remoteHost',
+                              required: true,
+                              placeHolder: '',
+                              promptMessage: 'Enter the host where the Graylog server is running',
+                              title: 'Enter the host where the Graylog server is running'"/>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Port:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.port"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'port',
+                              placeHolder: '12201',
+                              promptMessage: 'Enter the port number on which the Graylog server runs',
+                              title: 'Port number on which the Graylog server runs',
+                              constraints:{min:1,max:65535,places:0},
+                              invalidMessage:'Please enter a port number in range [1,65535]',
+                              rangeMessage:'Insert a integer in the range [1,65535]'"/>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Reconnection interval [ms]:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.reconnectionInterval"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'reconnectionInterval',
+                              placeHolder: '60000',
+                              promptMessage: 'Enter the time period of connection periodical reset',
+                              title: 'Time period of connection periodical reset',
+                              constraints:{min:0,max:2147483647,places:0},
+                              invalidMessage:'Please enter a reconnection interval',
+                              rangeMessage:'Insert zero or a positive integer'"/>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Connection timeout [ms]:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.connectionTimeout"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'connectionTimeout',
+                              placeHolder: '15000',
+                              promptMessage: 'Enter the connection timeout',
+                              title: 'Connection timeout',
+                              constraints:{min:0,max:2147483647,places:0},
+                              invalidMessage:'Please enter a connection timeout',
+                              rangeMessage:'Insert zero (infinity) or a positive integer'"/>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Maximum reconnection attempts:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.maximumReconnectionAttempts"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'maximumReconnectionAttempts',
+                              placeHolder: '2',
+                              promptMessage: 'Enter how many times can the client try to reconnect',
+                              title: 'Maximum reconnection attempts',
+                              constraints:{min:0,max:2147483647,places:0},
+                              invalidMessage:'Please enter a maximum reconnection attempts',
+                              rangeMessage:'Insert zero or a positive integer'"/>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Retry delay [ms]:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.retryDelay"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'retryDelay',
+                              placeHolder: '3000',
+                              promptMessage: 'Enter delay between reconnection attempts',
+                              title: 'Retry delay',
+                              constraints:{min:0,max:2147483647,places:0},
+                              invalidMessage:'Please enter a retry delay',
+                              rangeMessage:'Insert zero or a positive integer'"/>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>Message Buffer Options</legend>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Capacity:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.messageBufferCapacity"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'messageBufferCapacity',
+                              placeHolder: '256',
+                              promptMessage: 'Enter the capacity of the message buffer',
+                              title: 'Message buffer capacity',
+                              constraints:{min:1,max:2147483647,places:0},
+                              invalidMessage:'Please enter a message buffer capacity',
+                              rangeMessage:'Insert a positive integer'"/>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>Logger Stopping</legend>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Messages flush timeout [ms]:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.messagesFlushTimeOut"
+                           data-dojo-type="dijit/form/NumberTextBox"
+                           data-dojo-props="
+                              name: 'messagesFlushTimeOut',
+                              placeHolder: '1000',
+                              promptMessage: 'Enter the timeout of the flushing of remaining messages in the buffer at the logger stop',
+                              title: 'Messages flush timeout',
+                              constraints:{min:0,max:2147483647,places:0},
+                              invalidMessage:'Please enter a messages flush timeout',
+                              rangeMessage:'Insert zero (infinity) or a positive integer'"/>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>GELF encoder options</legend>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Message origin host*:</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="text" id="addLogger.messageOriginHost"
+                           data-dojo-type="dijit/form/ValidationTextBox"
+                           data-dojo-props="
+                              name: 'messageOriginHost',
+                              required: true,
+                              placeHolder: 'hostname',
+                              promptMessage: 'Enter the origin host of the GELF log message',
+                              title: 'Enter the origin host of the GELF log message'"/>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Include raw message</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="checkbox" id="addLogger.rawMessageIncluded"
+                           data-dojo-type="dijit/form/CheckBox"
+                           data-dojo-props="
+                                  name: 'rawMessageIncluded',
+                                  required: false,
+                                  checked: false"/>
+                    <div data-dojo-type="dijit/Tooltip"
+                         data-dojo-props="connectId: ['addLogger.rawMessageIncluded'],
+                                      label: 'If selected, the raw text of exception is included in the GELF log message'">
+                    </div>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Include event marker</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="checkbox" id="addLogger.eventMarkerIncluded"
+                           data-dojo-type="dijit/form/CheckBox"
+                           data-dojo-props="
+                                  name: 'eventMarkerIncluded',
+                                  required: false,
+                                  checked: true"/>
+                    <div data-dojo-type="dijit/Tooltip"
+                         data-dojo-props="connectId: ['addLogger.eventMarkerIncluded'],
+                                      label: 'If selected, the event marker is included in the GELF log message'">
+                    </div>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Include MDC properties</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="checkbox" id="addLogger.mdcPropertiesIncluded"
+                           data-dojo-type="dijit/form/CheckBox"
+                           data-dojo-props="
+                                  name: 'mdcPropertiesIncluded',
+                                  required: false,
+                                  checked: true"/>
+                    <div data-dojo-type="dijit/Tooltip"
+                         data-dojo-props="connectId: ['addLogger.mdcPropertiesIncluded'],
+                                      label: 'If selected, the MDC properties are included in the GELF log message'">
+                    </div>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Include caller data</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="checkbox" id="addLogger.callerDataIncluded"
+                           data-dojo-type="dijit/form/CheckBox"
+                           data-dojo-props="
+                                  name: 'callerDataIncluded',
+                                  required: false,
+                                  checked: false"/>
+                    <div data-dojo-type="dijit/Tooltip"
+                         data-dojo-props="connectId: ['addLogger.callerDataIncluded'],
+                                      label: 'If selected, the caller data are included in the GELF log message'">
+                    </div>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Include root exception data</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="checkbox" id="addLogger.rootExceptionDataIncluded"
+                           data-dojo-type="dijit/form/CheckBox"
+                           data-dojo-props="
+                                  name: 'rootExceptionDataIncluded',
+                                  required: false,
+                                  checked: false"/>
+                    <div data-dojo-type="dijit/Tooltip"
+                         data-dojo-props="connectId: ['addLogger.rootExceptionDataIncluded'],
+                                      label: 'If selected, the root exception data are included in the GELF log message'">
+                    </div>
+                </div>
+            </div>
+            <div class="clear">
+                <div class="formLabel-labelCell tableContainer-labelCell">Include log level name</div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <input type="checkbox" id="addLogger.logLevelNameIncluded"
+                           data-dojo-type="dijit/form/CheckBox"
+                           data-dojo-props="
+                                  name: 'logLevelNameIncluded',
+                                  required: false,
+                                  checked: false"/>
+                    <div data-dojo-type="dijit/Tooltip"
+                         data-dojo-props="connectId: ['addLogger.logLevelNameIncluded'],
+                                      label: 'If selected, the log level name is included in the GELF log message'">
+                    </div>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>GELF Log Message Static Fields</legend>
+            <div class="clear" id="addLogger.staticFields"
+                 data-dojo-type="qpid/common/MapInputWidget"
+                 data-dojo-props="
+                    name: 'staticFields',
+                    keyValueTemplate: 'logger/Graylog/showStaticField.html'">
+                <div class="formLabel-labelCell tableContainer-labelCell">
+                    <label for="addLogger.staticFields.key">Name (Key):</label><br>
+                    <input type="text" id="addLogger.staticFields.key"
+                           data-dojo-type="dijit/form/ValidationTextBox"
+                           data-dojo-props="
+                              name: 'staticFields.key',
+                              placeHolder: '',
+                              promptMessage: 'Enter the static field name',
+                              title: 'Name (Key)',
+                              regExp: '[\\w\\.\\-]+',
+                              invalidMessage: 'Name/key has to obey the schema: [\\w\\.\\-]+'"/>
+                </div>
+                <div class="formLabel-controlCell tableContainer-valueCell">
+                    <label for="addLogger.staticFields.value">Value:</label><br>
+                    <input type="text" id="addLogger.staticFields.value"
+                           data-dojo-type="dijit/form/ValidationTextBox"
+                           data-dojo-props="
+                              name: 'staticFields.value',
+                              placeHolder: '',
+                              promptMessage: 'Enter the static field value',
+                              title: 'Value'"/>
+                </div>
+                <div class="clear">
+                    <button data-dojo-type="dijit/form/Button" id="addLogger.staticFields.insertButton"
+                            data-dojo-props="label: 'Insert'" type="submit">Insert
+                    </button>
+                    <button data-dojo-type="dijit/form/Button" id="addLogger.staticFields.clearButton"
+                            data-dojo-props="label: 'Clear'" type="reset">Clear
+                    </button>
+                </div>
+                <div class="keyValueList clear mapList-scroll-y"></div>
+            </div>
+        </fieldset>
+    </div>
+</div>
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/show.html b/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/show.html
new file mode 100644
index 0000000..6de2af0
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/show.html
@@ -0,0 +1,135 @@
+<!--
+  ~ 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.
+  ~
+  -->
+<div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>Connection Options</legend>
+            <div class="alignLeft">
+                <div class="clear">
+                    <div class="formLabel-labelCell">Remote Host:</div>
+                    <div class="remoteHost formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Port:</div>
+                    <div class="port formValue-valueCell"></div>
+                </div>
+            </div>
+            <div class="alignRight">
+                <div class="clear">
+                    <div class="formLabel-labelCell">Reconnection interval:</div>
+                    <div class="formValue-valueCell">
+                        <span class="reconnectionInterval"></span>
+                        <span>ms</span>
+                    </div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Connection Timeout:</div>
+                    <div class="formValue-valueCell">
+                        <span class="connectionTimeout"></span>
+                        <span>ms</span>
+                    </div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Maximum connection attempts:</div>
+                    <div class="maximumReconnectionAttempts formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Connection retry delay:</div>
+                    <div class="formValue-valueCell">
+                        <span class="retryDelay"></span>
+                        <span>ms</span>
+                    </div>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>Message Buffer Options</legend>
+            <div class="alignLeft">
+                <div class="clear">
+                    <div class="formLabel-labelCell">Capacity:</div>
+                    <div class="messageBufferCapacity formValue-valueCell"></div>
+                </div>
+            </div>
+            <div class="alignRight">
+                <div class="clear">
+                    <div class="formLabel-labelCell">Current buffer usage:</div>
+                    <div class="appenderBufferUsage formValue-valueCell"></div>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>Logger Stopping</legend>
+            <div class="clear">
+                <div class="formLabel-labelCell">Messages flush time out:</div>
+                <div class="formValue-valueCell">
+                    <span class="messagesFlushTimeOut"></span>
+                    <span>ms</span>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>GELF Log Message Encoder Options</legend>
+            <div class="alignLeft">
+                <div class="clear">
+                    <div class="formLabel-labelCell">Message origin host:</div>
+                    <div class="messageOriginHost formValue-valueCell"></div>
+                </div>
+            </div>
+            <div class="alignRight">
+                <div class="clear">
+                    <div class="formLabel-labelCell">Raw message included:</div>
+                    <div class="rawMessageIncluded formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Event marker included:</div>
+                    <div class="eventMarkerIncluded formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">MDC properties included:</div>
+                    <div class="mdcPropertiesIncluded formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Caller data included:</div>
+                    <div class="callerDataIncluded formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Root exception data included:</div>
+                    <div class="rootExceptionDataIncluded formValue-valueCell"></div>
+                </div>
+                <div class="clear">
+                    <div class="formLabel-labelCell">Log level name included:</div>
+                    <div class="logLevelNameIncluded formValue-valueCell"></div>
+                </div>
+            </div>
+        </fieldset>
+    </div>
+    <div class="formBox clear">
+        <fieldset>
+            <legend>GELF Log Message Static Fields</legend>
+            <div class="staticFields clear"></div>
+        </fieldset>
+    </div>
+</div>
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/showStaticField.html b/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/showStaticField.html
new file mode 100644
index 0000000..1bbb8f7
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/resources/logger/graylog/showStaticField.html
@@ -0,0 +1,4 @@
+<div class="clear">
+    <div class="key formLabel-labelCell">:</div>
+    <div class="value formValue-valueCell"></div>
+</div>
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/CallerDataFilterTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/CallerDataFilterTest.java
new file mode 100644
index 0000000..b604b8c
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/CallerDataFilterTest.java
@@ -0,0 +1,167 @@
+/*
+ *
+ * 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.qpid.server.logging;
+
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.MessageLogger;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class CallerDataFilterTest extends UnitTestBase
+{
+    public static class CallerDataTestLogger implements MessageLogger
+    {
+        private StackTraceElement[] _stackTraceElements;
+
+        @Override
+        public boolean isEnabled()
+        {
+            catchStackTrace();
+            return false;
+        }
+
+        @Override
+        public boolean isMessageEnabled(String logHierarchy)
+        {
+            catchStackTrace();
+            return false;
+        }
+
+        @Override
+        public void message(LogMessage message)
+        {
+            catchStackTrace();
+        }
+
+        @Override
+        public void message(LogSubject subject, LogMessage message)
+        {
+            catchStackTrace();
+        }
+
+        private void catchStackTrace()
+        {
+            _stackTraceElements = Thread.currentThread().getStackTrace();
+        }
+
+        public StackTraceElement[] getStackTrace()
+        {
+            return _stackTraceElements;
+        }
+    }
+
+    private CallerDataFilter _filter;
+    private CallerDataTestLogger _logger;
+
+    @Before
+    public void setUp()
+    {
+        _filter = new CallerDataFilter();
+        _logger = new CallerDataTestLogger();
+    }
+
+    @Test
+    public void testFilter_nullAsInput()
+    {
+        StackTraceElement[] result = _filter.filter(null);
+        assertNotNull(result);
+        assertEquals(0, result.length);
+    }
+
+    @Test
+    public void testFilter_emptyInput()
+    {
+        StackTraceElement[] result = _filter.filter(new StackTraceElement[0]);
+        assertNotNull(result);
+        assertEquals(0, result.length);
+    }
+
+    @Test
+    public void testFilter_withoutLogger()
+    {
+        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+        StackTraceElement[] result = _filter.filter(stackTrace);
+        assertNotNull(result);
+
+        assertTrue(Arrays.deepEquals(stackTrace, result));
+    }
+
+    @Test
+    public void testFilter_withLogger()
+    {
+        _logger.message(() -> "ClassName");
+        StackTraceElement[] result = _filter.filter(_logger.getStackTrace());
+        assertNotNull(result);
+
+        final String loggerName = _logger.getClass().getName();
+        assertFalse(Arrays.stream(result).anyMatch(e -> e.getClassName().contains(loggerName)));
+    }
+
+    @Test
+    public void testFilter_withLogger_InvalidMethod()
+    {
+        _logger.isEnabled();
+        StackTraceElement[] stackTrace = _logger.getStackTrace();
+        StackTraceElement[] result = _filter.filter(_logger.getStackTrace());
+        assertNotNull(result);
+
+        assertTrue(Arrays.deepEquals(stackTrace, result));
+    }
+
+    @Test
+    public void testFilter_withLoggerOnly()
+    {
+        _logger.message(() -> "ClassName");
+        final String loggerName = _logger.getClass().getName();
+        StackTraceElement[] stackTrace = Arrays.stream(_logger.getStackTrace())
+                .filter(e -> e.getClassName().contains(loggerName))
+                .toArray(StackTraceElement[]::new);
+
+        StackTraceElement[] result = _filter.filter(stackTrace);
+        assertNotNull(result);
+
+        assertTrue(Arrays.deepEquals(stackTrace, result));
+    }
+
+    @Test
+    public void testFilter_withUnknownClass()
+    {
+        StackTraceElement element1 = new StackTraceElement("unknown_class_xyz", "message", "file", 7);
+        StackTraceElement element2 = new StackTraceElement("unknown_class_xyz", "message", "file", 17);
+
+        final StackTraceElement[] stackTrace = {element1, element2};
+        StackTraceElement[] result = _filter.filter(stackTrace);
+        assertNotNull(result);
+
+        assertTrue(Arrays.deepEquals(stackTrace, result));
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/GraylogAppenderTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/GraylogAppenderTest.java
new file mode 100644
index 0000000..97b6e46
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/GraylogAppenderTest.java
@@ -0,0 +1,932 @@
+/*
+ * 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.qpid.server.logging.logback;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.Context;
+import de.siegmar.logbackgelf.GelfEncoder;
+import de.siegmar.logbackgelf.GelfTcpAppender;
+import org.apache.qpid.server.logging.logback.event.TestLoggingEvent;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class GraylogAppenderTest extends UnitTestBase
+{
+    static class TestGelfAppenderConfiguration implements GelfAppenderConfiguration
+    {
+        private String _remoteHost = "localhost";
+
+        private int _port = 12201;
+
+        private int _reconnectionInterval = 10000;
+
+        private int _connectionTimeout = 300;
+
+        private int _maximumReconnectionAttempts = 1;
+
+        private int _retryDelay = 500;
+
+        private int _messagesFlushTimeOut = 10000;
+
+        private int _messageBufferCapacity = 10000;
+
+        private String _messageOriginHost = "BrokerJ";
+
+        private boolean _rawMessageIncluded = false;
+
+        private boolean _eventMarkerIncluded = false;
+
+        private boolean _mdcPropertiesIncluded = false;
+
+        private boolean _callerDataIncluded = false;
+
+        private boolean _rootExceptionDataIncluded = false;
+
+        private boolean _logLevelNameIncluded = false;
+
+        private final Map<String, Object> _staticFields = new LinkedHashMap<>();
+
+        public TestGelfAppenderConfiguration()
+        {
+            super();
+        }
+
+        @Override
+        public String getRemoteHost()
+        {
+            return _remoteHost;
+        }
+
+        public TestGelfAppenderConfiguration withRemoteHost(String remoteHost)
+        {
+            this._remoteHost = remoteHost;
+            return this;
+        }
+
+        @Override
+        public int getPort()
+        {
+            return _port;
+        }
+
+        public TestGelfAppenderConfiguration withPort(int port)
+        {
+            this._port = port;
+            return this;
+        }
+
+        @Override
+        public int getReconnectionInterval()
+        {
+            return _reconnectionInterval;
+        }
+
+        public TestGelfAppenderConfiguration withReconnectionInterval(int reconnectionInterval)
+        {
+            this._reconnectionInterval = reconnectionInterval;
+            return this;
+        }
+
+        @Override
+        public int getConnectionTimeout()
+        {
+            return _connectionTimeout;
+        }
+
+        public TestGelfAppenderConfiguration withConnectionTimeout(int connectionTimeout)
+        {
+            this._connectionTimeout = connectionTimeout;
+            return this;
+        }
+
+        @Override
+        public int getMaximumReconnectionAttempts()
+        {
+            return _maximumReconnectionAttempts;
+        }
+
+        public TestGelfAppenderConfiguration withMaximumReconnectionAttempts(int maximumReconnectionAttempts)
+        {
+            this._maximumReconnectionAttempts = maximumReconnectionAttempts;
+            return this;
+        }
+
+        @Override
+        public int getRetryDelay()
+        {
+            return _retryDelay;
+        }
+
+        public TestGelfAppenderConfiguration withRetryDelay(int retryDelay)
+        {
+            this._retryDelay = retryDelay;
+            return this;
+        }
+
+        @Override
+        public int getMessagesFlushTimeOut()
+        {
+            return _messagesFlushTimeOut;
+        }
+
+        public TestGelfAppenderConfiguration withMessagesFlushTimeOut(int messagesFlushTimeOut)
+        {
+            this._messagesFlushTimeOut = messagesFlushTimeOut;
+            return this;
+        }
+
+        @Override
+        public int getMessageBufferCapacity()
+        {
+            return _messageBufferCapacity;
+        }
+
+        public TestGelfAppenderConfiguration withMessageBufferCapacity(int messageBufferCapacity)
+        {
+            this._messageBufferCapacity = messageBufferCapacity;
+            return this;
+        }
+
+        @Override
+        public String getMessageOriginHost()
+        {
+            return _messageOriginHost;
+        }
+
+        public TestGelfAppenderConfiguration withMessageOriginHost(String messageOriginHost)
+        {
+            this._messageOriginHost = messageOriginHost;
+            return this;
+        }
+
+        @Override
+        public boolean isRawMessageIncluded()
+        {
+            return _rawMessageIncluded;
+        }
+
+        public TestGelfAppenderConfiguration withRawMessageIncluded(boolean rawMessageIncluded)
+        {
+            this._rawMessageIncluded = rawMessageIncluded;
+            return this;
+        }
+
+        @Override
+        public boolean isEventMarkerIncluded()
+        {
+            return _eventMarkerIncluded;
+        }
+
+        public TestGelfAppenderConfiguration withEventMarkerIncluded(boolean eventMarkerIncluded)
+        {
+            this._eventMarkerIncluded = eventMarkerIncluded;
+            return this;
+        }
+
+        @Override
+        public boolean hasMdcPropertiesIncluded()
+        {
+            return _mdcPropertiesIncluded;
+        }
+
+        public TestGelfAppenderConfiguration withMdcPropertiesIncluded(boolean mdcPropertiesIncluded)
+        {
+            this._mdcPropertiesIncluded = mdcPropertiesIncluded;
+            return this;
+        }
+
+        @Override
+        public boolean isCallerDataIncluded()
+        {
+            return _callerDataIncluded;
+        }
+
+        public TestGelfAppenderConfiguration withCallerDataIncluded(boolean callerDataIncluded)
+        {
+            this._callerDataIncluded = callerDataIncluded;
+            return this;
+        }
+
+        @Override
+        public boolean hasRootExceptionDataIncluded()
+        {
+            return _rootExceptionDataIncluded;
+        }
+
+        public TestGelfAppenderConfiguration withRootExceptionDataIncluded(boolean rootExceptionDataIncluded)
+        {
+            this._rootExceptionDataIncluded = rootExceptionDataIncluded;
+            return this;
+        }
+
+        @Override
+        public boolean isLogLevelNameIncluded()
+        {
+            return _logLevelNameIncluded;
+        }
+
+        public TestGelfAppenderConfiguration withLogLevelNameIncluded(boolean logLevelNameIncluded)
+        {
+            this._logLevelNameIncluded = logLevelNameIncluded;
+            return this;
+        }
+
+        @Override
+        public Map<String, Object> getStaticFields()
+        {
+            return _staticFields;
+        }
+
+        public TestGelfAppenderConfiguration addStaticFields(Map<String, ?> map)
+        {
+            this._staticFields.putAll(map);
+            return this;
+        }
+
+        public GraylogAppender newAppender(Context context)
+        {
+            return GraylogAppender.newInstance(context, this);
+        }
+    }
+
+    static class DefaultGelfAppenderConfiguration implements GelfAppenderConfiguration
+    {
+        @Override
+        public String getRemoteHost()
+        {
+            return "localhost";
+        }
+
+        @Override
+        public String getMessageOriginHost()
+        {
+            return "BrokerJ";
+        }
+
+        public GraylogAppender newAppender(Context context)
+        {
+            return GraylogAppender.newInstance(context, this);
+        }
+    }
+
+    @Test
+    public void testNewInstance()
+    {
+        TestGelfAppenderConfiguration logger = new TestGelfAppenderConfiguration();
+        Context context = new LoggerContext();
+        GraylogAppender appender = GraylogAppender.newInstance(context, logger);
+        assertNotNull(appender);
+    }
+
+    @Test
+    public void testStart()
+    {
+        TestGelfAppenderConfiguration logger = new TestGelfAppenderConfiguration();
+        Context context = new LoggerContext();
+        GraylogAppender appender = logger.newAppender(context);
+        appender.setName("GelfAppender");
+        appender.start();
+
+        assertTrue(appender.isStarted());
+        assertEquals("GelfAppender", appender.getName());
+
+        Iterator<Appender<ILoggingEvent>> iterator = appender.iteratorForAppenders();
+        Appender<ILoggingEvent> app = null;
+        while (iterator.hasNext())
+        {
+            app = iterator.next();
+            assertNotNull(app);
+            assertTrue(app.isStarted());
+            assertEquals("GelfAppender", app.getName());
+            assertEquals(context, app.getContext());
+            assertTrue(app instanceof GelfTcpAppender);
+        }
+        assertNotNull(app);
+    }
+
+    @Test
+    public void testStart_again()
+    {
+        TestGelfAppenderConfiguration logger = new TestGelfAppenderConfiguration();
+        Context context = new LoggerContext();
+        GraylogAppender appender = logger.newAppender(context);
+        appender.setName("GelfAppender");
+        appender.start();
+        assertTrue(appender.isStarted());
+
+        Iterator<Appender<ILoggingEvent>> iterator = appender.iteratorForAppenders();
+        List<Appender<ILoggingEvent>> appenders = new ArrayList<>();
+        while (iterator.hasNext())
+        {
+            appenders.add(iterator.next());
+        }
+        assertFalse(appenders.isEmpty());
+
+        appender.start();
+        assertTrue(appender.isStarted());
+
+        iterator = appender.iteratorForAppenders();
+        List<Appender<ILoggingEvent>> newAppenders = new ArrayList<>();
+        while (iterator.hasNext())
+        {
+            newAppenders.add(iterator.next());
+        }
+        assertFalse(newAppenders.isEmpty());
+        assertTrue(newAppenders.containsAll(appenders));
+        assertTrue(appenders.containsAll(newAppenders));
+    }
+
+    @Test
+    public void testSetName()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration();
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.setName("GelfAppender");
+        appender.start();
+
+        appender.setName("NewGelfAppender");
+        Iterator<Appender<ILoggingEvent>> iterator = appender.iteratorForAppenders();
+        while (iterator.hasNext())
+        {
+            Appender<ILoggingEvent> app = iterator.next();
+            assertNotNull(app);
+            assertEquals("NewGelfAppender", app.getName());
+        }
+    }
+
+    @Test
+    public void testSetContext()
+    {
+        TestGelfAppenderConfiguration logger = new TestGelfAppenderConfiguration();
+        Context context = new LoggerContext();
+        GraylogAppender appender = logger.newAppender(context);
+        appender.setName("GelfAppender");
+        appender.start();
+
+        appender.setContext(context);
+        Iterator<Appender<ILoggingEvent>> iterator = appender.iteratorForAppenders();
+        while (iterator.hasNext())
+        {
+            Appender<ILoggingEvent> app = iterator.next();
+            assertNotNull(app);
+            assertEquals(context, app.getContext());
+        }
+    }
+
+    @Test
+    public void testRemoteHost()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withRemoteHost("Remote");
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals("Remote", gelfAppender.getGraylogHost());
+    }
+
+    @Test
+    public void testRemoteHost_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals("localhost", gelfAppender.getGraylogHost());
+    }
+
+    @Test
+    public void testPort()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withPort(42456);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(42456, gelfAppender.getGraylogPort());
+    }
+
+    @Test
+    public void testPort_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(12201, gelfAppender.getGraylogPort());
+    }
+
+    @Test
+    public void testReconnectInterval_withRounding()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withReconnectionInterval(11456);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(12, gelfAppender.getReconnectInterval());
+    }
+
+    @Test
+    public void testReconnectInterval_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(60, gelfAppender.getReconnectInterval());
+    }
+
+    @Test
+    public void testReconnectInterval()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withReconnectionInterval(11000);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(11, gelfAppender.getReconnectInterval());
+    }
+
+    @Test
+    public void testConnectTimeout()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withConnectionTimeout(1123);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(1123, gelfAppender.getConnectTimeout());
+    }
+
+    @Test
+    public void testConnectTimeout_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(15000, gelfAppender.getConnectTimeout());
+    }
+
+    @Test
+    public void testMaximumReconnectionAttempts()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withMaximumReconnectionAttempts(17);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(17, gelfAppender.getMaxRetries());
+    }
+
+    @Test
+    public void testMaximumReconnectionAttempts_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(2, gelfAppender.getMaxRetries());
+    }
+
+    @Test
+    public void testRetryDelay()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withRetryDelay(178);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(178, gelfAppender.getRetryDelay());
+    }
+
+    @Test
+    public void testRetryDelay_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfTcpAppender gelfAppender = extractGelfAppender(appender);
+        assertNotNull(gelfAppender);
+        assertEquals(3000, gelfAppender.getRetryDelay());
+    }
+
+    @Test
+    public void testMessageOriginHost()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withMessageOriginHost("Broker");
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertEquals("Broker", gelfEncoder.getOriginHost());
+    }
+
+    @Test
+    public void testRawMessageIncluded_True()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withRawMessageIncluded(true);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeRawMessage());
+    }
+
+    @Test
+    public void testRawMessageIncluded_False()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withRawMessageIncluded(false);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeRawMessage());
+    }
+
+    @Test
+    public void testRawMessageIncluded_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeRawMessage());
+    }
+
+    @Test
+    public void testEventMarkerIncluded_True()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withEventMarkerIncluded(true);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeMarker());
+    }
+
+    @Test
+    public void testEventMarkerIncluded_False()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withEventMarkerIncluded(false);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeMarker());
+    }
+
+    @Test
+    public void testEventMarkerIncluded_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeMarker());
+    }
+
+    @Test
+    public void testMdcPropertiesIncluded_True()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withMdcPropertiesIncluded(true);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeMdcData());
+    }
+
+    @Test
+    public void testMdcPropertiesIncluded_False()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withMdcPropertiesIncluded(false);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeMdcData());
+    }
+
+    @Test
+    public void testMdcPropertiesIncluded_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeMdcData());
+    }
+
+    @Test
+    public void testCallerDataIncluded_True()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withCallerDataIncluded(true);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeCallerData());
+    }
+
+    @Test
+    public void testCallerDataIncluded_False()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withCallerDataIncluded(false);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeCallerData());
+    }
+
+    @Test
+    public void testCallerDataIncluded_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeCallerData());
+    }
+
+    @Test
+    public void testRootExceptionDataIncluded_True()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withRootExceptionDataIncluded(true);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeRootCauseData());
+    }
+
+    @Test
+    public void testRootExceptionDataIncluded_False()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withRootExceptionDataIncluded(false);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeRootCauseData());
+    }
+
+    @Test
+    public void testRootExceptionDataIncluded_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeRootCauseData());
+    }
+
+    @Test
+    public void testLogLevelNameIncluded_True()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withLogLevelNameIncluded(true);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertTrue(gelfEncoder.isIncludeLevelName());
+    }
+
+    @Test
+    public void testLogLevelNameIncluded_False()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withLogLevelNameIncluded(false);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeLevelName());
+    }
+
+    @Test
+    public void testLogLevelNameIncluded_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        assertFalse(gelfEncoder.isIncludeLevelName());
+    }
+
+    @Test
+    public void testStaticFields()
+    {
+        Map<String, Object> staticFields = new HashMap<>(2);
+        staticFields.put("A", "A.A");
+        staticFields.put("B", 234);
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().addStaticFields(staticFields);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        Map<String, Object> fields = gelfEncoder.getStaticFields();
+        assertNotNull(fields);
+        assertEquals(2, fields.size());
+        assertEquals("A.A", fields.get("A"));
+        assertEquals(234, fields.get("B"));
+    }
+
+    @Test
+    public void testStaticFields_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        GelfEncoder gelfEncoder = extractGelfEncoder(appender);
+        assertNotNull(gelfEncoder);
+        Map<String, Object> fields = gelfEncoder.getStaticFields();
+        assertNotNull(fields);
+        assertTrue(fields.isEmpty());
+    }
+
+    @Test
+    public void testMessageBufferCapacity()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withMessageBufferCapacity(789);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        assertEquals(789, appender.getQueueSize());
+    }
+
+    @Test
+    public void testMessageBufferCapacity_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        assertEquals(256, appender.getQueueSize());
+    }
+
+    @Test
+    public void testMessagesFlushTimeOut()
+    {
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration().withMessagesFlushTimeOut(567);
+        Context context = new LoggerContext();
+        GraylogAppender appender = configuration.newAppender(context);
+        appender.start();
+
+        assertEquals(567, appender.getMaxFlushTime());
+    }
+
+    @Test
+    public void testMessagesFlushTimeOut_Default()
+    {
+        Context context = new LoggerContext();
+        GraylogAppender appender = new DefaultGelfAppenderConfiguration().newAppender(context);
+        appender.start();
+
+        assertEquals(1000, appender.getMaxFlushTime());
+    }
+
+    @Test
+    public void testDoAppend()
+    {
+        Context context = new LoggerContext();
+        TestGelfAppenderConfiguration configuration = new TestGelfAppenderConfiguration();
+        GraylogAppender appender = configuration.newAppender(context);
+        try
+        {
+            appender.doAppend(new TestLoggingEvent());
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    private GelfTcpAppender extractGelfAppender(GraylogAppender appender)
+    {
+        Iterator<Appender<ILoggingEvent>> iterator = appender.iteratorForAppenders();
+        while ((iterator.hasNext()))
+        {
+            Appender<ILoggingEvent> app = iterator.next();
+            if (app instanceof GelfTcpAppender)
+            {
+                return (GelfTcpAppender) app;
+            }
+        }
+        return null;
+    }
+
+    private GelfEncoder extractGelfEncoder(GraylogAppender appender)
+    {
+        Iterator<Appender<ILoggingEvent>> iterator = appender.iteratorForAppenders();
+        while (iterator.hasNext())
+        {
+            Appender<ILoggingEvent> app = iterator.next();
+            if (app instanceof GelfTcpAppender)
+            {
+                return ((GelfTcpAppender) app).getEncoder();
+            }
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/LoggingEventTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/LoggingEventTest.java
new file mode 100644
index 0000000..c25bf5e
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/LoggingEventTest.java
@@ -0,0 +1,298 @@
+package org.apache.qpid.server.logging.logback.event;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.MessageLogger;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class LoggingEventTest extends UnitTestBase
+{
+    public static class LocalTestLogger implements MessageLogger
+    {
+        private StackTraceElement[] _stackTrace = null;
+
+        @Override
+        public boolean isEnabled()
+        {
+            fillStackTrace();
+            return false;
+        }
+
+        @Override
+        public boolean isMessageEnabled(String logHierarchy)
+        {
+            fillStackTrace();
+            return false;
+        }
+
+        @Override
+        public void message(LogMessage message)
+        {
+            fillStackTrace();
+        }
+
+        @Override
+        public void message(LogSubject subject, LogMessage message)
+        {
+            fillStackTrace();
+        }
+
+        private void fillStackTrace()
+        {
+            _stackTrace = Thread.currentThread().getStackTrace();
+        }
+
+        public TestLoggingEvent event()
+        {
+            return new TestLoggingEvent().withCallerData(_stackTrace);
+        }
+    }
+
+    @Test
+    public void testWrap_NullAsInput()
+    {
+        assertNull(LoggingEvent.wrap(null));
+    }
+
+    @Test
+    public void testWrap()
+    {
+        assertNotNull(LoggingEvent.wrap(new TestLoggingEvent()));
+    }
+
+    @Test
+    public void testGetThreadName()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getThreadName(), wrapper.getThreadName());
+    }
+
+    @Test
+    public void testGetLevel()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getLevel(), wrapper.getLevel());
+    }
+
+    @Test
+    public void testGetMessage()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getMessage(), wrapper.getMessage());
+    }
+
+    @Test
+    public void testGetArgumentArray()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertTrue(Arrays.deepEquals(event.getArgumentArray(), wrapper.getArgumentArray()));
+    }
+
+    @Test
+    public void testGetFormattedMessage()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getFormattedMessage(), wrapper.getFormattedMessage());
+    }
+
+    @Test
+    public void testGetLoggerName()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getLoggerName(), wrapper.getLoggerName());
+    }
+
+    @Test
+    public void testGetLoggerContextVO()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getLoggerContextVO(), wrapper.getLoggerContextVO());
+    }
+
+    @Test
+    public void testGetThrowableProxy()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getThrowableProxy(), wrapper.getThrowableProxy());
+    }
+
+    @Test
+    public void testGetCallerData_NullAsInput()
+    {
+        TestLoggingEvent event = new TestLoggingEvent().withCallerData(null);
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        StackTraceElement[] data = wrapper.getCallerData();
+        assertNotNull(data);
+        assertEquals(0, data.length);
+
+        data = wrapper.getCallerData();
+        assertNotNull(data);
+        assertEquals(0, data.length);
+    }
+
+    @Test
+    public void testGetCallerData_EmptyInput()
+    {
+        TestLoggingEvent event = new TestLoggingEvent().withCallerData(new StackTraceElement[0]);
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        StackTraceElement[] callerData = wrapper.getCallerData();
+        assertNotNull(callerData);
+        assertEquals(0, callerData.length);
+
+        callerData = wrapper.getCallerData();
+        assertNotNull(callerData);
+        assertEquals(0, callerData.length);
+    }
+
+    @Test
+    public void testGetCallerData_AllData()
+    {
+        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+        TestLoggingEvent event = new TestLoggingEvent().withCallerData(stackTrace);
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        StackTraceElement[] callerData = wrapper.getCallerData();
+        assertNotNull(callerData);
+        assertTrue(Arrays.deepEquals(stackTrace, callerData));
+
+        callerData = wrapper.getCallerData();
+        assertNotNull(callerData);
+        assertTrue(Arrays.deepEquals(stackTrace, callerData));
+    }
+
+    @Test
+    public void testGetCallerData_FilteredData()
+    {
+        LocalTestLogger logger = new LocalTestLogger();
+        logger.message(() -> "Class");
+
+        ILoggingEvent wrapper = LoggingEvent.wrap(logger.event());
+        assertNotNull(wrapper);
+
+        StackTraceElement[] callerData = wrapper.getCallerData();
+        assertNotNull(callerData);
+
+        final String name = LocalTestLogger.class.getName();
+        assertFalse(Arrays.stream(callerData).anyMatch(e -> e.getClassName().contains(name)));
+    }
+
+    @Test
+    public void testHasCallerData_NegativeResult()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        event.withCallerData(null);
+        assertFalse(wrapper.hasCallerData());
+
+        event.withCallerData(new StackTraceElement[0]);
+        assertFalse(wrapper.hasCallerData());
+    }
+
+    @Test
+    public void testHasCallerData_PositiveResult()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        event.withCallerData(Thread.currentThread().getStackTrace());
+        assertTrue(wrapper.hasCallerData());
+    }
+
+    @Test
+    public void testGetMarker()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getMarker(), wrapper.getMarker());
+    }
+
+    @Test
+    public void tesGetMDCPropertyMap()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        Map<String, String> originalMap = event.getMDCPropertyMap();
+        Map<String, String> map = wrapper.getMDCPropertyMap();
+
+        assertEquals(originalMap.keySet(), map.keySet());
+        for (Map.Entry<String, String> entry : originalMap.entrySet())
+        {
+            assertEquals(entry.getValue(), map.get(entry.getKey()));
+        }
+    }
+
+    @Test
+    public void testGetMdc()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+
+        Map<String, String> originalMap = event.getMdc();
+        Map<String, String> map = wrapper.getMdc();
+
+        assertEquals(originalMap.keySet(), map.keySet());
+        for (Map.Entry<String, String> entry : originalMap.entrySet())
+        {
+            assertEquals(entry.getValue(), map.get(entry.getKey()));
+        }
+    }
+
+    @Test
+    public void testGetTimeStamp()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertEquals(event.getTimeStamp(), wrapper.getTimeStamp());
+    }
+
+    @Test
+    public void testPrepareForDeferredProcessing()
+    {
+        TestLoggingEvent event = new TestLoggingEvent();
+        ILoggingEvent wrapper = LoggingEvent.wrap(event);
+        assertNotNull(wrapper);
+        assertFalse(event.isPreparedForDeferredProcessing());
+        wrapper.prepareForDeferredProcessing();
+        assertTrue(event.isPreparedForDeferredProcessing());
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/TestLoggingEvent.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/TestLoggingEvent.java
new file mode 100644
index 0000000..f85a149
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/event/TestLoggingEvent.java
@@ -0,0 +1,224 @@
+package org.apache.qpid.server.logging.logback.event;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.LoggerContextVO;
+import ch.qos.logback.classic.spi.StackTraceElementProxy;
+import org.slf4j.Marker;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+public class TestLoggingEvent implements ILoggingEvent
+{
+    private final String _threadName = Thread.currentThread().getName();
+
+    private final Object[] _argumentArray = {"A", "B"};
+
+    private final LoggerContextVO _loggerContext = new LoggerContextVO(new LoggerContext());
+
+    private final long _timeStamp = System.currentTimeMillis();
+
+    private final Marker _marker = new Marker()
+    {
+        @Override
+        public String getName()
+        {
+            return Marker.ANY_MARKER;
+        }
+
+        @Override
+        public void add(Marker marker)
+        {
+        }
+
+        @Override
+        public boolean remove(Marker marker)
+        {
+            return false;
+        }
+
+        @Override
+        public boolean hasChildren()
+        {
+            return false;
+        }
+
+        @Override
+        public boolean hasReferences()
+        {
+            return false;
+        }
+
+        @Override
+        public Iterator<Marker> iterator()
+        {
+            return Collections.emptyIterator();
+        }
+
+        @Override
+        public boolean contains(Marker marker)
+        {
+            return false;
+        }
+
+        @Override
+        public boolean contains(String s)
+        {
+            return false;
+        }
+    };
+
+    private final Map<String, String> _mdcMap = Collections.singletonMap("A", "B");
+
+    private boolean preparedForDeferredProcessing = false;
+
+    private final IThrowableProxy _throwableProxy = new IThrowableProxy()
+    {
+        @Override
+        public String getMessage()
+        {
+            return "message";
+        }
+
+        @Override
+        public String getClassName()
+        {
+            return "className";
+        }
+
+        @Override
+        public StackTraceElementProxy[] getStackTraceElementProxyArray()
+        {
+            return new StackTraceElementProxy[0];
+        }
+
+        @Override
+        public int getCommonFrames()
+        {
+            return 0;
+        }
+
+        @Override
+        public IThrowableProxy getCause()
+        {
+            return null;
+        }
+
+        @Override
+        public IThrowableProxy[] getSuppressed()
+        {
+            return new IThrowableProxy[0];
+        }
+    };
+
+    private StackTraceElement[] _callerData;
+
+    @Override
+    public String getThreadName()
+    {
+        return _threadName;
+    }
+
+    @Override
+    public Level getLevel()
+    {
+        return Level.INFO;
+    }
+
+    @Override
+    public String getMessage()
+    {
+        return "Error message";
+    }
+
+    @Override
+    public Object[] getArgumentArray()
+    {
+        return _argumentArray;
+    }
+
+    @Override
+    public String getFormattedMessage()
+    {
+        return "Formatted message";
+    }
+
+    @Override
+    public String getLoggerName()
+    {
+        return "Logger";
+    }
+
+    @Override
+    public LoggerContextVO getLoggerContextVO()
+    {
+        return _loggerContext;
+    }
+
+    @Override
+    public IThrowableProxy getThrowableProxy()
+    {
+        return _throwableProxy;
+    }
+
+    @Override
+    public StackTraceElement[] getCallerData()
+    {
+        return _callerData;
+    }
+
+    @Override
+    public boolean hasCallerData()
+    {
+        return _callerData != null;
+    }
+
+    @Override
+    public Marker getMarker()
+    {
+        return _marker;
+    }
+
+    @Override
+    public Map<String, String> getMDCPropertyMap()
+    {
+        return _mdcMap;
+    }
+
+    /**
+     * @deprecated getMDCPropertyMap method should be used instead.
+     */
+    @Deprecated
+    @Override
+    public Map<String, String> getMdc()
+    {
+        return _mdcMap;
+    }
+
+    @Override
+    public long getTimeStamp()
+    {
+        return _timeStamp;
+    }
+
+    @Override
+    public void prepareForDeferredProcessing()
+    {
+        preparedForDeferredProcessing = true;
+    }
+
+    public boolean isPreparedForDeferredProcessing()
+    {
+        return preparedForDeferredProcessing;
+    }
+
+    public TestLoggingEvent withCallerData(StackTraceElement[] data)
+    {
+        _callerData = data;
+        return this;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastOneTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastOneTest.java
new file mode 100644
index 0000000..839a067
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastOneTest.java
@@ -0,0 +1,93 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AtLeastOneTest extends UnitTestBase
+{
+    @Test
+    public void validator()
+    {
+        assertNotNull("Factory method has to produce a instance", AtLeastOne.validator());
+    }
+
+    @Test
+    public void testValidate_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            AtLeastOne.validateValue(null, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value 'null' as it has to be at least 1", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            AtLeastOne.validateValue(2, object, "attr");
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            AtLeastOne.validateValue(0, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '0' as it has to be at least 1", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastTest.java
new file mode 100644
index 0000000..7cea738
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastTest.java
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AtLeastTest extends UnitTestBase
+{
+    @Test
+    public void testValidate_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        Validator<Integer> validator = new AtLeast(42);
+        try
+        {
+            validator.validate(null, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value 'null' as it has to be at least 42", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        Validator<Integer> validator = new AtLeast(42);
+        try
+        {
+            validator.validate(42, object, "attr");
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        Validator<Integer> validator = new AtLeast(42);
+        try
+        {
+            validator.validate(41, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '41' as it has to be at least 42", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastZeroTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastZeroTest.java
new file mode 100644
index 0000000..80c115c
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/AtLeastZeroTest.java
@@ -0,0 +1,93 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AtLeastZeroTest extends UnitTestBase
+{
+    @Test
+    public void validator()
+    {
+        assertNotNull("Factory method has to produce a instance", AtLeastZero.validator());
+    }
+
+    @Test
+    public void testValidate_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            AtLeastZero.validateValue(null, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value 'null' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            AtLeastZero.validateValue(2, object, "attr");
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            AtLeastZero.validateValue(-1, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-1' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java
new file mode 100644
index 0000000..4d62581
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java
@@ -0,0 +1,788 @@
+package org.apache.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.logging.logback.GelfAppenderConfiguration;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class GelfConfigurationValidatorTest extends UnitTestBase
+{
+    private static class TestLogger implements GelfAppenderConfiguration
+    {
+        private int _port = 12201;
+
+        private int _reconnectionInterval = 10000;
+
+        private int _connectionTimeout = 300;
+
+        private int _maximumReconnectionAttempts = 1;
+
+        private int _retryDelay = 500;
+
+        private int _messagesFlushTimeOut = 10000;
+
+        private int _messageBufferCapacity = 10000;
+
+        private final Map<String, Object> _staticFields = new LinkedHashMap<>();
+
+        @Override
+        public String getRemoteHost()
+        {
+            return "localhost";
+        }
+
+        @Override
+        public int getPort()
+        {
+            return _port;
+        }
+
+        public TestLogger withPort(int port)
+        {
+            this._port = port;
+            return this;
+        }
+
+        @Override
+        public int getReconnectionInterval()
+        {
+            return _reconnectionInterval;
+        }
+
+        public TestLogger withReconnectionInterval(int reconnectionInterval)
+        {
+            this._reconnectionInterval = reconnectionInterval;
+            return this;
+        }
+
+        @Override
+        public int getConnectionTimeout()
+        {
+            return _connectionTimeout;
+        }
+
+        public TestLogger withConnectionTimeout(int connectionTimeout)
+        {
+            this._connectionTimeout = connectionTimeout;
+            return this;
+        }
+
+        @Override
+        public int getMaximumReconnectionAttempts()
+        {
+            return _maximumReconnectionAttempts;
+        }
+
+        public TestLogger withMaximumReconnectionAttempts(int maximumReconnectionAttempts)
+        {
+            this._maximumReconnectionAttempts = maximumReconnectionAttempts;
+            return this;
+        }
+
+        @Override
+        public int getRetryDelay()
+        {
+            return _retryDelay;
+        }
+
+        public TestLogger withRetryDelay(int retryDelay)
+        {
+            this._retryDelay = retryDelay;
+            return this;
+        }
+
+        @Override
+        public int getMessagesFlushTimeOut()
+        {
+            return _messagesFlushTimeOut;
+        }
+
+        public TestLogger withMessagesFlushTimeOut(int messagesFlushTimeOut)
+        {
+            this._messagesFlushTimeOut = messagesFlushTimeOut;
+            return this;
+        }
+
+        @Override
+        public int getMessageBufferCapacity()
+        {
+            return _messageBufferCapacity;
+        }
+
+        public TestLogger withMessageBufferCapacity(int messageBufferCapacity)
+        {
+            this._messageBufferCapacity = messageBufferCapacity;
+            return this;
+        }
+
+        @Override
+        public String getMessageOriginHost()
+        {
+            return "BrokerJ";
+        }
+
+        @Override
+        public boolean isRawMessageIncluded()
+        {
+            return true;
+        }
+
+        @Override
+        public boolean isEventMarkerIncluded()
+        {
+            return true;
+        }
+
+        @Override
+        public boolean hasMdcPropertiesIncluded()
+        {
+            return true;
+        }
+
+        @Override
+        public boolean isCallerDataIncluded()
+        {
+            return true;
+        }
+
+        @Override
+        public boolean hasRootExceptionDataIncluded()
+        {
+            return true;
+        }
+
+        @Override
+        public boolean isLogLevelNameIncluded()
+        {
+            return true;
+        }
+
+        @Override
+        public Map<String, Object> getStaticFields()
+        {
+            return _staticFields;
+        }
+
+        public TestLogger addStaticFields(Map<String, ?> map)
+        {
+            this._staticFields.putAll(map);
+            return this;
+        }
+    }
+
+    @Test
+    public void testValidate_Port_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withPort(4567), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withPort(4567), object, Collections.singleton(GelfConfigurationValidator.PORT.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.PORT.validate(logger.withPort(4567), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_Port_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withPort(456789), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'port' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '456789' as it has to be in range [1, 65535]", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.PORT.validate(logger.withPort(456789), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'port' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '456789' as it has to be in range [1, 65535]", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withPort(456789), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ReconnectionInterval_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withReconnectionInterval(500), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withReconnectionInterval(500), object, Collections.singleton(GelfConfigurationValidator.RECONNECTION_INTERVAL.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.RECONNECTION_INTERVAL.validate(logger.withReconnectionInterval(500), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ReconnectionInterval_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withReconnectionInterval(-1), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'reconnectionInterval' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-1' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.RECONNECTION_INTERVAL.validate(logger.withReconnectionInterval(-1), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'reconnectionInterval' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-1' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withReconnectionInterval(-1), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ConnectionTimeout_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withConnectionTimeout(500), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withConnectionTimeout(500), object, Collections.singleton(GelfConfigurationValidator.CONNECTION_TIMEOUT.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.CONNECTION_TIMEOUT.validate(logger.withConnectionTimeout(500), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ConnectionTimeout_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withConnectionTimeout(-1), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'connectionTimeout' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-1' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.CONNECTION_TIMEOUT.validate(logger.withConnectionTimeout(-1), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'connectionTimeout' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-1' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withConnectionTimeout(-1), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_MaximumReconnectionAttempts_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMaximumReconnectionAttempts(5), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMaximumReconnectionAttempts(5), object, Collections.singleton(GelfConfigurationValidator.MAXIMUM_RECONNECTION_ATTEMPTS.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.MAXIMUM_RECONNECTION_ATTEMPTS.validate(logger.withMaximumReconnectionAttempts(5), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+    }
+
+    @Test
+    public void testValidate_MaximumReconnectionAttempts_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMaximumReconnectionAttempts(-5), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'maximumReconnectionAttempts' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-5' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.MAXIMUM_RECONNECTION_ATTEMPTS.validate(logger.withMaximumReconnectionAttempts(-5), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'maximumReconnectionAttempts' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-5' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMaximumReconnectionAttempts(-1), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_RetryDelay_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withRetryDelay(50), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withRetryDelay(50), object, Collections.singleton(GelfConfigurationValidator.RETRY_DELAY.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.RETRY_DELAY.validate(logger.withRetryDelay(50), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_RetryDelay_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withRetryDelay(-5), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'retryDelay' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-5' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.RETRY_DELAY.validate(logger.withRetryDelay(-5), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'retryDelay' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-5' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withRetryDelay(-5), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_MessagesFlushTimeOut_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessagesFlushTimeOut(50), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessagesFlushTimeOut(50), object, Collections.singleton(GelfConfigurationValidator.FLUSH_TIME_OUT.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.FLUSH_TIME_OUT.validate(logger.withMessagesFlushTimeOut(50), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_MessagesFlushTimeOut_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessagesFlushTimeOut(-5), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'messagesFlushTimeOut' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-5' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.FLUSH_TIME_OUT.validate(logger.withMessagesFlushTimeOut(-5), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'messagesFlushTimeOut' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '-5' as it has to be at least 0", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessagesFlushTimeOut(-5), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_MessageBufferCapacity_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessageBufferCapacity(250), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessageBufferCapacity(250), object, Collections.singleton(GelfConfigurationValidator.BUFFER_CAPACITY.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.BUFFER_CAPACITY.validate(logger.withMessageBufferCapacity(250), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_MessageBufferCapacity_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessageBufferCapacity(0), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'messageBufferCapacity' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '0' as it has to be at least 1", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.BUFFER_CAPACITY.validate(logger.withMessageBufferCapacity(0), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'messageBufferCapacity' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '0' as it has to be at least 1", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.withMessageBufferCapacity(0), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_StaticFields_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.addStaticFields(Collections.singletonMap("A", 345)), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.addStaticFields(Collections.singletonMap("A", 345)), object, Collections.singleton(GelfConfigurationValidator.STATIC_FIELDS.attributeName()));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.STATIC_FIELDS.validate(logger.addStaticFields(Collections.singletonMap("A", 345)), object);
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_StaticFields_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        TestLogger logger = new TestLogger();
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.addStaticFields(Collections.singletonMap("A", true)), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Value of 'staticFields attribute instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be 'true', as it has to be a string or number", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.STATIC_FIELDS.validate(logger.addStaticFields(Collections.singletonMap("A", true)), object);
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Value of 'staticFields attribute instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be 'true', as it has to be a string or number", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+
+        try
+        {
+            GelfConfigurationValidator.validateConfiguration(logger.addStaticFields(Collections.singletonMap("A", true)), object, Collections.singleton("A"));
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFieldsTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFieldsTest.java
new file mode 100644
index 0000000..37e7038
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfMessageStaticFieldsTest.java
@@ -0,0 +1,142 @@
+package org.apache.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+public class GelfMessageStaticFieldsTest extends UnitTestBase
+{
+    @Test
+    public void testValidator()
+    {
+        assertNotNull("Factory method has to produce a instance", GelfMessageStaticFields.validator());
+    }
+
+    @Test
+    public void testValidate_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            GelfMessageStaticFields.validateStaticFields(null, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be 'null'", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidateKey_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            Map<String, Object> map = new HashMap<>();
+            map.put("B", "B");
+            map.put(null, "A");
+            GelfMessageStaticFields.validateStaticFields(map, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Key of 'attr attribute instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be 'null'. Key pattern is: [\\w\\.\\-]+", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidateValue_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            Map<String, Object> map = new HashMap<>();
+            map.put("B", "B");
+            map.put("A", null);
+            GelfMessageStaticFields.validateStaticFields(map, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Value of 'attr attribute instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be 'null', as it has to be a string or number", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidateKey_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            Map<String, Object> map = new HashMap<>();
+            map.put("B", "AB");
+            map.put("A", 234);
+            GelfMessageStaticFields.validateStaticFields(map, object, "attr");
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidateKey_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+
+        try
+        {
+            GelfMessageStaticFields.validateStaticFields(Collections.singletonMap("{abc}", "true"), object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Key of 'attr attribute instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be '{abc}'. Key pattern is: [\\w\\.\\-]+", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidateValue_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+
+        try
+        {
+            GelfMessageStaticFields.validateStaticFields(Collections.singletonMap("A", true), object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Value of 'attr attribute instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot be 'true', as it has to be a string or number", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/PortTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/PortTest.java
new file mode 100644
index 0000000..3d2b8df
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/PortTest.java
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.test.utils.UnitTestBase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+public class PortTest extends UnitTestBase
+{
+    @Test
+    public void testValidator()
+    {
+        assertNotNull("Factory method has to produce a instance", Port.validator());
+    }
+
+    @Test
+    public void testValidate_NullAsInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            Port.validatePort(null, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value 'null' as it has to be in range [1, 65535]", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_ValidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            Port.validatePort(14420, object, "attr");
+        }
+        catch (RuntimeException e)
+        {
+            fail("Any exception is not expected");
+        }
+    }
+
+    @Test
+    public void testValidate_InvalidInput()
+    {
+        TestConfiguredObject object = new TestConfiguredObject();
+        try
+        {
+            Port.validatePort(170641, object, "attr");
+            fail("An exception is expected");
+        }
+        catch (IllegalConfigurationException e)
+        {
+            assertEquals("Attribute 'attr' instance of org.apache.qpid.server.logging.logback.validator.TestConfiguredObject named 'TestConfiguredObject' cannot have value '170641' as it has to be in range [1, 65535]", e.getMessage());
+        }
+        catch (RuntimeException e)
+        {
+            fail("A generic exception is not expected");
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObject.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObject.java
new file mode 100644
index 0000000..ffb9620
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObject.java
@@ -0,0 +1,486 @@
+/*
+ * 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.qpid.server.logging.logback.validator;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.ConfigurationChangeListener;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFactory;
+import org.apache.qpid.server.model.LifetimePolicy;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.preferences.UserPreferences;
+import org.apache.qpid.server.security.SecurityToken;
+import org.apache.qpid.server.security.access.Operation;
+import org.apache.qpid.server.store.ConfiguredObjectRecord;
+
+import javax.security.auth.Subject;
+import java.lang.reflect.Type;
+import java.security.AccessControlException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+public class TestConfiguredObject implements ConfiguredObject<TestConfiguredObject>
+{
+    public static final String CONFIGURED_OBJECT = "ConfiguredObject";
+
+    public static final String _NAME = "TestConfiguredObject";
+
+    private final Date _now = new Date();
+
+    private final Map<String, String> _context = new LinkedHashMap<>();
+
+    private final Map<String, Object> _attributes = new LinkedHashMap<>();
+
+    private final TaskExecutor _taskExecutor = CurrentThreadTaskExecutor.newStartedInstance();
+
+    private final ConfiguredObject<?> _parent;
+
+    private UUID _uuid = UUID.randomUUID();
+
+    private UserPreferences _userPreferences;
+
+    public TestConfiguredObject()
+    {
+        super();
+        _parent = null;
+    }
+
+    public TestConfiguredObject(ConfiguredObject<?> parent, Map<String, Object> attributes)
+    {
+        super();
+        _parent = parent;
+        _attributes.putAll(attributes);
+    }
+
+    @Override
+    public UUID getId()
+    {
+        return _uuid;
+    }
+
+    public TestConfiguredObject withId(UUID id)
+    {
+        this._uuid = id;
+        return this;
+    }
+
+    @Override
+    public String getName()
+    {
+        return _NAME;
+    }
+
+    @Override
+    public String getDescription()
+    {
+        return getName();
+    }
+
+    @Override
+    public String getType()
+    {
+        return CONFIGURED_OBJECT;
+    }
+
+    @Override
+    public Map<String, String> getContext()
+    {
+        return _context;
+    }
+
+    @Override
+    public String getLastUpdatedBy()
+    {
+        return "user";
+    }
+
+    @Override
+    public Date getLastUpdatedTime()
+    {
+        return _now;
+    }
+
+    @Override
+    public String getCreatedBy()
+    {
+        return "user";
+    }
+
+    @Override
+    public Date getCreatedTime()
+    {
+        return _now;
+    }
+
+    @Override
+    public State getDesiredState()
+    {
+        return State.ACTIVE;
+    }
+
+    @Override
+    public State getState()
+    {
+        return State.ACTIVE;
+    }
+
+    @Override
+    public Date getLastOpenedTime()
+    {
+        return _now;
+    }
+
+    @Override
+    public void addChangeListener(ConfigurationChangeListener listener)
+    {
+    }
+
+    @Override
+    public boolean removeChangeListener(ConfigurationChangeListener listener)
+    {
+        return false;
+    }
+
+    @Override
+    public ConfiguredObject<?> getParent()
+    {
+        return _parent;
+    }
+
+    @Override
+    public boolean isDurable()
+    {
+        return false;
+    }
+
+    @Override
+    public LifetimePolicy getLifetimePolicy()
+    {
+        return LifetimePolicy.IN_USE;
+    }
+
+    @Override
+    public Map<String, Object> getStatistics(List<String> statistics)
+    {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public String setContextVariable(String name, String value)
+    {
+        return _context.put(name, value);
+    }
+
+    @Override
+    public String removeContextVariable(String name)
+    {
+        return _context.remove(name);
+    }
+
+    @Override
+    public Collection<String> getAttributeNames()
+    {
+        return _attributes.keySet();
+    }
+
+    @Override
+    public Object getAttribute(String name)
+    {
+        return _attributes.get(name);
+    }
+
+    @Override
+    public Map<String, Object> getActualAttributes()
+    {
+        return _attributes;
+    }
+
+    @Override
+    public Map<String, Object> getStatistics()
+    {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz)
+    {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public <C extends ConfiguredObject> C getChildById(Class<C> clazz, UUID id)
+    {
+        return null;
+    }
+
+    @Override
+    public <C extends ConfiguredObject> C getChildByName(Class<C> clazz, String name)
+    {
+        return null;
+    }
+
+    @Override
+    public <C extends ConfiguredObject> C createChild(Class<C> childClass, Map<String, Object> attributes)
+    {
+        return null;
+    }
+
+    @Override
+    public <C extends ConfiguredObject> ListenableFuture<C> getAttainedChildById(Class<C> childClass, UUID id)
+    {
+        final SettableFuture<C> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public <C extends ConfiguredObject> ListenableFuture<C> getAttainedChildByName(Class<C> childClass, String name)
+    {
+        final SettableFuture<C> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public <C extends ConfiguredObject> ListenableFuture<C> createChildAsync(Class<C> childClass, Map<String, Object> attributes)
+    {
+        final SettableFuture<C> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public void setAttributes(Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException
+    {
+        _attributes.clear();
+        _attributes.putAll(attributes);
+    }
+
+    @Override
+    public ListenableFuture<Void> setAttributesAsync(Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException
+    {
+        setAttributes(attributes);
+
+        final SettableFuture<Void> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public Class<? extends ConfiguredObject> getCategoryClass()
+    {
+        return TestConfiguredObject.class;
+    }
+
+    @Override
+    public Class<? extends ConfiguredObject> getTypeClass()
+    {
+        return TestConfiguredObject.class;
+    }
+
+    @Override
+    public boolean managesChildStorage()
+    {
+        return false;
+    }
+
+    @Override
+    public <C extends ConfiguredObject<C>> C findConfiguredObject(Class<C> clazz, String name)
+    {
+        if (getClass().equals(clazz) && Objects.equals(getName(), name))
+        {
+            return (C) this;
+        }
+        return null;
+    }
+
+    @Override
+    public ConfiguredObjectRecord asObjectRecord()
+    {
+        final TestConfiguredObject me = this;
+        return new ConfiguredObjectRecord()
+        {
+            @Override
+            public UUID getId()
+            {
+                return me.getId();
+            }
+
+            @Override
+            public String getType()
+            {
+                return me.getType();
+            }
+
+            @Override
+            public Map<String, Object> getAttributes()
+            {
+                return me.getActualAttributes();
+            }
+
+            @Override
+            public Map<String, UUID> getParents()
+            {
+                return Collections.emptyMap();
+            }
+        };
+    }
+
+    @Override
+    public void open()
+    {
+    }
+
+    @Override
+    public ListenableFuture<Void> openAsync()
+    {
+        final SettableFuture<Void> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public void close()
+    {
+    }
+
+    @Override
+    public ListenableFuture<Void> closeAsync()
+    {
+        final SettableFuture<Void> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public ListenableFuture<Void> deleteAsync()
+    {
+        final SettableFuture<Void> returnVal = SettableFuture.create();
+        returnVal.set(null);
+        return returnVal;
+    }
+
+    @Override
+    public TaskExecutor getChildExecutor()
+    {
+        return null;
+    }
+
+    @Override
+    public ConfiguredObjectFactory getObjectFactory()
+    {
+        return TestModel.MODEL.getObjectFactory();
+    }
+
+    @Override
+    public Model getModel()
+    {
+        return TestModel.MODEL;
+    }
+
+    @Override
+    public void delete()
+    {
+    }
+
+    @Override
+    public boolean hasEncrypter()
+    {
+        return false;
+    }
+
+    @Override
+    public void decryptSecrets()
+    {
+    }
+
+    @Override
+    public UserPreferences getUserPreferences()
+    {
+        return _userPreferences;
+    }
+
+    @Override
+    public void setUserPreferences(UserPreferences userPreferences)
+    {
+        _userPreferences = userPreferences;
+    }
+
+    @Override
+    public void authorise(Operation operation) throws AccessControlException
+    {
+    }
+
+    @Override
+    public void authorise(Operation operation, Map<String, Object> arguments) throws AccessControlException
+    {
+    }
+
+    @Override
+    public void authorise(SecurityToken token, Operation operation, Map<String, Object> arguments) throws AccessControlException
+    {
+    }
+
+    @Override
+    public SecurityToken newToken(Subject subject)
+    {
+        return null;
+    }
+
+    @Override
+    public <T> T getContextValue(Class<T> clazz, String propertyName)
+    {
+        if (String.class.equals(clazz))
+        {
+            return (T) _context.get(propertyName);
+        }
+        return null;
+    }
+
+    @Override
+    public <T> T getContextValue(Class<T> clazz, Type t, String propertyName)
+    {
+        return getContextValue(clazz, propertyName);
+    }
+
+    @Override
+    public Set<String> getContextKeys(boolean excludeSystem)
+    {
+        return _context.keySet();
+    }
+
+    @Override
+    public TaskExecutor getTaskExecutor()
+    {
+        return _taskExecutor;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectFactory.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectFactory.java
new file mode 100644
index 0000000..67a2d54
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectFactory.java
@@ -0,0 +1,100 @@
+/*
+ * 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.qpid.server.logging.logback.validator;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFactory;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.plugin.ConfiguredObjectTypeFactory;
+import org.apache.qpid.server.store.ConfiguredObjectRecord;
+import org.apache.qpid.server.store.UnresolvedConfiguredObject;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+public class TestConfiguredObjectFactory implements ConfiguredObjectFactory
+{
+    private static final TestConfiguredObjectTypeFactory _FACTORY = new TestConfiguredObjectTypeFactory();
+
+    private final Model _model;
+
+    public TestConfiguredObjectFactory(Model model)
+    {
+        _model = model;
+    }
+
+    @Override
+    public <X extends ConfiguredObject<X>> UnresolvedConfiguredObject<X> recover(ConfiguredObjectRecord record, ConfiguredObject<?> parent)
+    {
+        if (!TestConfiguredObject.TYPE.equals(record.getType()))
+        {
+            return null;
+        }
+        return (UnresolvedConfiguredObject<X>) _FACTORY.recover(this, record, parent);
+    }
+
+    @Override
+    public <X extends ConfiguredObject<X>> X create(Class<X> clazz, Map<String, Object> attributes, ConfiguredObject<?> parent)
+    {
+        if (TestConfiguredObject.class.equals(clazz))
+        {
+            return (X) _FACTORY.create(this, attributes, parent);
+        }
+        return null;
+    }
+
+    @Override
+    public <X extends ConfiguredObject<X>> ListenableFuture<X> createAsync(Class<X> clazz, Map<String, Object> attributes, ConfiguredObject<?> parent)
+    {
+        final SettableFuture<X> returnVal = SettableFuture.create();
+        returnVal.set(create(clazz, attributes, parent));
+        return returnVal;
+    }
+
+    @Override
+    public <X extends ConfiguredObject<X>> ConfiguredObjectTypeFactory<X> getConfiguredObjectTypeFactory(String category, String type)
+    {
+        if (TestConfiguredObject.class.getSimpleName().equals(category) && TestConfiguredObject.TYPE.equals(type))
+        {
+            return (ConfiguredObjectTypeFactory<X>) new TestConfiguredObjectTypeFactory();
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<String> getSupportedTypes(Class<? extends ConfiguredObject> category)
+    {
+        if (TestConfiguredObject.class.equals(category))
+        {
+            return Collections.singleton(TestConfiguredObject.TYPE);
+        }
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Model getModel()
+    {
+        return _model;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectTypeFactory.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectTypeFactory.java
new file mode 100644
index 0000000..acec505
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestConfiguredObjectTypeFactory.java
@@ -0,0 +1,88 @@
+/*
+ * 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.qpid.server.logging.logback.validator;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFactory;
+import org.apache.qpid.server.plugin.ConfiguredObjectTypeFactory;
+import org.apache.qpid.server.store.ConfiguredObjectDependency;
+import org.apache.qpid.server.store.ConfiguredObjectRecord;
+import org.apache.qpid.server.store.UnresolvedConfiguredObject;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+public class TestConfiguredObjectTypeFactory implements ConfiguredObjectTypeFactory<TestConfiguredObject>
+{
+    @Override
+    public Class<? super TestConfiguredObject> getCategoryClass()
+    {
+        return TestConfiguredObject.class;
+    }
+
+    @Override
+    public TestConfiguredObject create(ConfiguredObjectFactory factory, Map<String, Object> attributes, ConfiguredObject<?> parent)
+    {
+        return new TestConfiguredObject(parent, attributes);
+    }
+
+    @Override
+    public ListenableFuture<TestConfiguredObject> createAsync(ConfiguredObjectFactory factory, Map<String, Object> attributes, ConfiguredObject<?> parent)
+    {
+        final SettableFuture<TestConfiguredObject> returnVal = SettableFuture.create();
+        returnVal.set(create(factory, attributes, parent));
+        return returnVal;
+    }
+
+    @Override
+    public UnresolvedConfiguredObject<TestConfiguredObject> recover(final ConfiguredObjectFactory factory, final ConfiguredObjectRecord record, final ConfiguredObject<?> parent)
+    {
+        return new UnresolvedConfiguredObject<TestConfiguredObject>()
+        {
+            @Override
+            public ConfiguredObject<?> getParent()
+            {
+                return parent;
+            }
+
+            @Override
+            public Collection<ConfiguredObjectDependency<?>> getUnresolvedDependencies()
+            {
+                return Collections.emptyList();
+            }
+
+            @Override
+            public TestConfiguredObject resolve()
+            {
+                return create(factory, record.getAttributes(), parent).withId(record.getId());
+            }
+        };
+    }
+
+    @Override
+    public String getType()
+    {
+        return null;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestModel.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestModel.java
new file mode 100644
index 0000000..8bd09a0
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/TestModel.java
@@ -0,0 +1,118 @@
+/*
+ * 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.qpid.server.logging.logback.validator;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFactory;
+import org.apache.qpid.server.model.ConfiguredObjectTypeRegistry;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.plugin.ConfiguredObjectRegistration;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class TestModel extends Model
+{
+    public static final TestModel MODEL = new TestModel().init();
+
+    private static final ConfiguredObjectRegistration REGISTRATION = new ConfiguredObjectRegistration()
+    {
+        @Override
+        public Collection<Class<? extends ConfiguredObject>> getConfiguredObjectClasses()
+        {
+            return Collections.singleton(TestConfiguredObject.class);
+        }
+
+        @Override
+        public String getType()
+        {
+            return null;
+        }
+    };
+
+    private TestConfiguredObjectFactory _factory;
+
+    private ConfiguredObjectTypeRegistry _registry;
+
+    private TestModel()
+    {
+        super();
+    }
+
+    private TestModel init()
+    {
+        _factory = new TestConfiguredObjectFactory(this);
+        _registry = new ConfiguredObjectTypeRegistry(
+                Collections.singleton(REGISTRATION),
+                Collections.emptySet(),
+                Collections.singleton(TestConfiguredObject.class),
+                _factory);
+        return this;
+    }
+
+    @Override
+    public Collection<Class<? extends ConfiguredObject>> getSupportedCategories()
+    {
+        return Collections.singletonList(TestConfiguredObject.class);
+    }
+
+    @Override
+    public Collection<Class<? extends ConfiguredObject>> getChildTypes(Class<? extends ConfiguredObject> parent)
+    {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Class<? extends ConfiguredObject> getRootCategory()
+    {
+        return TestConfiguredObject.class;
+    }
+
+    @Override
+    public Class<? extends ConfiguredObject> getParentType(Class<? extends ConfiguredObject> child)
+    {
+        return null;
+    }
+
+    @Override
+    public int getMajorVersion()
+    {
+        return 1;
+    }
+
+    @Override
+    public int getMinorVersion()
+    {
+        return 1;
+    }
+
+    @Override
+    public ConfiguredObjectFactory getObjectFactory()
+    {
+        return _factory;
+    }
+
+    @Override
+    public ConfiguredObjectTypeRegistry getTypeRegistry()
+    {
+        return _registry;
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/util/ArrayUtilsTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/util/ArrayUtilsTest.java
new file mode 100644
index 0000000..f4fac79
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/util/ArrayUtilsTest.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.qpid.server.util;
+
+import junit.framework.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+
+public class ArrayUtilsTest extends TestCase
+{
+    @Test
+    public void testClone_NullInput()
+    {
+        assertNull(ArrayUtils.clone(null));
+    }
+
+    @Test
+    public void testClone_EmptyInput()
+    {
+        String[] data = new String[0];
+        String[] result = ArrayUtils.clone(data);
+        assertArrayEquals(data, result);
+        assertNotSame(data, result);
+    }
+
+    @Test
+    public void testClone()
+    {
+        String[] data = new String[]{"A", null, "B", "CC"};
+        String[] result = ArrayUtils.clone(data);
+        assertArrayEquals(data, result);
+        assertNotSame(data, result);
+    }
+
+    @Test
+    public void testIsEmpty_NullInput()
+    {
+        assertTrue(ArrayUtils.isEmpty(null));
+    }
+
+    @Test
+    public void testIsEmpty_EmptyInput()
+    {
+        assertTrue(ArrayUtils.isEmpty(new String[0]));
+    }
+
+    @Test
+    public void testIsEmpty()
+    {
+        assertFalse(ArrayUtils.isEmpty(new String[]{"A"}));
+        assertFalse(ArrayUtils.isEmpty(new String[]{"A", "B"}));
+        assertFalse(ArrayUtils.isEmpty(new String[]{null}));
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/resources/addLogger.html b/broker-plugins/management-http/src/main/java/resources/addLogger.html
index c0aa6b5..2b9cbe4 100644
--- a/broker-plugins/management-http/src/main/java/resources/addLogger.html
+++ b/broker-plugins/management-http/src/main/java/resources/addLogger.html
@@ -20,8 +20,8 @@
 <div class="dijitHidden">
     <div data-dojo-type="dijit/Dialog" data-dojo-props="title:'Add Logger'" id="addLogger">
         <form id="addLogger.form" method="post" data-dojo-type="dijit/form/Form">
-            <div class="hidden infoPane" id="brokerLoggerEditWarning">Changes will only take effect after Broker restart.</div>
-            <div class="hidden infoPane" id="virtualHostlLoggerEditWarning">Changes will only take effect after VirtualHost restart.</div>
+            <div class="hidden loggerInfoPane" id="brokerLoggerEditWarning">Changes will only take effect after Broker restart.</div>
+            <div class="hidden loggerInfoPane" id="virtualHostlLoggerEditWarning">Changes will only take effect after VirtualHost restart.</div>
             <div id="addLogger.contentPane">
                 <div class="clear">
                     <div class="formLabel-labelCell tableContainer-labelCell">Name*:</div>
diff --git a/broker-plugins/management-http/src/main/java/resources/css/common.css b/broker-plugins/management-http/src/main/java/resources/css/common.css
index 4646275..8001d37 100644
--- a/broker-plugins/management-http/src/main/java/resources/css/common.css
+++ b/broker-plugins/management-http/src/main/java/resources/css/common.css
@@ -384,6 +384,18 @@ h1 {
     height:100%;
 }
 
+.loggerInfoPane
+{
+    margin-left: 5px;
+    margin-bottom: 5px;
+    padding: 5px 5px 5px 1.2em;
+    font-style: italic;
+    background:url("../images/notification.svg") no-repeat left center;
+    background-size: 1em;
+    width:100%;
+    height:1em;
+}
+
 .dgrid-column-selected
 {
     width: 2em;
@@ -839,3 +851,10 @@ td.advancedSearchField, col.autoWidth {
 .queueMessages .field-size { width: 20%;}
 .queueMessages .field-state { width: 20%; }
 .queueMessages .field-arrivalTime { width: auto }
+
+.mapList-scroll-y
+{
+    height: 5em;
+    overflow-y: auto;
+    overflow-x: visible;
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/common/MapInputWidget.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/common/MapInputWidget.js
new file mode 100644
index 0000000..94109bc
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/common/MapInputWidget.js
@@ -0,0 +1,450 @@
+/*
+ * 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.
+ *
+ */
+
+define("qpid/common/MapInputWidget", [
+    "dojo/_base/declare",
+    "dojo/dom-construct",
+    "dojo/query",
+    "dojo/_base/event",
+    "dojox/html/entities",
+    "dijit",
+    "dijit/registry",
+    "dijit/form/Form",
+], function (declare, domConstruct, query, event, entities, dijit, registry, Form)
+{
+    return declare("qpid.common.MapInputWidget", [Form],
+        {
+            value: {},
+
+            _setValueAttr: function (obj)
+            {
+                if (typeof obj == "object")
+                {
+                    this.value = obj;
+                    this._widgetValue = this._initWidgetValue();
+                    this._listContainers().forEach(container => this._initListContainer(container));
+                }
+            },
+
+            _getValueAttr()
+            {
+                return this._widgetValue();
+            },
+
+            _initWidgetValue()
+            {
+                const widgetValue = {};
+
+                for (let [key, value] of Object.entries(this.value || {}))
+                {
+                    const trimmedKey = key.trim();
+                    if (trimmedKey.length)
+                    {
+                        widgetValue[trimmedKey] = value;
+                    }
+                }
+                this.value = widgetValue;
+
+                return () => this.value;
+            },
+
+            _widgetValue()
+            {
+                return (this._widgetValue = this._initWidgetValue())();
+            },
+
+            _getWidgetAttribute(widget, name)
+            {
+                const attribute = widget.get(name);
+                return attribute != "undefined" ? attribute : undefined;
+            },
+
+            _getWidgetAttributeAsString(widget, name)
+            {
+                const attribute = widget.get(name);
+                if (attribute !== undefined && attribute !== null)
+                {
+                    const str = String(attribute).trim();
+                    return str !== "undefined" ? str : "";
+                }
+                return "";
+            },
+
+            _getWidgetName(widget)
+            {
+                return this._getWidgetAttributeAsString(widget, "name");
+            },
+
+            _findNodes(className, domNode)
+            {
+                if (domNode)
+                {
+                    return query("." + className, domNode) || [];
+                }
+                return [];
+            },
+
+            _initWidgetName()
+            {
+                const name = this._getWidgetName(this);
+                return () => name;
+            },
+
+            _widgetName()
+            {
+                return (this._widgetName = this._initWidgetName())();
+            },
+
+            _initKeyClass()
+            {
+                const className = this._getWidgetAttributeAsString(this, "keyClass");
+                if (!className.length)
+                {
+                    return () => "key";
+                }
+                return () => className;
+
+            },
+
+            _keyClass()
+            {
+                return (this._keyClass = this._initKeyClass())();
+            },
+
+            _initValueClass()
+            {
+                const className = this._getWidgetAttributeAsString(this, "valueClass");
+                if (!className.length)
+                {
+                    return () => "value";
+                }
+                return () => className;
+            },
+
+            _valueClass()
+            {
+                return (this._valueClass = this._initValueClass())();
+            },
+
+            _initListClass()
+            {
+                const className = this._getWidgetAttributeAsString(this, "listClass");
+                if (!className.length)
+                {
+                    return () => "keyValueList";
+                }
+                return () => className;
+            },
+
+            _listClass()
+            {
+                return (this._listClass = this._initListClass())();
+            },
+
+            _getWidgetById(attributeName)
+            {
+                const id = this._getWidgetAttribute(this, attributeName);
+                if (id)
+                {
+                    return registry.byId(id, this.domNode);
+                }
+                return undefined;
+            },
+
+            _getWidgetByName(name)
+            {
+                const childName = this._widgetName() + "." + name;
+                for (let childWidget of this.getChildren() || [])
+                {
+                    if (this._getWidgetName(childWidget) === childName)
+                    {
+                        return childWidget;
+                    }
+                }
+                return undefined;
+            },
+
+            _newSupplier(widget)
+            {
+                if (!widget)
+                {
+                    return () => null;
+                }
+                if (widget instanceof dijit.form.RadioButton)
+                {
+                    return () => this._getWidgetAttribute(widget, "checked") ? this._getWidgetAttribute(widget, "value") : null;
+                }
+                if (widget instanceof dijit.form.CheckBox)
+                {
+                    return () => this._getWidgetAttribute(widget, "checked");
+                }
+                return () => this._getWidgetAttribute(widget, "value");
+            },
+
+            _widget: Symbol("widget"),
+
+            _reset: Symbol("reset"),
+
+            _decorateWithReset(obj)
+            {
+                if (["function", "object"].includes(typeof obj))
+                {
+                    const widget = obj[this._widget];
+                    if (widget)
+                    {
+                        if (typeof widget.reset === "function")
+                        {
+                            obj[this._reset] = () => widget.reset();
+                            return obj;
+                        }
+                    }
+                    obj[this._reset] = () =>
+                    {
+                    }
+                }
+                return obj;
+            },
+
+            _initKeySupplier()
+            {
+                let widget = this._getWidgetById("keySupplierId");
+                if (!widget)
+                {
+                    widget = this._getWidgetByName(this._keyClass());
+                }
+                if (widget)
+                {
+                    const rawSupplier = this._newSupplier(widget);
+                    const supplier = function ()
+                    {
+                        let result = rawSupplier();
+                        if (result)
+                        {
+                            result = String(result).trim();
+                            return result.length ? result : null;
+                        }
+                        return null;
+                    }
+                    supplier[this._widget] = widget;
+                    return this._decorateWithReset(supplier);
+                }
+                return this._decorateWithReset(() => null);
+            },
+
+            _keySupplier()
+            {
+                return (this._keySupplier = this._initKeySupplier())();
+            },
+
+            _initValueSupplier()
+            {
+                let widget = this._getWidgetById("valueSupplierId");
+                if (!widget)
+                {
+                    widget = this._getWidgetByName(this._valueClass());
+                }
+                if (widget)
+                {
+                    const rawSupplier = this._newSupplier(widget);
+                    const supplier = function ()
+                    {
+                        const value = rawSupplier();
+                        if (typeof value == "string" && !value.length)
+                        {
+                            return null;
+                        }
+                        return value;
+                    }
+                    supplier[this._widget] = widget;
+                    return this._decorateWithReset(supplier);
+                }
+                return this._decorateWithReset(() => null);
+            },
+
+            _valueSupplier()
+            {
+                return (this._valueSupplier = this._initValueSupplier())();
+            },
+
+            _initKeyValueTemplate()
+            {
+                let template = '<div class="clear">' +
+                    '<div class="key formLabel-labelCell">:</div>' +
+                    '<div class="value formValue-valueCell"></div>' +
+                    '</div>';
+                const file = this._getWidgetAttribute(this, "keyValueTemplate");
+                if (file)
+                {
+                    try
+                    {
+                        require(["dojo/text!" + file], t =>
+                        {
+                            if (t)
+                            {
+                                template = t;
+                            }
+                        });
+                    }
+                    catch (e)
+                    {
+                        console.warn(e);
+                    }
+                }
+                return () => template;
+            },
+
+            _keyValueTemplate()
+            {
+                return (this._keyValueTemplate = this._initKeyValueTemplate())();
+            },
+
+            _insertNodeToContainer: Symbol("insertNode"),
+
+            _deleteNodeFromContainer: Symbol("deleteNode"),
+
+            _initListContainer(container)
+            {
+                domConstruct.empty(container);
+
+                const containerDomNodes = {};
+                const mapWidget = this;
+                const insertNode = function (key, value)
+                {
+                    let newDomNode;
+                    if (containerDomNodes[key])
+                    {
+                        newDomNode = domConstruct.place(mapWidget._keyValueTemplate(), containerDomNodes[key], "replace");
+                    }
+                    else
+                    {
+                        newDomNode = domConstruct.place(mapWidget._keyValueTemplate(), container, "last");
+                    }
+                    containerDomNodes[key] = newDomNode;
+                    mapWidget._findNodes(mapWidget._keyClass(), newDomNode).forEach(function (node)
+                    {
+                        const innerHTML = node.innerHTML;
+                        if (typeof innerHTML === "string")
+                        {
+                            node.innerHTML = entities.encode(key) + innerHTML.trim();
+                        }
+                        else
+                        {
+                            node.innerHTML = entities.encode(key);
+                        }
+                    });
+                    mapWidget._findNodes(mapWidget._valueClass(), newDomNode).forEach(node => node.innerHTML = entities.encode(String(value)));
+                }
+                container[this._insertNodeToContainer] = insertNode;
+
+                container[this._deleteNodeFromContainer] = function (key)
+                {
+                    if (key && containerDomNodes[key])
+                    {
+                        domConstruct.destroy(containerDomNodes[key]);
+                        delete containerDomNodes[key];
+                    }
+                }
+
+                for (let [key, value] of Object.entries(this._widgetValue()))
+                {
+                    insertNode(key, value);
+                }
+            },
+
+            _initListContainers()
+            {
+                const containers = this._findNodes(this._listClass(), this.domNode);
+                containers.forEach(container => this._initListContainer(container));
+                return () => containers;
+            },
+
+            _listContainers()
+            {
+                return (this._listContainers = this._initListContainers())();
+            },
+
+            _addKeyValueItem()
+            {
+                const key = this._keySupplier();
+                const value = this._valueSupplier();
+                if (key && value)
+                {
+                    this._listContainers().forEach(container => container[this._insertNodeToContainer](key, value));
+                    this._widgetValue()[key] = value;
+                    this._keySupplier[this._reset]();
+                    this._valueSupplier[this._reset]();
+                }
+            },
+
+            _deleteKeyValueItem(key)
+            {
+                if (key && this.value[key])
+                {
+                    delete this.value[key];
+                }
+                this._listContainers().forEach(container => container[this._deleteNodeFromContainer](key));
+            },
+
+            _initWidget()
+            {
+                this._keyClass = this._initKeyClass();
+                this._valueClass = this._initValueClass();
+                this._keySupplier = this._initKeySupplier();
+                this._keyValueTemplate = this._initKeyValueTemplate();
+                this._valueSupplier = this._initValueSupplier();
+                this._listContainers = this._initListContainers();
+                this._initWidget = () =>
+                {
+                };
+            },
+
+            onSubmit()
+            {
+                this.inherited(arguments);
+                if (this.isValid())
+                {
+                    this._addKeyValueItem();
+                }
+                return false;
+            },
+
+            onReset()
+            {
+                this.inherited(arguments);
+                const key = this._keySupplier();
+                if (key)
+                {
+                    this._deleteKeyValueItem(key);
+                }
+                else
+                {
+                    this.value = {};
+                    this._listContainers().forEach(container => this._initListContainer(container));
+                }
+                return true;
+            },
+
+            startup()
+            {
+                this.inherited(arguments);
+                this._initWidget();
+            }
+        });
+});
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
index 3cef2b7..8b88d2b 100644
--- a/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js
@@ -1112,24 +1112,64 @@ define(["dojo/_base/xhr",
             return containerObject;
         };
 
-        util.updateAttributeNodes = function (containerObject, restData) {
+        util.updateBooleanAttributeNode = function (containerObject, restData, util) {
+            containerObject.containerNode.innerHTML = util.buildCheckboxMarkup(restData);
+        }
+
+        util.updateMapAttributeNode = function (containerObject, restData, util, template, key = "key", value = "value") {
+            if (!template)
+            {
+                return;
+            }
+            dom.empty(containerObject.containerNode);
+            for (let [restRecordKey, restRecordValue] of Object.entries(restData))
+            {
+                let newNode = dom.place(template, containerObject.containerNode, "last");
+
+                util.findNode(key, newNode).forEach(node => {
+                    let innerHTML = node.innerHTML;
+                    if (typeof innerHTML === "string")
+                    {
+                        node.innerHTML = entities.encode(String(restRecordKey)) + innerHTML.trim();
+                    } else {
+                        node.innerHTML = entities.encode(String(restRecordKey));
+                    }
+                });
+
+                util.findNode(value, newNode).forEach(node => node.innerHTML = entities.encode(String(restRecordValue)));
+            }
+        }
+
+        util.findNode = function (key, containerNode)
+        {
+            if (containerNode)
+            {
+                return query("." + key, containerNode) || [];
+            }
+            return [];
+        }
+
+        util.updateAttributeNodes = function (containerObject, restData, booleanUpdater = util.updateBooleanAttributeNode, mapUpdater = null)
+        {
             for (var attrName in containerObject)
             {
                 if (containerObject.hasOwnProperty(attrName) && attrName in restData)
                 {
-                    var content = "";
-                    if (containerObject[attrName].attributeType === "Boolean")
+                    if (containerObject[attrName].attributeType === "Boolean" && typeof booleanUpdater === "function")
+                    {
+                        booleanUpdater(containerObject[attrName], restData[attrName], util);
+                    }
+                    else if (containerObject[attrName].attributeType === "Map" && typeof mapUpdater === "function")
                     {
-                        content = util.buildCheckboxMarkup(restData[attrName]);
+                        mapUpdater(containerObject[attrName], restData[attrName], util)
                     }
                     else
                     {
-                        content = entities.encode(String(restData[attrName]));
+                        containerObject[attrName].containerNode.innerHTML = entities.encode(String(restData[attrName]));
                     }
-                    containerObject[attrName].containerNode.innerHTML = content;
                 }
             }
-        };
+        }
 
         return util;
     });
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/common/widgetconfigurer.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/common/widgetconfigurer.js
index 4a8f22d..f5a452b 100644
--- a/broker-plugins/management-http/src/main/java/resources/js/qpid/common/widgetconfigurer.js
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/common/widgetconfigurer.js
@@ -190,7 +190,7 @@ define(["dojo/_base/xhr",
                     widget.defaultValue = defaultValue;
                     if ( widget instanceof dijit.form.CheckBox)
                     {
-                        widget.set("checked", defaultValue === true);
+                        widget.set("checked", defaultValue === true || defaultValue === "true");
                     }
                 }
             }
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addLogger.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addLogger.js
index 9c368ae..1739d0e 100644
--- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addLogger.js
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addLogger.js
@@ -22,6 +22,7 @@ define(["dojo/_base/lang",
         "dojo/dom",
         "dojo/dom-construct",
         "dojo/dom-style",
+        "dojo/dom-geometry",
         "dijit/registry",
         "dojo/parser",
         "dojo/store/Memory",
@@ -45,12 +46,13 @@ define(["dojo/_base/lang",
         "dijit/layout/ContentPane",
         "dojox/layout/TableContainer",
         "dojo/domReady!"],
-    function (lang, dom, construct, domStyle, registry, parser, memory, array, event, json, util, template)
+    function (lang, dom, construct, domStyle, domGeometry, registry, parser, memory, array, event, json, util, template)
     {
-        var addLogger = {
+        const addLogger = {
             init: function ()
             {
-                var that = this;
+                const that = this;
+                this.initDocument = document.documentElement;
                 this.containerNode = construct.create("div", {innerHTML: template});
                 parser.parse(this.containerNode)
                     .then(function (instances)
@@ -60,7 +62,7 @@ define(["dojo/_base/lang",
             },
             _postParse: function ()
             {
-                var that = this;
+                const that = this;
                 this.name = registry.byId("addLogger.name");
                 this.name.set("regExpGen", util.nameOrContextVarRegexp);
 
@@ -117,8 +119,8 @@ define(["dojo/_base/lang",
                     this._configure(actualData.type);
                 }
 
-                var brokerLoggerEditWarningNode = dom.byId("brokerLoggerEditWarning");
-                var virtualHostlLoggerEditWarningNode = dom.byId("virtualHostlLoggerEditWarning");
+                const brokerLoggerEditWarningNode = dom.byId("brokerLoggerEditWarning");
+                const virtualHostlLoggerEditWarningNode = dom.byId("virtualHostlLoggerEditWarning");
                 domStyle.set(brokerLoggerEditWarningNode,
                     "display",
                     !this.isNew && this.category === "BrokerLogger" ? "block" : "none");
@@ -126,13 +128,15 @@ define(["dojo/_base/lang",
                     "display",
                     !this.isNew && this.category === "VirtualHostLogger" ? "block" : "none");
 
-                util.loadEffectiveAndInheritedActualData(this.management, this.modelObj, lang.hitch(this, function(data)
+                util.loadEffectiveAndInheritedActualData(this.management, this.modelObj, lang.hitch(this, function (data)
                 {
-                    this.context.setData(this.isNew ? {} : this.initialData.context ,
+                    this.context.setData(this.isNew ? {} : this.initialData.context,
                         data.effective.context,
                         data.inheritedActual.context);
                     this._loadCategoryUserInterfacesAndShowDialog(actualData);
                 }));
+                this._resetSize();
+                this.dialog.show();
             },
             hide: function ()
             {
@@ -154,12 +158,12 @@ define(["dojo/_base/lang",
             {
                 if (this.form.validate())
                 {
-                    var excludedData = this.initialData
-                                       || this.management.metadata.getDefaultValueForType(this.category,
+                    const excludedData = this.initialData
+                        || this.management.metadata.getDefaultValueForType(this.category,
                             this.loggerType.get("value"));
-                    var formData = util.getFormWidgetValues(this.form, excludedData);
-                    var context = this.context.get("value");
-                    var oldContext = null;
+                    const formData = util.getFormWidgetValues(this.form, excludedData);
+                    const context = this.context.get("value");
+                    let oldContext = null;
                     if (this.initialData !== null && this.initialData !== undefined)
                     {
                         oldContext = this.initialData.context;
@@ -168,7 +172,7 @@ define(["dojo/_base/lang",
                     {
                         formData["context"] = context;
                     }
-                    var that = this;
+                    const that = this;
                     if (this.isNew)
                     {
                         this.management.create(this.category, this.modelObj, formData)
@@ -193,7 +197,7 @@ define(["dojo/_base/lang",
             },
             _destroyTypeFields: function (typeFieldsContainer)
             {
-                var widgets = registry.findWidgets(typeFieldsContainer);
+                const widgets = registry.findWidgets(typeFieldsContainer);
                 array.forEach(widgets, function (item)
                 {
                     item.destroyRecursive();
@@ -206,14 +210,15 @@ define(["dojo/_base/lang",
 
                 if (type)
                 {
+                    const [maxHeight, maxWidth] = this._calculateMaxHeight(this.dialog, this.form);
                     this._configure(type);
-                    var that = this;
+                    const that = this;
                     require(["qpid/management/logger/" + this.category.toLowerCase() + "/" + type.toLowerCase()
-                             + "/add"], function (typeUI)
+                    + "/add"], function (typeUI)
                     {
                         try
                         {
-                            var promise = typeUI.show({
+                            const promise = typeUI.show({
                                 containerNode: that.typeFieldsContainer,
                                 data: that.initialData,
                                 metadata: that.management.metadata,
@@ -230,6 +235,8 @@ define(["dojo/_base/lang",
                                         type,
                                         that.initialData,
                                         that.management.metadata);
+                                    that._setFormOverflow(maxHeight, maxWidth, typeUI);
+                                    that._setPosition();
                                 });
                             }
                         }
@@ -239,25 +246,30 @@ define(["dojo/_base/lang",
                         }
                     });
                 }
+                else
+                {
+                    this._resetFormOverflow();
+                    this._setPosition();
+                }
             },
             _configure: function (type)
             {
                 if (!this.configured)
                 {
-                    var metadata = this.management.metadata;
+                    const metadata = this.management.metadata;
                     util.applyToWidgets(this.allFieldsContainer, this.category, type, this.initialData, metadata);
                     this.configured = true;
                 }
             },
             _loadCategoryUserInterfacesAndShowDialog: function (actualData)
             {
-                var that = this;
-                var node = construct.create("div", {}, this.categoryFieldsContainer);
+                const that = this;
+                const node = construct.create("div", {}, this.categoryFieldsContainer);
                 require(["qpid/management/logger/" + this.category.toLowerCase() + "/add"], function (categoryUI)
                 {
                     try
                     {
-                        var promise = categoryUI.show({
+                        const promise = categoryUI.show({
                             containerNode: node,
                             data: actualData
                         });
@@ -283,6 +295,53 @@ define(["dojo/_base/lang",
                         console.error(e);
                     }
                 });
+            },
+            _calculateMaxHeight: function ()
+            {
+                const loggerGeometry = domGeometry.getMarginBox(this.dialog.domNode);
+                const formGeometry = domGeometry.getContentBox(this.form.domNode);
+                const documentGeometry = domGeometry.getContentBox(this.initDocument);
+                const maxHeight = documentGeometry.h - (loggerGeometry.h - formGeometry.h) - 7;
+                const maxWidth = documentGeometry.w - (loggerGeometry.w - formGeometry.w) - 7;
+                return [maxHeight, maxWidth];
+            },
+            _setFormOverflow: function (maxHeight, maxWidth, typeUI)
+            {
+                const formNode = this.form.domNode;
+                if (formNode.clientHeight > maxHeight || formNode.clientWidth > maxWidth)
+                {
+                    if (typeUI && typeof typeUI.doNotScroll === "function")
+                    {
+                        typeUI.doNotScroll(this.typeFieldsContainer);
+                    }
+                    domStyle.set(formNode, {
+                        maxWidth: maxWidth + "px",
+                        maxHeight: maxHeight + "px",
+                        overflow: "scroll"
+                    });
+                    domStyle.set(this.dialog.domNode, {height: "initial", weight: "initial"});
+                }
+                else
+                {
+                    this._resetFormOverflow();
+                }
+            },
+            _resetFormOverflow: function ()
+            {
+                domStyle.set(this.form.domNode, {maxWidth: "", maxHeight: "", overflow: "initial"});
+                this._resetSize();
+            },
+            _resetSize: function ()
+            {
+                domStyle.set(this.form.domNode, {height: "initial", weight: "initial"});
+                domStyle.set(this.dialog.domNode, {height: "initial", weight: "initial"});
+            },
+            _setPosition: function ()
+            {
+                const rectangle = this.dialog.domNode.getBoundingClientRect();
+                const top = Math.round((this.initDocument.offsetHeight - rectangle.height) / 2.0);
+                const left = Math.round((this.initDocument.offsetWidth - rectangle.width) / 2.0);
+                domStyle.set(this.dialog.domNode, {position: "fixed", top: top + "px", left: left + "px"});
             }
         };
 
diff --git a/broker/pom.xml b/broker/pom.xml
index 61ca655..4f2ae15 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -28,6 +28,25 @@
   <name>Apache Qpid Broker-J</name>
   <description>Broker configuration and executable</description>
 
+  <profiles>
+    <profile>
+      <id>graylog</id>
+      <activation>
+        <property>
+          <name>graylog</name>
+        </property>
+      </activation>
+
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.qpid</groupId>
+          <artifactId>qpid-broker-plugins-graylog-logging-logback</artifactId>
+          <scope>runtime</scope>
+        </dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+
   <dependencies>
     <dependency>
       <groupId>org.apache.qpid</groupId>
diff --git a/doc/java-broker/src/docbkx/runtime/Java-Broker-Runtime-Log-Files.xml b/doc/java-broker/src/docbkx/runtime/Java-Broker-Runtime-Log-Files.xml
index d1f93da..a4ba2b8 100644
--- a/doc/java-broker/src/docbkx/runtime/Java-Broker-Runtime-Log-Files.xml
+++ b/doc/java-broker/src/docbkx/runtime/Java-Broker-Runtime-Log-Files.xml
@@ -168,6 +168,15 @@
         default the circular buffer holds the last 4096 log events. The contents of the buffer can
         be viewed via Management. See <xref linkend="Java-Broker-Runtime-Logging-Management-MemoryLogger"/></para>
     </section>
+    <section xml:id="Java-Broker-Runtime-Logging-Loggers-GraylogLogger">
+      <title>GraylogLogger</title>
+      <para><emphasis>GraylogLogger</emphasis> - sends log messages to a Graylog server in
+        <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://docs.graylog.org/en/3.2/pages/gelf.html">GELF format</link> via TCP.
+        The hostname and port number of the Graylog server has to be configured. The content of the log messages is also configurable.</para>
+      <para>The Broker has to be built using option <code>-Dgraylog</code> to support Graylog logger, please visit
+        <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://github.com/apache/qpid-broker-j/blob/master/doc/developer-guide/src/main/markdown/build-instructions.md#maven-commands">build instructions</link>.
+      </para>
+    </section>
   </section>
   <section xml:id="Java-Broker-Runtime-Logging-InclusionRules">
     <title>Inclusion Rules</title>
diff --git a/pom.xml b/pom.xml
index bafdcdc..2362bb0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -359,6 +359,12 @@
 
       <dependency>
         <groupId>org.apache.qpid</groupId>
+        <artifactId>qpid-broker-plugins-graylog-logging-logback</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.qpid</groupId>
         <artifactId>qpid-broker-plugins-memory-store</artifactId>
         <version>${project.version}</version>
       </dependency>
@@ -643,6 +649,7 @@
         <artifactId>dgrid</artifactId>
         <version>${dgrid-version}</version>
       </dependency>
+
       <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
@@ -1623,6 +1630,27 @@
         </plugins>
       </build>
     </profile>
+
+    <profile>
+      <id>graylog</id>
+      <activation>
+        <property>
+          <name>graylog</name>
+        </property>
+      </activation>
+
+      <modules>
+        <module>broker-plugins/graylog-logging-logback</module>
+      </modules>
+      <properties>
+        <graylog/>
+        <profile>java-mms.1-0</profile>
+        <profile.broker.version>1.0</profile.broker.version>
+        <profile.virtualhostnode.type>Memory</profile.virtualhostnode.type>
+        <profile.virtualhostnode.context.blueprint>{"type":"ProvidedStore","globalAddressDomains":"${dollar.sign}{qpid.globalAddressDomains}"}</profile.virtualhostnode.context.blueprint>
+        <profile.qpid.tests.mms.messagestore.persistence>true</profile.qpid.tests.mms.messagestore.persistence>
+      </properties>
+    </profile>
   </profiles>
 </project>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 03/05: QPID-8368: [Broker-J] Make GrayLogger implementations conditionally available

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

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit e89981067878c904786bb7ff1fb6aa51a002b8a9
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Sat Aug 22 22:07:27 2020 +0100

    QPID-8368: [Broker-J] Make GrayLogger implementations conditionally available
---
 .../logging/logback/BrokerGraylogLoggerImpl.java   |  3 +-
 .../qpid/server/logging/logback/GelfCheck.java     | 37 ++++++++++++++++++++++
 .../logback/VirtualHostGraylogLoggerImpl.java      |  3 +-
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
index 420d979..e75a6fd 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
@@ -41,7 +41,8 @@ import java.util.Set;
 public class BrokerGraylogLoggerImpl extends AbstractBrokerLogger<BrokerGraylogLoggerImpl>
         implements BrokerGraylogLogger<BrokerGraylogLoggerImpl>
 {
-    @ManagedObjectFactoryConstructor
+    @ManagedObjectFactoryConstructor(conditionallyAvailable = true,
+            condition = "org.apache.qpid.server.logging.logback.GelfCheck#isAvailable()")
     public BrokerGraylogLoggerImpl(Map<String, Object> attributes, Broker<?> broker)
     {
         super(attributes, broker);
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfCheck.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfCheck.java
new file mode 100644
index 0000000..3b21892
--- /dev/null
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfCheck.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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.qpid.server.logging.logback;
+
+public class GelfCheck
+{
+    public static boolean isAvailable()
+    {
+        try
+        {
+            Class.forName("de.siegmar.logbackgelf.GelfEncoder");
+            return true;
+        }
+        catch (ClassNotFoundException | NoClassDefFoundError e)
+        {
+            return false;
+        }
+    }
+}
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
index de95e63..0a4f38c 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
@@ -92,7 +92,8 @@ public class VirtualHostGraylogLoggerImpl extends AbstractVirtualHostLogger<Virt
 
     private AsyncAppender _appender;
 
-    @ManagedObjectFactoryConstructor
+    @ManagedObjectFactoryConstructor(conditionallyAvailable = true,
+            condition = "org.apache.qpid.server.logging.logback.GelfCheck#isAvailable()")
     public VirtualHostGraylogLoggerImpl(Map<String, Object> attributes, VirtualHost<?> virtualHost)
     {
         super(attributes, virtualHost);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 02/05: QPID-8368: [Broker-J] Move ManagedObject annotations from interfaces to implementations

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

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 4f6de183f3cede807814d865cc9a19072d0206e1
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Sat Aug 22 22:01:44 2020 +0100

    QPID-8368: [Broker-J] Move ManagedObject annotations from interfaces to implementations
---
 .../apache/qpid/server/logging/logback/BrokerGraylogLogger.java  | 4 ----
 .../qpid/server/logging/logback/BrokerGraylogLoggerImpl.java     | 8 +++++++-
 .../qpid/server/logging/logback/VirtualHostGraylogLogger.java    | 5 -----
 .../server/logging/logback/VirtualHostGraylogLoggerImpl.java     | 9 ++++++++-
 4 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java
index 4617672..0a24c32 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java
@@ -21,11 +21,7 @@
 package org.apache.qpid.server.logging.logback;
 
 import org.apache.qpid.server.model.BrokerLogger;
-import org.apache.qpid.server.model.ManagedObject;
 
-@ManagedObject(category = false, type = GraylogLogger.TYPE,
-        description = "Logger implementation that writes log events to a remote graylog server",
-        validChildTypes = "org.apache.qpid.server.logging.logback.AbstractLogger#getSupportedBrokerLoggerChildTypes()")
 public interface BrokerGraylogLogger<X extends BrokerGraylogLogger<X>> extends BrokerLogger<X>, GraylogLogger<X>
 {
 }
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
index 26e4e26..420d979 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
@@ -24,16 +24,22 @@ import ch.qos.logback.classic.AsyncAppender;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.Context;
+
 import org.apache.qpid.server.logging.logback.validator.GelfConfigurationValidator;
 import org.apache.qpid.server.model.Broker;
 import org.apache.qpid.server.model.ManagedAttributeField;
+import org.apache.qpid.server.model.ManagedObject;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
 
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
-public class BrokerGraylogLoggerImpl extends AbstractBrokerLogger<BrokerGraylogLoggerImpl> implements BrokerGraylogLogger<BrokerGraylogLoggerImpl>
+@ManagedObject(category = false, type = GraylogLogger.TYPE,
+        description = "Logger implementation that writes log events to a remote graylog server",
+        validChildTypes = "org.apache.qpid.server.logging.logback.AbstractLogger#getSupportedBrokerLoggerChildTypes()")
+public class BrokerGraylogLoggerImpl extends AbstractBrokerLogger<BrokerGraylogLoggerImpl>
+        implements BrokerGraylogLogger<BrokerGraylogLoggerImpl>
 {
     @ManagedObjectFactoryConstructor
     public BrokerGraylogLoggerImpl(Map<String, Object> attributes, Broker<?> broker)
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java
index 8582cde..47c2143 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java
@@ -20,13 +20,8 @@
  */
 package org.apache.qpid.server.logging.logback;
 
-import org.apache.qpid.server.model.ManagedObject;
 import org.apache.qpid.server.model.VirtualHostLogger;
 
-@ManagedObject(category = false,
-        type = GraylogLogger.TYPE,
-        validChildTypes = "org.apache.qpid.server.logging.logback.AbstractLogger#getSupportedVirtualHostLoggerChildTypes()",
-        amqpName = "org.apache.qpid.VirtualHostGraylogLogger")
 public interface VirtualHostGraylogLogger<X extends VirtualHostGraylogLogger<X>> extends VirtualHostLogger<X>, GraylogLogger<X>
 {
 }
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
index 2c07913..de95e63 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
@@ -24,8 +24,10 @@ import ch.qos.logback.classic.AsyncAppender;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.Context;
+
 import org.apache.qpid.server.logging.logback.validator.GelfConfigurationValidator;
 import org.apache.qpid.server.model.ManagedAttributeField;
+import org.apache.qpid.server.model.ManagedObject;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
 import org.apache.qpid.server.model.VirtualHost;
 
@@ -33,7 +35,12 @@ import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
-public class VirtualHostGraylogLoggerImpl extends AbstractVirtualHostLogger<VirtualHostGraylogLoggerImpl> implements VirtualHostGraylogLogger<VirtualHostGraylogLoggerImpl>
+@ManagedObject(category = false,
+        type = GraylogLogger.TYPE,
+        validChildTypes = "org.apache.qpid.server.logging.logback.AbstractLogger#getSupportedVirtualHostLoggerChildTypes()",
+        amqpName = "org.apache.qpid.VirtualHostGraylogLogger")
+public class VirtualHostGraylogLoggerImpl extends AbstractVirtualHostLogger<VirtualHostGraylogLoggerImpl>
+        implements VirtualHostGraylogLogger<VirtualHostGraylogLoggerImpl>
 {
     @ManagedAttributeField
     private String _remoteHost;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 04/05: QPID-8368: [Broker-J] Add graylog module to the project module list and include it as a dependecy of qpid-broker

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

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 7efcddd11e7a14a5f916d3951eaf25ae4f1d7757
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Sat Aug 22 23:41:14 2020 +0100

    QPID-8368: [Broker-J] Add graylog module to the project module list and include it as a dependecy of qpid-broker
---
 broker-plugins/graylog-logging-logback/pom.xml |  7 ++-----
 broker/pom.xml                                 | 25 ++++++----------------
 pom.xml                                        | 29 ++++++++------------------
 3 files changed, 17 insertions(+), 44 deletions(-)

diff --git a/broker-plugins/graylog-logging-logback/pom.xml b/broker-plugins/graylog-logging-logback/pom.xml
index 7371dfa..dcc452d 100644
--- a/broker-plugins/graylog-logging-logback/pom.xml
+++ b/broker-plugins/graylog-logging-logback/pom.xml
@@ -31,10 +31,6 @@
   <name>Apache Qpid Broker-J LogBack GrayLog Logging Plug-in</name>
   <description>LogBack GrayLog Logging broker plug-in</description>
 
-  <properties>
-    <logback-gelf-version>3.0.0</logback-gelf-version>
-  </properties>
-
   <dependencies>
     <dependency>
       <groupId>org.apache.qpid</groupId>
@@ -56,7 +52,8 @@
     <dependency>
       <groupId>de.siegmar</groupId>
       <artifactId>logback-gelf</artifactId>
-      <version>${logback-gelf-version}</version>
+      <scope>provided</scope>
+      <optional>true</optional>
     </dependency>
 
     <dependency>
diff --git a/broker/pom.xml b/broker/pom.xml
index 4f2ae15..0d06449 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -28,25 +28,6 @@
   <name>Apache Qpid Broker-J</name>
   <description>Broker configuration and executable</description>
 
-  <profiles>
-    <profile>
-      <id>graylog</id>
-      <activation>
-        <property>
-          <name>graylog</name>
-        </property>
-      </activation>
-
-      <dependencies>
-        <dependency>
-          <groupId>org.apache.qpid</groupId>
-          <artifactId>qpid-broker-plugins-graylog-logging-logback</artifactId>
-          <scope>runtime</scope>
-        </dependency>
-      </dependencies>
-    </profile>
-  </profiles>
-
   <dependencies>
     <dependency>
       <groupId>org.apache.qpid</groupId>
@@ -176,6 +157,12 @@
       <scope>runtime</scope>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-broker-plugins-graylog-logging-logback</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
     <!-- test dependencies -->
     <dependency>
       <groupId>org.apache.qpid</groupId>
diff --git a/pom.xml b/pom.xml
index 2362bb0..522a7a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,6 +156,7 @@
     <kerby-version>2.0.0</kerby-version>
     <bcprov-version>1.64</bcprov-version>
     <bcpkix-version>1.64</bcpkix-version>
+    <logback-gelf-version>3.0.0</logback-gelf-version>
   </properties>
 
   <modules>
@@ -174,6 +175,7 @@
     <module>broker-plugins/jdbc-provider-bone</module>
     <module>broker-plugins/jdbc-store</module>
     <module>broker-plugins/jdbc-logging-logback</module>
+    <module>broker-plugins/graylog-logging-logback</module>
     <module>broker-plugins/logging-logback</module>
     <module>broker-plugins/management-amqp</module>
     <module>broker-plugins/management-http</module>
@@ -825,6 +827,13 @@
         <artifactId>bcpkix-jdk15on</artifactId>
         <version>${bcpkix-version}</version>
       </dependency>
+
+      <dependency>
+        <groupId>de.siegmar</groupId>
+        <artifactId>logback-gelf</artifactId>
+        <version>${logback-gelf-version}</version>
+      </dependency>
+
     </dependencies>
   </dependencyManagement>
 
@@ -1631,26 +1640,6 @@
       </build>
     </profile>
 
-    <profile>
-      <id>graylog</id>
-      <activation>
-        <property>
-          <name>graylog</name>
-        </property>
-      </activation>
-
-      <modules>
-        <module>broker-plugins/graylog-logging-logback</module>
-      </modules>
-      <properties>
-        <graylog/>
-        <profile>java-mms.1-0</profile>
-        <profile.broker.version>1.0</profile.broker.version>
-        <profile.virtualhostnode.type>Memory</profile.virtualhostnode.type>
-        <profile.virtualhostnode.context.blueprint>{"type":"ProvidedStore","globalAddressDomains":"${dollar.sign}{qpid.globalAddressDomains}"}</profile.virtualhostnode.context.blueprint>
-        <profile.qpid.tests.mms.messagestore.persistence>true</profile.qpid.tests.mms.messagestore.persistence>
-      </properties>
-    </profile>
   </profiles>
 </project>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 05/05: QPID-8368: [Broker-J] Move graylog loggers into graylog package

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

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 03243548847eaae1603bc5f809eb8d4a03079329
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Sun Aug 23 00:33:00 2020 +0100

    QPID-8368: [Broker-J] Move graylog loggers into graylog package
    
    This closes #52
---
 .../server/logging/logback/{ => graylog}/BrokerGraylogLogger.java    | 2 +-
 .../logging/logback/{ => graylog}/BrokerGraylogLoggerImpl.java       | 5 +++--
 .../logging/logback/{ => graylog}/GelfAppenderConfiguration.java     | 2 +-
 .../server/logging/logback/{ => graylog}/GelfAppenderDefaults.java   | 2 +-
 .../apache/qpid/server/logging/logback/{ => graylog}/GelfCheck.java  | 2 +-
 .../logging/logback/{ => graylog}/GelfEncoderConfiguration.java      | 2 +-
 .../server/logging/logback/{ => graylog}/GelfEncoderDefaults.java    | 2 +-
 .../qpid/server/logging/logback/{ => graylog}/GraylogAppender.java   | 2 +-
 .../qpid/server/logging/logback/{ => graylog}/GraylogLogger.java     | 2 +-
 .../logging/logback/{ => graylog}/VirtualHostGraylogLogger.java      | 2 +-
 .../logging/logback/{ => graylog}/VirtualHostGraylogLoggerImpl.java  | 5 +++--
 .../server/logging/logback/validator/GelfConfigurationValidator.java | 2 +-
 .../server/logging/logback/{ => graylog}/GraylogAppenderTest.java    | 2 +-
 .../logging/logback/validator/GelfConfigurationValidatorTest.java    | 2 +-
 14 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLogger.java
similarity index 94%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLogger.java
index 0a24c32..291844e 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLogger.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLogger.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import org.apache.qpid.server.model.BrokerLogger;
 
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLoggerImpl.java
similarity index 97%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLoggerImpl.java
index e75a6fd..cc22cad 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/BrokerGraylogLoggerImpl.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/BrokerGraylogLoggerImpl.java
@@ -18,13 +18,14 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import ch.qos.logback.classic.AsyncAppender;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.Context;
 
+import org.apache.qpid.server.logging.logback.AbstractBrokerLogger;
 import org.apache.qpid.server.logging.logback.validator.GelfConfigurationValidator;
 import org.apache.qpid.server.model.Broker;
 import org.apache.qpid.server.model.ManagedAttributeField;
@@ -42,7 +43,7 @@ public class BrokerGraylogLoggerImpl extends AbstractBrokerLogger<BrokerGraylogL
         implements BrokerGraylogLogger<BrokerGraylogLoggerImpl>
 {
     @ManagedObjectFactoryConstructor(conditionallyAvailable = true,
-            condition = "org.apache.qpid.server.logging.logback.GelfCheck#isAvailable()")
+            condition = "org.apache.qpid.server.logging.logback.graylog.GelfCheck#isAvailable()")
     public BrokerGraylogLoggerImpl(Map<String, Object> attributes, Broker<?> broker)
     {
         super(attributes, broker);
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderConfiguration.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderConfiguration.java
similarity index 96%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderConfiguration.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderConfiguration.java
index 7d2fc24..491e82d 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderConfiguration.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderConfiguration.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 public interface GelfAppenderConfiguration extends GelfEncoderConfiguration
 {
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderDefaults.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderDefaults.java
similarity index 97%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderDefaults.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderDefaults.java
index 6749af9..750d1e8 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfAppenderDefaults.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfAppenderDefaults.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 public enum GelfAppenderDefaults
 {
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfCheck.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfCheck.java
similarity index 95%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfCheck.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfCheck.java
index 3b21892..7c47b41 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfCheck.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfCheck.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 public class GelfCheck
 {
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderConfiguration.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderConfiguration.java
similarity index 96%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderConfiguration.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderConfiguration.java
index 500c571..d3f5c16 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderConfiguration.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderConfiguration.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import java.util.Collections;
 import java.util.Map;
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderDefaults.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderDefaults.java
similarity index 97%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderDefaults.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderDefaults.java
index 62890ec..661ed2a 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GelfEncoderDefaults.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GelfEncoderDefaults.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 public enum GelfEncoderDefaults
 {
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogAppender.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppender.java
similarity index 98%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogAppender.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppender.java
index 841bb8b..ac9e89b 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogAppender.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppender.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import ch.qos.logback.classic.AsyncAppender;
 import ch.qos.logback.classic.spi.ILoggingEvent;
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogLogger.java
similarity index 98%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogLogger.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogLogger.java
index f47230e..2282376 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/GraylogLogger.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/GraylogLogger.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import ch.qos.logback.classic.AsyncAppender;
 import org.apache.qpid.server.model.ConfiguredObject;
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLogger.java
similarity index 94%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLogger.java
index 47c2143..67094e6 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLogger.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLogger.java
@@ -18,7 +18,7 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import org.apache.qpid.server.model.VirtualHostLogger;
 
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLoggerImpl.java
similarity index 97%
rename from broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
rename to broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLoggerImpl.java
index 0a4f38c..b4965f5 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/VirtualHostGraylogLoggerImpl.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/graylog/VirtualHostGraylogLoggerImpl.java
@@ -18,13 +18,14 @@
  * under the License.
  *
  */
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import ch.qos.logback.classic.AsyncAppender;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.Context;
 
+import org.apache.qpid.server.logging.logback.AbstractVirtualHostLogger;
 import org.apache.qpid.server.logging.logback.validator.GelfConfigurationValidator;
 import org.apache.qpid.server.model.ManagedAttributeField;
 import org.apache.qpid.server.model.ManagedObject;
@@ -93,7 +94,7 @@ public class VirtualHostGraylogLoggerImpl extends AbstractVirtualHostLogger<Virt
     private AsyncAppender _appender;
 
     @ManagedObjectFactoryConstructor(conditionallyAvailable = true,
-            condition = "org.apache.qpid.server.logging.logback.GelfCheck#isAvailable()")
+            condition = "org.apache.qpid.server.logging.logback.graylog.GelfCheck#isAvailable()")
     public VirtualHostGraylogLoggerImpl(Map<String, Object> attributes, VirtualHost<?> virtualHost)
     {
         super(attributes, virtualHost);
diff --git a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java
index 2eb0d37..0556df6 100644
--- a/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java
+++ b/broker-plugins/graylog-logging-logback/src/main/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidator.java
@@ -21,7 +21,7 @@
 
 package org.apache.qpid.server.logging.logback.validator;
 
-import org.apache.qpid.server.logging.logback.GelfAppenderConfiguration;
+import org.apache.qpid.server.logging.logback.graylog.GelfAppenderConfiguration;
 import org.apache.qpid.server.model.ConfiguredObject;
 
 import java.util.Arrays;
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/GraylogAppenderTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppenderTest.java
similarity index 99%
rename from broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/GraylogAppenderTest.java
rename to broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppenderTest.java
index 97b6e46..b3e1bd7 100644
--- a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/GraylogAppenderTest.java
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/graylog/GraylogAppenderTest.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.qpid.server.logging.logback;
+package org.apache.qpid.server.logging.logback.graylog;
 
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.spi.ILoggingEvent;
diff --git a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java
index 4d62581..be1b37c 100644
--- a/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java
+++ b/broker-plugins/graylog-logging-logback/src/test/java/org/apache/qpid/server/logging/logback/validator/GelfConfigurationValidatorTest.java
@@ -1,7 +1,7 @@
 package org.apache.qpid.server.logging.logback.validator;
 
 import org.apache.qpid.server.configuration.IllegalConfigurationException;
-import org.apache.qpid.server.logging.logback.GelfAppenderConfiguration;
+import org.apache.qpid.server.logging.logback.graylog.GelfAppenderConfiguration;
 import org.apache.qpid.test.utils.UnitTestBase;
 import org.junit.Test;
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org