Spring+Log4j+ActiveMQ实现远程记录日志(4)

logMessageListener指向我们自己实现的日志消息处理逻辑类,topicDestination则关注topic为“logTopic”的消息,而jmsContainer把这两个对象绑在一起,这样就能接收并处理消息了。

最后就是伟大的监听器了LogMessageListener了:

package com.demo.logging;
 
import javax.jms.Message;
import javax.jms.MessageListener;
import org.apache.activemq.command.ActiveMQObjectMessage;
import org.apache.log4j.spi.LoggingEvent;
 
public class LogMessageListener implements MessageListener {
    public void onMessage(Message message) {
        try {
            // receive log event in your consumer
            LoggingEvent event = (LoggingEvent)((ActiveMQObjectMessage)message).getObject();
            System.out.println("Logging project: [" + event.getLevel() + "]: "+ event.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

哈哈,说伟大,其实太简单了。但是可以看到,监听器里面就是之前Product项目中Main类里面移除的实现了MessageListener接口中的代码。
 
测试

在执行测试前,删掉ActiveMQ中所有的Queue,确保测试效果。

先运行Logging项目,开始Queue的监听。再运行Product的Main类的main函数,可以先看到Main类打印到控制台的日志:

接下来去看看Queue中的情况:

Spring+Log4j+ActiveMQ实现远程记录日志

可以看到有个叫logTopic的主题的消息,进了3条,出了3条。不用想,出Queue的3条日志已经被Logging项目的Listener接收并打印出来了,现在去看看Tomcat的控制台:

Spring+Log4j+ActiveMQ实现远程记录日志

还要注意Queue中的logTopic的Consumer数量为1而不是0,这与开始的截图不同。我们都知道这个Consumer是Logging项目中的LogMessageListener对象,它一直活着,是因为Tomcat一直活着;之前的Consumer数量为0,是因为在main函数执行完后,Queue的监听器(也是写日志的对象)就退出了。

通过把Product和Logging项目分别放在不同的机器上执行,在第三台机器上部署ActiveMQ(当然你可以把ActiveMQ搭建在任意可以访问的地方),再配置一下Product项目的log4j.properties文件和Logging项目的spring.xml文件就能用于生产环境啦。
 
JMSAppender类的分析

JMSAppender类将LoggingEvent实例序列化成ObjectMessage,并将其发送到JMS Server的一个指定Topic中,因此,使用此种将日志发送到远程的方式只支持Topic方式发送,不支持Queue方式发送。我们再log4j.properties中配置了这一句:

log4j.appender.jms=org.apache.log4j.net.JMSAppender

这一句指定了使用的Appender,打开这个Appender,在里面可以看到很多setter,比如:

Spring+Log4j+ActiveMQ实现远程记录日志

这些setter不是巧合,而正是对应了我们在log4j.properties中设置的其他几个选项:

log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory
log4j.appender.jms.ProviderURL=tcp://localhost:61616
log4j.appender.jms.TopicBindingName=logTopic
log4j.appender.jms.TopicConnectionFactoryBindingName=ConnectionFactory

来看看JMSAppender的activeOptions方法,这个方法是用于使我们在log4j.properties中的配置生效的:

/**
 * Options are activated and become effective only after calling this method.
 */
public void activateOptions() {
    TopicConnectionFactory topicConnectionFactory;
    try {
        Context jndi;
        LogLog.debug("Getting initial context.");
        if (initialContextFactoryName != null) {
            Properties env = new Properties();
            env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
            if (providerURL != null) {
                env.put(Context.PROVIDER_URL, providerURL);
            } else {
                LogLog.warn("You have set InitialContextFactoryName option but not the "
                        + "ProviderURL. This is likely to cause problems.");
            }
            if (urlPkgPrefixes != null) {
                env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
            }
 
            if (securityPrincipalName != null) {
                env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
                if (securityCredentials != null) {
                    env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
                } else {
                    LogLog.warn("You have set SecurityPrincipalName option but not the "
                            + "SecurityCredentials. This is likely to cause problems.");
                }
            }
            jndi = new InitialContext(env);
        } else {
            jndi = new InitialContext();
        }
 
        LogLog.debug("Looking up [" + tcfBindingName + "]");
        topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName);
        LogLog.debug("About to create TopicConnection.");
       
        ///////////////////////////////注意这里只会创建TopicConnection////////////////////////////
        if (userName != null) {
            topicConnection = topicConnectionFactory.createTopicConnection(userName, password);
        } else {
            topicConnection = topicConnectionFactory.createTopicConnection();
        }
 
        LogLog.debug("Creating TopicSession, non-transactional, " + "in AUTO_ACKNOWLEDGE mode.");
        topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
 
        LogLog.debug("Looking up topic name [" + topicBindingName + "].");
        Topic topic = (Topic) lookup(jndi, topicBindingName);
 
        LogLog.debug("Creating TopicPublisher.");
        topicPublisher = topicSession.createPublisher(topic);
 
        LogLog.debug("Starting TopicConnection.");
        topicConnection.start();
 
        jndi.close();
    } catch (JMSException e) {
        errorHandler.error("Error while activating options for appender named [" + name + "].", e,
                ErrorCode.GENERIC_FAILURE);
    } catch (NamingException e) {
        errorHandler.error("Error while activating options for appender named [" + name + "].", e,
                ErrorCode.GENERIC_FAILURE);
    } catch (RuntimeException e) {
        errorHandler.error("Error while activating options for appender named [" + name + "].", e,
                ErrorCode.GENERIC_FAILURE);
    }
}
 

上面初始化了一个TopicConnection,一个TopicSession,一个TopicPublisher。咱们再来看看这个Appender的append方法:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/e722fe48bca1937371ef202ec93d3572.html