当前位置: 首页 > 科技观察

使用Log4j、ActiveMQ和Spring进行异步日志记录

时间:2023-03-14 18:38:06 科技观察

我和我的团队正在创建一个由一组RESTfulJSON服务组成的服务平台,每个服务在平台和/或数据中都有独特的用途。由于平台中生成的日志分散在各处,我们认为最好将它们集中起来并提供一个基本的日常查看工具,使我们能够查看、过滤、排序和搜索我们所有的日志向上。我们也希望我们的日志是异步的,因为我们不希望我们的服务在写入日志的过程中被暂时阻塞(例如,它们可能会直接写入数据库)。实现这一目标的策略非常简单明了。安装ActiveMQ创建log4jlogappender并将日志写入队列(log4j自带这样的appender,不过我们现在自己写一个。)从MQ服务器集写一个消息监听器读取JMS队列中的日志并持久化log来看看这个策略是如何一步步实现的。安装ActiveMQ安装外部ActiveMQ服务器非常简单。此链接http://servicebus.blogspot.com/2011/02/installing-apache-active-mq-on-ubuntu.html是在Ubuntu上安装ActiveMQ的非常好的指南。您还可以选择在您的应用程序中嵌入一个消息代理,这很容易用Spring实现。具体的实现方法我们后面会详细讨论。创建Lo4jJMS日志附加程序首先,让我们创建一个log4jJMS日志附加程序。log4j自带了这样一个appender(appender不是把日志写到一个queue,而是写到一个topic)importjavax.jms.DeliveryMode;importjavax.jms.Destination;导入javax.jms.MessageProducer;importjavax.jmsimportorg.apache.activemq.ActiveMQConnectionFactory;importorg.apache.log4j.Appender;importorg.apache.log4j.AppenderSkeleton;importorg.apache.log4j.Logger;importorg.apache.log4j.PatternLayout;importorg.apache.log4j.spi.LoggingEvent;/***JMSQueueappenderisalog4jappenderthatwritesLoggingEventtoaqueue。*@authorfaheem**/publicclassJMSQueueAppenderextendsAppenderSkeletonimplementsAppender{privatestaticLoggerlogger=Logger.getLogger("JMSQueueAppender");privateStringbrokerUri;私有字符串队列名称;@Overridepublicvoidclose(){}@OverridepublicbooleanrequiresLayout(){returnfalse;}@Overrideprotectedsynchronizedvoidappend(LoggingEventevent){try{ActiveMQConnectionFactoryconnectionFactory=newActiveMQConnectionFactory(this.brokerUri);//创建公司连接javax.jms.Connectionconnection=connectionFactory.createConnection();connection.start();np//创建会话Sessionsession=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);//Createthedestination(TopicorQueue)Destinationdestination=session.createQueue(this.queueName);//创建一个MessageProducer从Session到TopicQueueMessageProducerproducer=session.createProducer(destination);producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);ObjectMessagemessage=session.createObjectMessage(newLoggingEventWrapper(event));//告诉生产者发送消息producer.send(message);//清理session.close();connection.close();}catch(Exceptione){e.printStackTrace();}}publicvoidsetBrokerUri(StringbrokerUri){this.brokerUri=brokerUri;}publicStringgetBrokerUri(){returnbrokerUri;}publicvoidsetQueueName(StringqueueName){this.queueName=queueName;}publicStringgetQueueName(){returnqueueName;}}第37行:log4j将一个LoggingEvent对象作为参数对append方法,这个LoggingEvent对象表示对记录器的调用,它封装了每个日志条目的所有信息第41和42行:创建一个连接工厂对象,以指向JMS的uri作为参数,在我们的例子中接下来,uri指向到我们的ActiveMQ服务器。第45、46和49行:我们与JMS服务器建立连接和会话。会话可以以各种模式打开。在Auto_Acknowledge模式下的会话中,消息的确认会自动发生。在Client_Acknowledge模式下,客户端需要明确确认消息的接收和/或处理。还有其他两种模式。具体可以参考文档http://download.oracle.com/javaee/1.4/api/javax/jms/Session.html第52行:创建队列。将队列名称作为参数发送到连接让我们看看这里发生了什么。第19行:我们实现了Log4J日志附加器接口,这需要我们实现三个方法:requiresLayout、close和append。我们现在将简化事情并实现所需的append方法。当调用记录器时将调用此方法。第56行:我们将发送模式设置为Non_Persistent。另一种可选模式是Persistent,其中消息被持久化到持久性存储系统。持久模式会降低系统速度,但可以提高消息传递的可靠性。第58行:我们在这一行做了很多事情。首先,我将一个LoggingEvent对象封装到一个LoggingEventWrapper对象中。这样做是因为LoggingEvent对象的一些属性不支持序列化,另一个原因是我想记录一些额外的信息,比如IP地址和主机名。接下来,使用JMS会话对象,我们准备一个用于发送的对象(LoggingEventWrapper对象)。第61行:我将对象发送到队列中。下面显示的是LoggingEventWrapper的代码。importjava.io.Serializable;importjava.net.InetAddress;importjava.net.UnknownHostException;importorg.apache.log4j.EnhancedPatternLayout;importorg.apache.log4j.spi.LoggingEvent;/***LoggingEventWrapsalog4jLoggingEvent对象。当LoggingEvent被序列化时,某些信息会丢失*时需要包装。这个想法是从LoggingEvent*对象中提取所需的所有信息,将其放入包装器中,然后序列化LoggingEventWrapper。这样所有必需的数据仍然*可用。*@authorfaheem**/publicclassLoggingEventWrapperimplementsSerializable{privatestaticfinalStringENHANCED_PATTERN_LAYOUT="%throwable";privatestaticfinallongserialVersionUID=3281981073249085474L;私人记录事件记录事件;privateLongtimeStamp;私人字符串级别;私人字符串记录器;私人字符串信息;私人字符串详细信息;私人字符串地址;私人字符串主机名;publicLoggingEventWrapper(LoggingEventloggingEvent){this.loggingEvent=loggingEvent;//格式化ventandsetdetailfieldEnhancedPatternLayoutlayout=newEnhancedPatternLayout();layout.setConversionPattern(ENHANCED_PATTERN_LAYOUT);this.detail=layout.format(this.loggingEvent);}publicLonggetTimeStamp(){returnthis.loggingEvent.timeStamp;}publicStringgetLevel(this)reveltLevel(this).toString();}publicStringgetLogger(){returnthis.loggingEvent.getLoggerName();}publicStringgetMessage(){returnthis.loggingEvent.getRenderedMessage();}publicStringgetDetail(){returnthis.detail;}publicLoggingEventgetLoggingEvent(){returnString;loggingEvent(){returnString;loggingEvent){try{returnInetAddress.getLocalHost().getHostAddress();}catch(UnknownHostExceptione){返回“CouldnotdetermineIP”;}}publicStringgetHostName(){try{returnInetAddress.getLocalHost().getHostName();“无法确定主机名”;}}}#p#MessageListener消息监听器将“监听”队列(或主题),一旦有新消息添加到队列,就会调用onMessage方法。导入javax.jms.JMSException;导入javax.jms.Message;importjavax.jms.MessageListener;importjavax.jms.ObjectMessage;importorg.apache.log4j.Logger;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype。成分;@ComponentpublicclassLogQueueListenerimplementsMessageListener{publicstaticLoggerlogger=Logger.getLogger(LogQueueListener.class);@AutowiredprivateILoggingServiceloggingService;publicvoidonMessage(finalMessagemessage){if(messageinstanceofObjectMessage){try{finalLoggingEventWrapperloggingEventWrapper=(LoggingEventWrapper)((ObjectMessage)message).getObject();loggingService.saveLog(loggingEventWrapper);}catch(finalJMSExceptione){logger.error(e.getMessage(),e);}catch(Exceptione){logger.error(e.getMessage(),e);}}}}第23行:检查从队列中获取的对象是否为ObjectMessage实例第26行:从消息中提取LoggingEventWrapper对象第27行:调用service方法持久化日志Spring配置<豆子xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:jms="http://www.springframework.org/schema/jms"xmlns:amq="http://activemq.apache.org/schema/core"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/jmshttp://www.springframework.org/schema/jms/spring-jms-3.0.xsdhttp://activemq.apache.org/schema/corehttp://activemq.apache.org/schema/core/activemq-core-5.5.0.xsd">第5到9行:使用broker标签设置嵌入式消息代理因为我使用的是外部消息代理,所以我不需要它。第12行:给你你要连接的队列名称第14行:代理服务器的URI第15~19行:连接出厂设置第26~28行:消息监听器设置,这里可以指定用来从队列中读取的消息,并找到消息的个数threads当然上面的例子不能给你用,你还需要包含所有的JMS依赖库,并实现servicethat完成日志持久化任务。不过,我希望这篇文章能为您提供一个不错的主意。英文原文:AsynchronousloggingusingLog4j,ActiveMQandSpring翻译链接:http://www.oschina.net/translate/asynchronous-logging-using-log4j-activemq-and-spring