AMQ學習筆記 - 15. 實踐方案:基於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     
11     <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
12         <property name="brokerURL" value="${topic.brokerURL}" />
13         <!-- add trusted packages. see http://activemq.apache.org/objectmessage.html -->
14         <property name="trustedPackages">
15             <list>
16                 <value>org.apache.log4j.spi</value>
17             </list>
18         </property>
19     </bean>
20     <bean id="connectionFactory"
21         class="org.springframework.jms.connection.SingleConnectionFactory">
22         <property name="targetConnectionFactory" ref="targetConnectionFactory" />
23     </bean>
24     
25     <bean id="destination" class="org.apache.activemq.command.ActiveMQTopic">
26         <constructor-arg name="name" value="${topic.topicName}" />
27     </bean>
28     
29     <!-- define the message-listener to receive and dipose log data. -->
30     <bean id="messageListener" class="cn.sinobest.asj.logserver.LogListener" />
31     
32     <bean id="jmsContainer"
33         class="org.springframework.jms.listener.DefaultMessageListenerContainer">
34         <property name="connectionFactory" ref="connectionFactory" />
35         <property name="destination" ref="destination" />
36         <property name="messageListener" ref="messageListener" />
37     </bean>
38 </beans>

 

說明:
在targetConnectionFactory的屬性中,指定了trustedPackages。ActiveMQ自5.12.2版本之后,強制用戶指定一份可信任的packages白名單,以對付ObjectMessage存在的安全漏洞。具體內容可參考: http://activemq.apache.org/objectmessage.html

1.2.4. topic.properties

1 topic.brokerURL=tcp://localhost:61616
2 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

1 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會有:






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM