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:38 UTC
[qpid-broker-j] 01/05: QPID-8368: [Broker-J] Graylog support
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