Repository: logging-log4j2 Updated Branches: refs/heads/master dc26e2387 -> e89ac6e5b
[LOG4J2-1294] The JMS Appender should use a JMS MapMessage for a Log4j MapMessage. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e89ac6e5 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e89ac6e5 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e89ac6e5 Branch: refs/heads/master Commit: e89ac6e5b16d15497c12758665dbbfb1534eb13d Parents: dc26e23 Author: Gary Gregory <[email protected]> Authored: Wed May 31 20:04:48 2017 -0700 Committer: Gary Gregory <[email protected]> Committed: Wed May 31 20:04:48 2017 -0700 ---------------------------------------------------------------------- .../appender/mom/activemq/JmsAppenderIT.java | 222 +++++++++++-------- .../log4j/core/appender/mom/JmsManager.java | 37 +++- .../log4j/core/layout/MessageLayout.java | 66 ++++++ src/changes/changes.xml | 3 + 4 files changed, 229 insertions(+), 99 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e89ac6e5/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java ---------------------------------------------------------------------- diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java index 4e03e46..b0bf594 100644 --- a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java +++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java @@ -17,11 +17,16 @@ package org.apache.logging.log4j.core.appender.mom.activemq; +import static org.junit.Assert.assertEquals; + import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; + import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; @@ -31,114 +36,145 @@ import javax.jms.ObjectMessage; import org.apache.activemq.jndi.ActiveMQInitialContextFactory; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.categories.Appenders; +import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.mom.JmsAppender; import org.apache.logging.log4j.core.appender.mom.JmsManager; import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.core.layout.MessageLayout; import org.apache.logging.log4j.core.layout.SerializedLayout; import org.apache.logging.log4j.core.net.JndiManager; +import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.message.SimpleMessage; -import org.junit.AfterClass; +import org.junit.After; +import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; -import static org.junit.Assert.*; - /** * Integration test for JmsAppender using an embedded ActiveMQ broker. */ @Category(Appenders.Jms.class) public class JmsAppenderIT { - private static final String KEY_SERIALIZABLE_PACKAGES = "org.apache.activemq.SERIALIZABLE_PACKAGES"; - - private static JmsManager jmsManager; - - private JmsAppender appender; - - @BeforeClass - public static void setUpClass() { - System.setProperty(KEY_SERIALIZABLE_PACKAGES, - "org.apache.logging.log4j.core.impl,org.apache.logging.log4j.util,org.apache.logging.log4j,java.rmi"); - final Properties additional = new Properties(); - additional.setProperty("queue.TestQueue", "TestQueue"); - final JndiManager jndiManager = JndiManager.getJndiManager(ActiveMQInitialContextFactory.class.getName(), - "vm://localhost?broker.persistent=false", null, null, null, additional); - jmsManager = JmsManager.getJmsManager("JmsManager", jndiManager, "ConnectionFactory", "TestQueue", null, null); - } - - @AfterClass - public static void tearDownClass() { - jmsManager.close(); - System.getProperties().remove(KEY_SERIALIZABLE_PACKAGES); - } - - @Before - public void setUp() throws Exception { - // @formatter:off - appender = JmsAppender.newBuilder(). - setName("JmsAppender"). - setLayout(SerializedLayout.createLayout()). - setIgnoreExceptions(true). - setJmsManager(jmsManager). - build(); - // @formatter:off - appender.start(); - } - - @Test - public void testLogToQueue() throws Exception { - final int messageCount = 100; - final MessageConsumer messageConsumer = jmsManager.createMessageConsumer(); - final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount); - messageConsumer.setMessageListener(consumer); - final String messageText = "Hello, World!"; - final String loggerName = this.getClass().getName(); - for (int i = 0; i < messageCount; i++) { - final LogEvent event = Log4jLogEvent.newBuilder().setLoggerName(loggerName) // - .setLoggerFqcn(loggerName).setLevel(Level.INFO) // - .setMessage(new SimpleMessage(messageText)).setThreadName(Thread.currentThread().getName()) // - .setTimeMillis(System.currentTimeMillis()).build(); - appender.append(event); - } - consumer.awaitAndAssertAllMessagesConsumed(); - } - - private static class JmsQueueConsumer implements MessageListener { - - private final int messageCount; - private final CountDownLatch countDownLatch; - private final Collection<LogEvent> events; - - private JmsQueueConsumer(final int messageCount) { - this.messageCount = messageCount; - this.countDownLatch = new CountDownLatch(messageCount); - this.events = new ArrayList<>(messageCount); - } - - @Override - public void onMessage(final Message message) { - try { - consume((ObjectMessage) message); - } catch (final JMSException e) { - e.printStackTrace(); - } - } - - private void consume(final ObjectMessage message) throws JMSException { - try { - final LogEvent event = (LogEvent) message.getObject(); - events.add(event); - } finally { - countDownLatch.countDown(); - } - } - - public void awaitAndAssertAllMessagesConsumed() throws InterruptedException { - countDownLatch.await(5, TimeUnit.SECONDS); - assertEquals(messageCount, events.size()); - } - } + private static final String KEY_SERIALIZABLE_PACKAGES = "org.apache.activemq.SERIALIZABLE_PACKAGES"; + + private JmsManager jmsManager; + + private JmsAppender appender; + + @Before + public void setUpClass() { + System.setProperty(KEY_SERIALIZABLE_PACKAGES, + "org.apache.logging.log4j.core.impl,org.apache.logging.log4j.util,org.apache.logging.log4j,java.rmi"); + final Properties additional = new Properties(); + additional.setProperty("queue.TestQueue", "TestQueue"); + final JndiManager jndiManager = JndiManager.getJndiManager(ActiveMQInitialContextFactory.class.getName(), + "vm://localhost?broker.persistent=false", null, null, null, additional); + jmsManager = JmsManager.getJmsManager("JmsManager", jndiManager, "ConnectionFactory", "TestQueue", null, null); + } + + @After + public void tearDownClass() { + jmsManager.close(); + System.getProperties().remove(KEY_SERIALIZABLE_PACKAGES); + } + + private void setUp(Layout<?> layout) throws Exception { + // @formatter:off + appender = JmsAppender.newBuilder() + .setName("JmsAppender") + .setLayout(layout) + .setIgnoreExceptions(true) + .setJmsManager(jmsManager) + .build(); + // @formatter:off + appender.start(); + } + + @Test + public void testLogMapMessageToQueue() throws Exception { + setUp(MessageLayout.createLayout()); + final int messageCount = 100; + final MessageConsumer messageConsumer = jmsManager.createMessageConsumer(); + final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount, javax.jms.MapMessage.class); + messageConsumer.setMessageListener(consumer); + final String messageText = "Hello, World!"; + final String loggerName = this.getClass().getName(); + for (int i = 0; i < messageCount; i++) { + Map<String, String> map = new HashMap<>(); + map.put("messageText", messageText); + map.put("threadName", Thread.currentThread().getName()); + final LogEvent event = Log4jLogEvent.newBuilder().setLoggerName(loggerName) // + .setLoggerFqcn(loggerName).setLevel(Level.INFO) // + .setMessage(new MapMessage(map)) // + .setTimeMillis(System.currentTimeMillis()).build(); + appender.append(event); + } + consumer.awaitAndAssertAllMessagesConsumed(); + } + + @Test + public void testLogObjectMessageToQueue() throws Exception { + setUp(SerializedLayout.createLayout()); + final int messageCount = 100; + final MessageConsumer messageConsumer = jmsManager.createMessageConsumer(); + final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount, ObjectMessage.class); + messageConsumer.setMessageListener(consumer); + final String messageText = "Hello, World!"; + final String loggerName = this.getClass().getName(); + for (int i = 0; i < messageCount; i++) { + final LogEvent event = Log4jLogEvent.newBuilder().setLoggerName(loggerName) // + .setLoggerFqcn(loggerName).setLevel(Level.INFO) // + .setMessage(new SimpleMessage(messageText)).setThreadName(Thread.currentThread().getName()) // + .setTimeMillis(System.currentTimeMillis()).build(); + appender.append(event); + } + consumer.awaitAndAssertAllMessagesConsumed(); + } + + private static class JmsQueueConsumer implements MessageListener { + + private final int messageCount; + private final Class<? extends Message> messageClass; + private final CountDownLatch countDownLatch; + private final Collection<Object> events; + + private JmsQueueConsumer(final int messageCount, Class<? extends Message> messageClass) { + this.messageCount = messageCount; + this.messageClass = messageClass; + this.countDownLatch = new CountDownLatch(messageCount); + this.events = new ArrayList<>(messageCount); + } + + @Override + public void onMessage(final Message message) { + try { + try { + final Object event; + Assert.assertTrue(String.format("Expected type '%s' to be an instance of %s", message.getClass(), messageClass), + messageClass.isAssignableFrom(message.getClass())); + if (message instanceof ObjectMessage) { + event = ((ObjectMessage) message).getObject(); + } else if (message instanceof javax.jms.MapMessage) { + event = message; + } else { + Assert.fail("Unexpected Message type: " + message); + event = null; + } + events.add(event); + } finally { + countDownLatch.countDown(); + } + } catch (final JMSException e) { + e.printStackTrace(); + } + } + + public void awaitAndAssertAllMessagesConsumed() throws InterruptedException { + countDownLatch.await(5, TimeUnit.SECONDS); + assertEquals(messageCount, events.size()); + } + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e89ac6e5/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java index 4ff0f05..db47743 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java @@ -18,12 +18,14 @@ package org.apache.logging.log4j.core.appender.mom; import java.io.Serializable; +import java.util.Map; import java.util.concurrent.TimeUnit; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; +import javax.jms.MapMessage; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; @@ -107,13 +109,23 @@ public class JmsManager extends AbstractManager { } /** - * Creates a TextMessage or ObjectMessage from a Serializable object. For instance, when using a text-based - * {@link org.apache.logging.log4j.core.Layout} such as {@link org.apache.logging.log4j.core.layout.PatternLayout}, - * the {@link org.apache.logging.log4j.core.LogEvent} message will be serialized to a String. When using a - * layout such as {@link org.apache.logging.log4j.core.layout.SerializedLayout}, the LogEvent message will be - * serialized as a Java object. + * Creates a TextMessage, MapMessage, or ObjectMessage from a Serializable object. + * <p> + * For instance, when using a text-based {@link org.apache.logging.log4j.core.Layout} such as + * {@link org.apache.logging.log4j.core.layout.PatternLayout}, the {@link org.apache.logging.log4j.core.LogEvent} + * message will be serialized to a String. + * </p> + * <p> + * When using a layout such as {@link org.apache.logging.log4j.core.layout.SerializedLayout}, the LogEvent message + * will be serialized as a Java object. + * </p> + * <p> + * When using a layout such as {@link org.apache.logging.log4j.core.layout.MessageLayout} and the LogEvent message + * is a Log4j MapMessage, the message will be serialized as a JMS MapMessage. + * </p> * - * @param object The LogEvent or String message to wrap. + * @param object + * The LogEvent or String message to wrap. * @return A new JMS message containing the provided object. * @throws JMSException */ @@ -121,9 +133,22 @@ public class JmsManager extends AbstractManager { if (object instanceof String) { return this.session.createTextMessage((String) object); } + else if (object instanceof org.apache.logging.log4j.message.MapMessage) { + return map((org.apache.logging.log4j.message.MapMessage) object, this.session.createMapMessage()); + } return this.session.createObjectMessage(object); } + private MapMessage map(org.apache.logging.log4j.message.MapMessage log4jMapMessage, MapMessage jmsMapMessage) + throws JMSException { + // Call getData() only once. + final Map<String, String> data = log4jMapMessage.getData(); + for (Map.Entry<String, String> entry : data.entrySet()) { + jmsMapMessage.setString(entry.getKey(), entry.getValue()); + } + return jmsMapMessage; + } + @Override protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) { boolean closed = true; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e89ac6e5/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MessageLayout.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MessageLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MessageLayout.java new file mode 100644 index 0000000..9adb8ee --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MessageLayout.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.logging.log4j.core.layout; + +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Node; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.message.Message; + +/** + * Formats a {@link LogEvent} in its {@link Message} form. + * <p> + * Useful in combination with a JMS Appender to map a Log4j {@link org.apache.logging.log4j.message.MapMessage} to a JMS + * {@link javax.jms.MapMessage}. + * </p> + */ +@Plugin(name = "MessageLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true) +public class MessageLayout extends AbstractLayout<Message> { + + public MessageLayout(Configuration configuration, byte[] header, byte[] footer) { + super(configuration, header, footer); + } + + public MessageLayout() { + super(null, null, null); + } + + @Override + public byte[] toByteArray(LogEvent event) { + return null; + } + + @Override + public Message toSerializable(LogEvent event) { + return event.getMessage(); + } + + @Override + public String getContentType() { + return null; + } + + @PluginFactory + public static Layout<?> createLayout() { + return new MessageLayout(); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e89ac6e5/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 3863f37..4936876 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -73,6 +73,9 @@ <action issue="LOG4J2-1860" dev="mikes" type="add"> Shortcut to add Property and KeyValuePair component in ConfigurationBuilder. </action> + <action issue="LOG4J2-1294" dev="ggregory" type="add"> + The JMS Appender should use a JMS MapMessage for a Log4j MapMessage. + </action> <action issue="LOG4J2-1868" dev="ggregory" type="update"> Update ZeroMQ's JeroMQ from 0.3.6 to 0.4.0. </action>
