如何在 ActiveMQ 中使用 log4j2 JMSAppender
How to use log4j2 JMSAppender with ActiveMQ
我正在努力编写一个将消息记录到队列的简单 POC 程序。我找到的所有教程和问答 (here and here) 都使用 log4j 1.2 版,它们将消息放入主题而不是队列。我的要求是登录队列。
我遵循了 official site 中提到的文档,但无法正常工作。
我的类路径上有 log4j2 和 ActiveMQ JAR,我创建了队列 "logQueue",我能够在 ActiveMQ Web 控制台中看到队列,当我尝试执行程序来写入日志时,我收到以下错误:
ERROR Error creating JmsManager using ConnectionFactory [ConnectionFactory] and Destination [logQueue]. javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
它显然看起来像一些 JNDI 问题,但我无法弄清楚是什么。根据 log4j 1.2 教程,我还将 jndi.properties 文件添加到我的类路径中,其值为
queue.logQueue=logQueue
但这显然没有帮助。下面是我的 log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<JMS name="jmsQueue" destinationBindingName="logQueue"
factoryBindingName="ConnectionFactory"
providerURL="tcp://localhost:61616"/>
</Appenders>
<Loggers>
<Root level="all">
<AppenderRef ref="jmsQueue"/>
</Root>
</Loggers>
</Configuration>
谢谢!
将 factoryName="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
添加到 JMS 元素解决了问题。
log4j2.xml 中的最终 JMS 元素如下所示:
<JMS name="jmsQueue" destinationBindingName="logQueue"
factoryName="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
factoryBindingName="ConnectionFactory"
providerURL="tcp://localhost:61616"/>
我在 appender 的初始化过程中遇到错误,所以我尝试自己编写而不是使用现有的 xml 配置。
依赖版本:
- ActiveMQ 5.12.1 客户端
- log4j-core-2.5
- slf4j-1.7.12
Java class:
复制 class 并将其放在项目中的任何位置,
package com.towersoft.eagleserver;
import java.io.Serializable;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
@Plugin(name = "JMSQueueAppender", category = "Core", elementType = "appender", printObject = true)
public class JMSQueueAppender extends AbstractAppender {
// private static Logger logger = Logger.getLogger("JMSQueueAppender");
private String brokerUri = "failover://tcp://localhost:61616";
private String queueName = "logQueue";
Session session;
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(this.brokerUri);
javax.jms.Connection connection;
Destination destination;
MessageProducer producer;
private void init() {
try {
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue(this.queueName);
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
} catch (Exception e) {
e.printStackTrace();
}
}
protected JMSQueueAppender(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
init();
}
@Override
public void append(LogEvent le) {
try {
if (connection == null) {
init();
}
TextMessage message = session.createTextMessage(le.getMessage().getFormattedMessage());
// Tell the producer to send the message
producer.send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void stop() {
super.stop();
try {
session.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@PluginFactory
public static JMSQueueAppender createAppender(
@PluginAttribute("name") String name,
@PluginElement("Layout") Layout<? extends Serializable> layout,
@PluginElement("Filter") final Filter filter) {
if (name == null) {
LOGGER.error("No name provided for MyCustomAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new JMSQueueAppender(name, filter, layout, true);
}
}
log4j2.xml :
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<JMSQueueAppender name="jmsQueue"/>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console"/>
<AppenderRef ref="jmsQueue"/>
</Root>
<Logger name="org.quartz" level="ERROR"/>
<Logger name="com.zaxxer" level="ERROR"/>
<Logger name="org.apache.activemq" level="ERROR"/>
</Loggers>
</Configuration>
我正好遇到了这个问题。通过将文件添加到我的名为 jndi.properties 的 Maven 资源目录来解决,内容为 ...
topic.logTopic=logTopic
queue.logQueue=logQueue
显然可以修改实际使用的名称。在我的例子中,我的 appender 可能是......
<JMS name="jmsQueue"
destinationBindingName="logQueue"
factoryName="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
factoryBindingName="ConnectionFactory"
providerURL="tcp://localhost:61616"
userName="admin"
password="admin">
<SyslogLayout />
</JMS>
注意:我必须修改 appender 的布局以避免序列化错误(其他可能的值是...JSONLayout、YamlLayout、HtmlLayout 等)。
希望对您有所帮助。
我正在努力编写一个将消息记录到队列的简单 POC 程序。我找到的所有教程和问答 (here and here) 都使用 log4j 1.2 版,它们将消息放入主题而不是队列。我的要求是登录队列。
我遵循了 official site 中提到的文档,但无法正常工作。
我的类路径上有 log4j2 和 ActiveMQ JAR,我创建了队列 "logQueue",我能够在 ActiveMQ Web 控制台中看到队列,当我尝试执行程序来写入日志时,我收到以下错误:
ERROR Error creating JmsManager using ConnectionFactory [ConnectionFactory] and Destination [logQueue]. javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
它显然看起来像一些 JNDI 问题,但我无法弄清楚是什么。根据 log4j 1.2 教程,我还将 jndi.properties 文件添加到我的类路径中,其值为
queue.logQueue=logQueue
但这显然没有帮助。下面是我的 log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<JMS name="jmsQueue" destinationBindingName="logQueue"
factoryBindingName="ConnectionFactory"
providerURL="tcp://localhost:61616"/>
</Appenders>
<Loggers>
<Root level="all">
<AppenderRef ref="jmsQueue"/>
</Root>
</Loggers>
</Configuration>
谢谢!
将 factoryName="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
添加到 JMS 元素解决了问题。
log4j2.xml 中的最终 JMS 元素如下所示:
<JMS name="jmsQueue" destinationBindingName="logQueue"
factoryName="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
factoryBindingName="ConnectionFactory"
providerURL="tcp://localhost:61616"/>
我在 appender 的初始化过程中遇到错误,所以我尝试自己编写而不是使用现有的 xml 配置。
依赖版本:
- ActiveMQ 5.12.1 客户端
- log4j-core-2.5
- slf4j-1.7.12
Java class:
复制 class 并将其放在项目中的任何位置,
package com.towersoft.eagleserver;
import java.io.Serializable;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
@Plugin(name = "JMSQueueAppender", category = "Core", elementType = "appender", printObject = true)
public class JMSQueueAppender extends AbstractAppender {
// private static Logger logger = Logger.getLogger("JMSQueueAppender");
private String brokerUri = "failover://tcp://localhost:61616";
private String queueName = "logQueue";
Session session;
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(this.brokerUri);
javax.jms.Connection connection;
Destination destination;
MessageProducer producer;
private void init() {
try {
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue(this.queueName);
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
} catch (Exception e) {
e.printStackTrace();
}
}
protected JMSQueueAppender(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
init();
}
@Override
public void append(LogEvent le) {
try {
if (connection == null) {
init();
}
TextMessage message = session.createTextMessage(le.getMessage().getFormattedMessage());
// Tell the producer to send the message
producer.send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void stop() {
super.stop();
try {
session.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@PluginFactory
public static JMSQueueAppender createAppender(
@PluginAttribute("name") String name,
@PluginElement("Layout") Layout<? extends Serializable> layout,
@PluginElement("Filter") final Filter filter) {
if (name == null) {
LOGGER.error("No name provided for MyCustomAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new JMSQueueAppender(name, filter, layout, true);
}
}
log4j2.xml :
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<JMSQueueAppender name="jmsQueue"/>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console"/>
<AppenderRef ref="jmsQueue"/>
</Root>
<Logger name="org.quartz" level="ERROR"/>
<Logger name="com.zaxxer" level="ERROR"/>
<Logger name="org.apache.activemq" level="ERROR"/>
</Loggers>
</Configuration>
我正好遇到了这个问题。通过将文件添加到我的名为 jndi.properties 的 Maven 资源目录来解决,内容为 ...
topic.logTopic=logTopic
queue.logQueue=logQueue
显然可以修改实际使用的名称。在我的例子中,我的 appender 可能是......
<JMS name="jmsQueue"
destinationBindingName="logQueue"
factoryName="org.apache.activemq.jndi.ActiveMQInitialContextFactory"
factoryBindingName="ConnectionFactory"
providerURL="tcp://localhost:61616"
userName="admin"
password="admin">
<SyslogLayout />
</JMS>
注意:我必须修改 appender 的布局以避免序列化错误(其他可能的值是...JSONLayout、YamlLayout、HtmlLayout 等)。
希望对您有所帮助。