天天看点

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

随着项目的逐渐扩大,日志的增加也变得更快。log4j是常用的日志记录工具,在有些时候,我们可能需要将log4j的日志发送到专门用于记录日志的远程服务器,特别是对于稍微大一点的应用。这么做的优点有:

可以集中管理日志:可以把多台服务器上的日志都发送到一台日志服务器上,方便管理、查看和分析

可以减轻服务器的开销:日志不在服务器上了,因此服务器有更多可用的磁盘空间

可以提高服务器的性能:通过异步方式,记录日志时服务器只负责发送消息,不关心日志记录的时间和位置,服务器甚至不关心日志到底有没有记录成功

远程打印日志的原理:项目a需要打印日志,而a调用log4j来打印日志,log4j的jmsappender又给配置的地址(activemq地址)发送一条jms消息,此时绑定在queue上的项目b的监听器发现有消息到来,于是立即唤醒监听器的方法开始输出日志。

本文将使用两个java项目product和logging,其中product项目就是模拟线上的项目,而logging项目模拟运行在专用的日志服务器上的项目。说明:本文的例子是在windows平台下。

2. 解压后不需要任何配置,进入到bin下对应的系统架构文件夹

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

3. 双击activemq.bat启动,如果看到类似下面的页面,就代表activemq启动好了:

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

然后打开浏览器,输入地址:http://localhost:8161进入管理页面,用户名admin,密码admin:

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

可以点击manage activemq broker进入queue的查看界面。

我用maven来管理项目,方便维护各种依赖的jar包。先看下项目结构:

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

项目不复杂,主要是4个文件:pom.xml,main.java,log4j.properties和jndi.properties

pom.xml中主要是声明项目的依赖包,其余没有什么东西了:

<a href="http://my.oschina.net/itblog/blog/533730#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<code>&lt;!-- use to call write log methods --&gt;</code>

<code>&lt;</code><code>dependency</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>groupid</code><code>&gt;log4j&lt;/</code><code>groupid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>artifactid</code><code>&gt;log4j&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>version</code><code>&gt;1.2.17&lt;/</code><code>version</code><code>&gt;</code>

<code>&lt;/</code><code>dependency</code><code>&gt;</code>

<code>&lt;!-- log4j uses this lib --&gt;</code>

<code>    </code><code>&lt;</code><code>groupid</code><code>&gt;org.slf4j&lt;/</code><code>groupid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>artifactid</code><code>&gt;slf4j-log4j12&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>version</code><code>&gt;1.7.13&lt;/</code><code>version</code><code>&gt;</code>

<code>&lt;!-- spring jms lib --&gt;</code>

<code>    </code><code>&lt;</code><code>groupid</code><code>&gt;org.springframework&lt;/</code><code>groupid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>artifactid</code><code>&gt;spring-jms&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>version</code><code>&gt;4.0.0.release&lt;/</code><code>version</code><code>&gt;</code>

<code>&lt;!-- activemq lib --&gt;</code>

<code>    </code><code>&lt;</code><code>groupid</code><code>&gt;org.apache.activemq&lt;/</code><code>groupid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>artifactid</code><code>&gt;activemq-core&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>version</code><code>&gt;5.7.0&lt;/</code><code>version</code><code>&gt;</code>

main.java:

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

<code>package</code> <code>com.demo.product;</code>

<code>import</code> <code>javax.jms.connection;</code>

<code>import</code> <code>javax.jms.destination;</code>

<code>import</code> <code>javax.jms.message;</code>

<code>import</code> <code>javax.jms.messageconsumer;</code>

<code>import</code> <code>javax.jms.messagelistener;</code>

<code>import</code> <code>javax.jms.session;</code>

<code>import</code> <code>org.apache.activemq.activemqconnectionfactory;</code>

<code>import</code> <code>org.apache.activemq.command.activemqobjectmessage;</code>

<code>import</code> <code>org.apache.log4j.logger;</code>

<code>import</code> <code>org.apache.log4j.spi.loggingevent;</code>

<code>public</code> <code>class</code> <code>main </code><code>implements</code> <code>messagelistener {</code>

<code>    </code> 

<code>    </code><code>public</code> <code>main() </code><code>throws</code> <code>exception {</code>

<code>        </code><code>// create consumer and listen queue</code>

<code>        </code><code>activemqconnectionfactory factory = </code>

<code>                </code><code>new</code> <code>activemqconnectionfactory(</code><code>"tcp://localhost:61616"</code><code>);</code>

<code>        </code><code>connection connection = factory.createconnection();</code>

<code>        </code><code>session session = connection.createsession(</code><code>false</code><code>, session.auto_acknowledge);</code>

<code>        </code><code>connection.start();</code>

<code>        </code><code>//////////////注意这里jmsappender只支持topicdestination,下面会说到////////////////</code>

<code>        </code><code>destination topicdestination = session.createtopic(</code><code>"logtopic"</code><code>);</code>

<code>        </code><code>messageconsumer consumer = session.createconsumer(topicdestination);</code>

<code>        </code><code>consumer.setmessagelistener(</code><code>this</code><code>);</code>

<code>        </code> 

<code>        </code><code>// log a message</code>

<code>        </code><code>logger logger = logger.getlogger(main.</code><code>class</code><code>);</code>

<code>        </code><code>logger.info(</code><code>"info log."</code><code>);</code>

<code>        </code><code>logger.warn(</code><code>"warn log"</code><code>);</code>

<code>        </code><code>logger.error(</code><code>"error log."</code><code>);</code>

<code>        </code><code>// clean up</code>

<code>        </code><code>thread.sleep(</code><code>1000</code><code>);</code>

<code>        </code><code>consumer.close();</code>

<code>        </code><code>session.close();</code>

<code>        </code><code>connection.close();</code>

<code>        </code><code>system.exit(</code><code>1</code><code>);</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) </code><code>throws</code> <code>exception {</code>

<code>        </code><code>new</code> <code>main();</code>

<code>    </code><code>public</code> <code>void</code> <code>onmessage(message message) {</code>

<code>        </code><code>try</code> <code>{</code>

<code>            </code><code>// receive log event in your consumer</code>

<code>            </code><code>loggingevent event = (loggingevent)((activemqobjectmessage)message).getobject();</code>

<code>            </code><code>system.out.println(</code><code>"received log ["</code> <code>+ event.getlevel() + </code><code>"]: "</code><code>+ event.getmessage());</code>

<code>        </code><code>} </code><code>catch</code> <code>(exception e) {</code>

<code>            </code><code>e.printstacktrace();</code>

<code>        </code><code>}</code>

<code>}</code>

说明:然后是log4j.properties:

<code>log4j.rootlogger=info, stdout, jms</code>

<code> </code> 

<code>## be sure that activemq messages are not logged to 'jms' appender</code>

<code>log4j.logger.org.apache.activemq=info, stdout</code>

<code>log4j.appender.stdout=org.apache.log4j.consoleappender</code>

<code>log4j.appender.stdout.layout=org.apache.log4j.patternlayout</code>

<code>log4j.appender.stdout.layout.conversionpattern=%d %-5p %c - %m%n</code>

<code>## configure 'jms' appender. you'll also need jndi.properties file in order to make it work</code>

<code>log4j.appender.jms=org.apache.log4j.net.jmsappender</code>

<code>log4j.appender.jms.initialcontextfactoryname=org.apache.activemq.jndi.activemqinitialcontextfactory</code>

<code>log4j.appender.jms.providerurl=tcp://localhost:61616</code>

<code>log4j.appender.jms.topicbindingname=logtopic</code>

<code>log4j.appender.jms.topicconnectionfactorybindingname=connectionfactory</code>

其实按理说只需要这么三个文件就可以了,但是这时候执行会报错:

<code>javax.naming.namenotfoundexception: logtopic</code>

<code>    </code><code>at org.apache.activemq.jndi.readonlycontext.lookup(readonlycontext.java:</code><code>235</code><code>)</code>

<code>    </code><code>at javax.naming.initialcontext.lookup(unknown source)</code>

<code>    </code><code>at org.apache.log4j.net.jmsappender.lookup(jmsappender.java:</code><code>245</code><code>)</code>

<code>    </code><code>at org.apache.log4j.net.jmsappender.activateoptions(jmsappender.java:</code><code>222</code><code>)</code>

<code>    </code><code>at org.apache.log4j.config.propertysetter.activate(propertysetter.java:</code><code>307</code><code>)</code>

<code>        </code><code>...</code>

<code>    </code><code>at org.apache.activemq.activemqprefetchpolicy.&lt;clinit&gt;(activemqprefetchpolicy.java:</code><code>39</code><code>)</code>

<code>    </code><code>at org.apache.activemq.activemqconnectionfactory.&lt;init&gt;(activemqconnectionfactory.java:</code><code>84</code><code>)</code>

<code>    </code><code>at org.apache.activemq.activemqconnectionfactory.&lt;init&gt;(activemqconnectionfactory.java:</code><code>137</code><code>)</code>

<code>    </code><code>at com.demo.product.main.&lt;init&gt;(main.java:</code><code>20</code><code>)</code>

<code>    </code><code>at com.demo.product.main.main(main.java:</code><code>43</code><code>)</code>

为什么会报错呢?来看看jmsappender的javadoc文档,它是这么描述的:

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

大意是说,jmsappender需要一个jndi配置来初始化一个jndi上下文(context)。因为有了这个上下文才能管理jms topic和topic的连接。于是为项目配置一个叫jndi.properties的文件,其内容为:

<code>topic.logtopic=logtopic</code>

然后再运行就不会报错了。我们先来看看activemq(注意切换到topic标签页下):

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

可以看到,主题为logtopic的消息,有3条进queue,这3条也出queue了。而出queue的消息,已经被我们的监听器收到并打印出来了:

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

需要注意的是,本例只是一个很简单的例子,目的是阐明远程打印日志的原理。实际项目中,一般日志服务器上运行着的,不是项目,而是专用的日志记录器。下面,我们就把这个项目拆分成两个项目,并用spring来管理这些用到的bean

修改后的product的项目结构并没有改变,改变的只是main类:

<code>public</code> <code>class</code> <code>main{</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>logger logger = logger.getlogger(main.</code><code>class</code><code>);</code>

<code>        </code><code>// just log a message</code>

<code>        </code><code>system.exit(</code><code>0</code><code>);</code>

这个main类和普通的logger调用一样,仅仅负责打印日志。有没有觉得太简单了呢?

来看看项目结构图:

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

为了让监听器一直活着,我把logging写成了一个web项目,跑在tomcat上。index.jsp就是个hello world字符串而已,用来验证logging活着。注意,在logging项目中,已没有product项目中的log4j.properties和jndi.properties两个文件。

来看看另外几个文件:

pom.xml(每个包的目的都写在注释里了):

<code>&lt;!-- use to cast object to logevent when received a log --&gt;</code>

<code>&lt;!-- use to receive jms message --&gt;</code>

<code>&lt;!-- use to load spring.xml --&gt;</code>

<code>    </code><code>&lt;</code><code>artifactid</code><code>&gt;spring-web&lt;/</code><code>artifactid</code><code>&gt;</code>

web.xml

<code>&lt;!</code><code>doctype</code> <code>web-app public</code>

<code> </code><code>"-//sun microsystems, inc.//dtd web application 2.3//en"</code>

<code> </code><code>"http://java.sun.com/dtd/web-app_2_3.dtd" &gt;</code>

<code>&lt;</code><code>web-app</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>context-param</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>param-name</code><code>&gt;contextconfiglocation&lt;/</code><code>param-name</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>param-value</code><code>&gt;classpath:spring.xml&lt;/</code><code>param-value</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>context-param</code><code>&gt;</code>

<code>    </code><code>&lt;!-- use to load spring.xml --&gt;</code>

<code>    </code><code>&lt;</code><code>listener</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>listener-class</code><code>&gt;</code>

<code>            </code><code>org.springframework.web.context.contextloaderlistener</code>

<code>        </code><code>&lt;/</code><code>listener-class</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>listener</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>welcome-file-list</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>welcome-file</code><code>&gt;index.jsp&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>welcome-file-list</code><code>&gt;</code>

<code>&lt;/</code><code>web-app</code><code>&gt;</code>

spring.xml

<code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"utf-8"</code><code>?&gt;</code>

<code>&lt;</code><code>beans</code> <code>xmlns</code><code>=</code><code>"http://www.springframework.org/schema/beans"</code>

<code>    </code><code>xmlns:xsi</code><code>=</code><code>"http://www.w3.org/2001/xmlschema-instance"</code> 

<code>    </code><code>xsi:schemalocation="</code>

<code>        </code><code>http://www.springframework.org/schema/beans </code>

<code>        </code><code>http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"jmstemplate"</code> <code>class</code><code>=</code><code>"org.springframework.jms.core.jmstemplate"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"connectionfactory"</code> <code>ref</code><code>=</code><code>"connectionfactory"</code><code>/&gt;</code>

<code>    </code><code>&lt;/</code><code>bean</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"connectionfactory"</code> <code>class</code><code>=</code><code>"org.springframework.jms.connection.singleconnectionfactory"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"targetconnectionfactory"</code> <code>ref</code><code>=</code><code>"targetconnectionfactory"</code><code>/&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"targetconnectionfactory"</code> <code>class</code><code>=</code><code>"org.apache.activemq.activemqconnectionfactory"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"brokerurl"</code> <code>value</code><code>=</code><code>"tcp://localhost:61616"</code><code>/&gt;</code>

<code>&lt;!-- as jmsappender only support the topic way to send messages, </code>

<code>     </code><code>thus queuedestination here is useless.</code>

<code>    </code><code>&lt;bean id="queuedestination" class="org.apache.activemq.command.activemqqueue"&gt;</code>

<code>        </code><code>&lt;constructor-arg name="name" value="queue" /&gt;</code>

<code>    </code><code>&lt;/bean&gt;</code>

<code> </code><code>--&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"topicdestination"</code> <code>class</code><code>=</code><code>"org.apache.activemq.command.activemqtopic"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>constructor-arg</code> <code>name</code><code>=</code><code>"name"</code> <code>value</code><code>=</code><code>"logtopic"</code> <code>/&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"jmscontainer"</code> <code>class</code><code>=</code><code>"org.springframework.jms.listener.defaultmessagelistenercontainer"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"connectionfactory"</code> <code>ref</code><code>=</code><code>"connectionfactory"</code> <code>/&gt;</code>

<code>         </code><code>&lt;!-- &lt;property name="destination" ref="queuedestination" /&gt;  --&gt;</code>

<code>         </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"destination"</code> <code>ref</code><code>=</code><code>"topicdestination"</code> <code>/&gt;</code>

<code>         </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"messagelistener"</code> <code>ref</code><code>=</code><code>"logmessagelistener"</code> <code>/&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"logmessagelistener"</code> <code>class</code><code>=</code><code>"com.demo.logging.logmessagelistener"</code><code>/&gt;</code>

<code>&lt;/</code><code>beans</code><code>&gt;</code>

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

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

<code>package</code> <code>com.demo.logging;</code>

<code>public</code> <code>class</code> <code>logmessagelistener </code><code>implements</code> <code>messagelistener {</code>

<code>            </code><code>system.out.println(</code><code>"logging project: ["</code> <code>+ event.getlevel() + </code><code>"]: "</code><code>+ event.getmessage());</code>

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

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

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

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

接下来去看看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类将loggingevent实例序列化成objectmessage,并将其发送到jms server的一个指定topic中,因此,使用此种将日志发送到远程的方式只支持topic方式发送,不支持queue方式发送。我们再log4j.properties中配置了这一句:

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

Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析

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

<code>log4j.appender.jms.providerurl=tcp:</code><code>//localhost:61616</code>

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

58

59

60

61

62

63

64

65

66

67

68

69

70

<code>/**</code>

<code> </code><code>* options are activated and become effective only after calling this method.</code>

<code> </code><code>*/</code>

<code>public</code> <code>void</code> <code>activateoptions() {</code>

<code>    </code><code>topicconnectionfactory topicconnectionfactory;</code>

<code>    </code><code>try</code> <code>{</code>

<code>        </code><code>context jndi;</code>

<code>        </code><code>loglog.debug(</code><code>"getting initial context."</code><code>);</code>

<code>        </code><code>if</code> <code>(initialcontextfactoryname != </code><code>null</code><code>) {</code>

<code>            </code><code>properties env = </code><code>new</code> <code>properties();</code>

<code>            </code><code>env.put(context.initial_context_factory, initialcontextfactoryname);</code>

<code>            </code><code>if</code> <code>(providerurl != </code><code>null</code><code>) {</code>

<code>                </code><code>env.put(context.provider_url, providerurl);</code>

<code>            </code><code>} </code><code>else</code> <code>{</code>

<code>                </code><code>loglog.warn(</code><code>"you have set initialcontextfactoryname option but not the "</code>

<code>                        </code><code>+ </code><code>"providerurl. this is likely to cause problems."</code><code>);</code>

<code>            </code><code>}</code>

<code>            </code><code>if</code> <code>(urlpkgprefixes != </code><code>null</code><code>) {</code>

<code>                </code><code>env.put(context.url_pkg_prefixes, urlpkgprefixes);</code>

<code>            </code><code>if</code> <code>(securityprincipalname != </code><code>null</code><code>) {</code>

<code>                </code><code>env.put(context.security_principal, securityprincipalname);</code>

<code>                </code><code>if</code> <code>(securitycredentials != </code><code>null</code><code>) {</code>

<code>                    </code><code>env.put(context.security_credentials, securitycredentials);</code>

<code>                </code><code>} </code><code>else</code> <code>{</code>

<code>                    </code><code>loglog.warn(</code><code>"you have set securityprincipalname option but not the "</code>

<code>                            </code><code>+ </code><code>"securitycredentials. this is likely to cause problems."</code><code>);</code>

<code>                </code><code>}</code>

<code>            </code><code>jndi = </code><code>new</code> <code>initialcontext(env);</code>

<code>        </code><code>} </code><code>else</code> <code>{</code>

<code>            </code><code>jndi = </code><code>new</code> <code>initialcontext();</code>

<code>        </code><code>loglog.debug(</code><code>"looking up ["</code> <code>+ tcfbindingname + </code><code>"]"</code><code>);</code>

<code>        </code><code>topicconnectionfactory = (topicconnectionfactory) lookup(jndi, tcfbindingname);</code>

<code>        </code><code>loglog.debug(</code><code>"about to create topicconnection."</code><code>);</code>

<code>        </code><code>///////////////////////////////注意这里只会创建topicconnection////////////////////////////</code>

<code>        </code><code>if</code> <code>(username != </code><code>null</code><code>) {</code>

<code>            </code><code>topicconnection = topicconnectionfactory.createtopicconnection(username, password);</code>

<code>            </code><code>topicconnection = topicconnectionfactory.createtopicconnection();</code>

<code>        </code><code>loglog.debug(</code><code>"creating topicsession, non-transactional, "</code> <code>+ </code><code>"in auto_acknowledge mode."</code><code>);</code>

<code>        </code><code>topicsession = topicconnection.createtopicsession(</code><code>false</code><code>, session.auto_acknowledge);</code>

<code>        </code><code>loglog.debug(</code><code>"looking up topic name ["</code> <code>+ topicbindingname + </code><code>"]."</code><code>);</code>

<code>        </code><code>topic topic = (topic) lookup(jndi, topicbindingname);</code>

<code>        </code><code>loglog.debug(</code><code>"creating topicpublisher."</code><code>);</code>

<code>        </code><code>topicpublisher = topicsession.createpublisher(topic);</code>

<code>        </code><code>loglog.debug(</code><code>"starting topicconnection."</code><code>);</code>

<code>        </code><code>topicconnection.start();</code>

<code>        </code><code>jndi.close();</code>

<code>    </code><code>} </code><code>catch</code> <code>(jmsexception e) {</code>

<code>        </code><code>errorhandler.error(</code><code>"error while activating options for appender named ["</code> <code>+ name + </code><code>"]."</code><code>, e,</code>

<code>                </code><code>errorcode.generic_failure);</code>

<code>    </code><code>} </code><code>catch</code> <code>(namingexception e) {</code>

<code>    </code><code>} </code><code>catch</code> <code>(runtimeexception e) {</code>

上面初始化了一个topicconnection,一个topicsession,一个topicpublisher。咱们再来看看这个appender的append方法:

<code> </code><code>* this method called by {@link appenderskeleton#doappend} method to do most</code>

<code> </code><code>* of the real appending work.</code>

<code>public</code> <code>void</code> <code>append(loggingevent event) {</code>

<code>    </code><code>if</code> <code>(!checkentryconditions()) {</code>

<code>        </code><code>return</code><code>;</code>

<code>        </code><code>objectmessage msg = topicsession.createobjectmessage();</code>

<code>        </code><code>if</code> <code>(locationinfo) {</code>

<code>            </code><code>event.getlocationinformation();</code>

<code>        </code><code>msg.setobject(event);</code>

<code>        </code><code>topicpublisher.publish(msg);</code><code>///////////////注意这一句//////////////</code>

<code>        </code><code>errorhandler.error(</code><code>"could not publish message in jmsappender ["</code> <code>+ name + </code><code>"]."</code><code>, </code>

<code>            </code><code>e, errorcode.generic_failure);</code>

这里使用topicpublisher.publish()方法,把序列化的消息发布出去。可见这也证明了jmsappender只支持以topic方式发送消息。