基于ActiveMQ的统一日志服务

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于ActiveMQ的统一日志服务相关的知识,希望对你有一定的参考价值。

概述


以ActiveMQ + Log4j + Spring的技术组合,实现基于消息队列的统一日志服务。

与参考文章的比较

1. 更新了技术的版本
    e.g. Spring升级到4.2.0,ActiveMQ升级到5.13.2

2. 更新了依赖
    e.g. 使用activemq-client 5.13.2替换activemq-core最高版本是5.7.0,并取消了多余的spring-jms依赖

3. 精简了配置
    e.g. 去掉spring.xml中的jmsTemplate

4. 其他略述

前提

为理解文章的内容,你可能需要先了解下面的知识:

1. 了解基于Maven的项目结构

2. 下载并运行ActiveMQ

3. 了解log4j基于properties配置的简单用法

4. 了解基于Spring-webmvc的框架的搭建

当然,这只是建议......

技术版本

  1. ActiveMQ - 5.13.2
  2. Log4j - 1.2.17
  3. Spring - 4.2.4

结构图


1.节点拓扑图

技术分享
说明:
  1. 应用系统基于log4j规范,通过JMSAppender将日志发送到ActiveMQ
  2. Log Server向ActiveMQ订阅消息,并指定MessageListener的实现来接收ActiveMQ发布的消息

实现


1.Log Server

你可以从amqlog-server拿到源代码。

1.1.文件目录结构

 1 pom.xml
 2 src/main/webapp/
 3     |---- WEB-INF/
 4               |---- web.xml
 5     |---- index.jsp # 忽略
 6 src/main/resources/
 7     |---- spring-beans.xml
 8     |---- topic.properties # 集中管理修改概率比较高的属性配置
 9 src/main/java/
10     |---- cn.sinobest.asj.logserver
11               |---- LogListener.java # 接收并输出log message

1.2.文件内容

1.2.1. pom.xml

 1 <project xmlns="http://maven.apache.org/POM/4.0.0"
 2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <groupId>cn.sinobest.asj</groupId>
 6     <artifactId>log-servler</artifactId>
 7     <packaging>war</packaging>
 8     <version>0.0.1-SNAPSHOT</version>
 9     <name>amqlog-servler Maven Webapp</name>
10     <url>http://maven.apache.org</url>
11     <description>日志服务器,从ActiveMQ订阅主题,从而获取相关的日志数据</description>
12     <dependencies>
13         <dependency>
14             <groupId>junit</groupId>
15             <artifactId>junit</artifactId>
16             <version>3.8.1</version>
17             <scope>test</scope>
18         </dependency>
19         <!-- use to import spring-webmvc framework -->
20         <dependency>
21             <groupId>org.springframework</groupId>
22             <artifactId>spring-web</artifactId>
23             <version>4.2.4.RELEASE</version>
24         </dependency>
25         <dependency>
26             <groupId>org.springframework</groupId>
27             <artifactId>spring-jms</artifactId>
28             <version>4.2.4.RELEASE</version>
29         </dependency>
30         <!-- use to subscribe topic message from ActiveMQ -->
31         <dependency>
32             <groupId>org.apache.activemq</groupId>
33             <artifactId>activemq-client</artifactId>
34             <version>5.13.2</version>
35         </dependency>
36         <!-- use to extract log content from message -->
37         <dependency>
38             <groupId>log4j</groupId>
39             <artifactId>log4j</artifactId>
40             <version>1.2.17</version>
41         </dependency>
42     </dependencies>
43     <build>
44         <finalName>amqlog-servler</finalName>
45     </build>
46 </project>

1.2.2. web.xml

 1 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 2     xmlns="http://java.sun.com/xml/ns/javaee"
 3     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 4     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 5     id="WebApp_ID" version="3.0" metadata-complete="false">
 6     <display-name>Archetype Created Web Application</display-name>
 7     <context-param>
 8         <param-name>contextConfigLocation</param-name>
 9         <param-value>
10                 classpath:spring-beans.xml
11             </param-value>
12     </context-param>
13     <listener>
14         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
15     </listener>
16 </web-app>

1.2.3. spring-beans.xml

装配图
技术分享
说明:左上角标识由谁提供具体的实现,没有标识的有自己提供实现。
内容
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans
 6         http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
 7         http://www.springframework.org/schema/context
 8         http://www.springframework.org/schema/context/spring-context-4.2.xsd">
 9     <context:property-placeholder location="classpath:topic.properties" />
10     <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
11         <property name="brokerURL" value="${topic.brokerURL}" />
12         <!-- add trusted packages. see http://activemq.apache.org/objectmessage.html -->
13         <property name="trustedPackages">
14             <list>
15                 <value>org.apache.log4j.spi</value>
16             </list>
17         </property>
18     </bean>
19     <bean id="connectionFactory"
20         class="org.springframework.jms.connection.SingleConnectionFactory">
21         <property name="targetConnectionFactory" ref="targetConnectionFactory" />
22     </bean>
23     <bean id="destination" class="org.apache.activemq.command.ActiveMQTopic">
24         <constructor-arg name="name" value="${topic.topicName}" />
25     </bean>
26     <!-- define the message-listener to receive and dipose log data. -->
27     <bean id="messageListener" class="cn.sinobest.asj.logserver.LogListener" />
28     <bean id="jmsContainer"
29         class="org.springframework.jms.listener.DefaultMessageListenerContainer">
30         <property name="connectionFactory" ref="connectionFactory" />
31         <property name="destination" ref="destination" />
32         <property name="messageListener" ref="messageListener" />
33     </bean>
34 </beans>
说明:
在targetConnectionFactory的属性中,指定了trustedPackages。ActiveMQ自5.12.2版本之后,强制用户指定一份可信任的packages白名单,以对付ObjectMessage存在的安全漏洞。具体内容可参考:http://activemq.apache.org/objectmessage.html

1.2.4. topic.properties

topic.brokerURL=tcp://localhost:61616
topic.topicName=demo
注意:brokerURL的值必须和ActiveMQ的监听地址一致。

1.2.5. LogListener.java

 1 package cn.sinobest.asj.logserver;
 2 import javax.jms.JMSException;
 3 import javax.jms.Message;
 4 import javax.jms.MessageListener;
 5 import org.apache.activemq.command.ActiveMQObjectMessage;
 6 import org.apache.log4j.spi.LoggingEvent;
 7 public class LogListener implements MessageListener {
 8     private static final String TEMPLATE = "[%-5s] %s";
 9     public void onMessage(Message message) {
10         try {
11             // extract LoggingEvent from message
12             // you must set org.apache.log4j.spi into the trusted packages list
13             // see spring-beans.xml in classpath
14             LoggingEvent event = (LoggingEvent) ((ActiveMQObjectMessage) message)
15                     .getObject();
16             String content = String.format(TEMPLATE, event.getLevel()
17                     .toString(), event.getMessage().toString());
18             System.out.println(content);
19         } catch (JMSException e) {
20             e.printStackTrace();
21         }
22     }
23 }
说明:这里的LoggingEvent来自package org.apache.log4j.spi,该package在spring-beans.xml的白名单中。

2.Log Client

log client模拟一般的应用系统。该应用系统有日志存储的需要,将日志发送给ActiveMQ而不用关心日志最终的存储方式。这里仅用一个简单的JavaSE project来模拟,但是已经足够提供完整的核心代码。
你可以从amqlog-client拿到源代码。

2.1.文件目录结构

1 pom.xml
2 src/main/resources/
3     |---- log4j.properties # 配置日志输出地点,及ActiveMQ的相关参数
4     |---- jndi.properties # 配置topic
5 src/main/java/
6     |---- cn.sinobest.asj.logclient
7               |---- LogProducer.java # 生成并输出日志

2.2.文件内容

2.2.1. pom.xml

 1 <project xmlns="http://maven.apache.org/POM/4.0.0"
 2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <groupId>cn.sinobest.asj</groupId>
 6     <artifactId>amqlog-client</artifactId>
 7     <version>0.0.1-SNAPSHOT</version>
 8     <name>Simple app to send log to ActiveMQ</name>
 9     <description>模拟一般的应用系统,通过log4j发送日志到ActiveMQ</description>
10     <dependencies>
11         <!-- use to write log -->
12         <dependency>
13             <groupId>log4j</groupId>
14             <artifactId>log4j</artifactId>
15             <version>1.2.17</version>
16         </dependency>
17         <dependency>
18             <groupId>commons-logging</groupId>
19             <artifactId>commons-logging</artifactId>
20             <version>1.1.1</version>
21         </dependency>
22         <!-- use to import class org.apache.activemq.jndi.ActiveMQInitialContextFactory 
23             to write log to ActiveMQ -->
24         <dependency>
25             <groupId>org.apache.activemq</groupId>
26             <artifactId>activemq-client</artifactId>
27             <version>5.13.2</version>
28         </dependency>
29     </dependencies>
30 </project>

2.2.2. log4j.properties

 1 # define the stand out appender
 2 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
 3 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 4 log4j.appender.stdout.layout.ConversionPattern=[%-5p] %-d{yyyy-MM-dd HH:mm:ss z}%n        %m%n%n
 5 
 6 # define the jms appender
 7 log4j.appender.jms=org.apache.log4j.net.JMSAppender
 8 log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory
 9 log4j.appender.jms.ProviderURL=tcp://localhost:61616
10 # TopicBindingName可以自由配置,只需要确保提供对应的jndi属性即可
11 log4j.appender.jms.TopicBindingName=topicName
12 # TopicConnectionFactoryBindingName目前不能自由配置
13 log4j.appender.jms.TopicConnectionFactoryBindingName=ConnectionFactory
14 
15 # define the logger
16 log4j.rootLogger=INFO, stdout, jms

注意:log4j.appender.jms.ProviderURL的值必须和ActiveMQ的监听地址一致。

2.2.3. jndi.properties

topic.topicName=demo
注意:key的后半部分(topicName)必须与log4j.properties中的log4j.appender.jms.TopicBindingName一致。
属性间的对应关系
技术分享

2.2.4. LogProducer.java

 1 package cn.sinobest.asj.logclient;
 2 import org.apache.commons.logging.Log;
 3 import org.apache.commons.logging.LogFactory;
 4 public class LogProducer {
 5     private static final Log log = LogFactory.getLog(LogProducer.class);
 6     /**
 7      * @param args
 8      */
 9     public static void main(String[] args) {
10         log.debug("this is a debug message.");
11         log.info("this is a info message.");
12         log.warn("this is a warn message.");
13         log.error("this is a error message");
14         System.exit(0);
15     }
16 }
说明:debug的内容不会发送到ActiveMQ。

测试


  1. 启动ActiveMQ
    cd到ActiveMQ的解压缩目录,在cmd执行bin\activemq start
  2. 部署Log Server到Tomcat并启动
  3. 运行Log Client的LogProducer main方法
  4. Log Server的Console会有:
    技术分享

 

以上是关于基于ActiveMQ的统一日志服务的主要内容,如果未能解决你的问题,请参考以下文章

zookeeper+activemq+集群消息中间件搭建

ActiveMQ的学习(ActiveMQ的持久化)

基于Ignite+Lucene+Log4j2的分布式统一日志查询最佳实践

系统梳理业务日志记录

Spring学习总结(13)——Spring+Log4j+ActiveMQ实现远程记录日志

基于ActiveMQ的企业级SOA解决方案与源代码实战