Mule ESB項目的日誌輸出有兩種方式,能夠在流程中添加Logger組件輸出日誌,也能夠在自定義的代碼中添加日誌輸出。Mule ESB日誌使用Log4j2庫進行輸出,Mule ESB 企業版使用的log4j2版本是2.1。java
咱們在ESB項目中拖入一個Logger控件,輸出通過Transformer轉化後的Json 報文。
web
這裏Logger控件裏的Message內容爲#[message.payloadAs(java.lang.String)],使用的是MEL(Mule Expression Language),等效於message.getPayloadAsString()spring
拖拽Logger控件後,在項目的src/main/resources目錄下生成了log4j2.xml文件,用於配置Log4j2的日誌輸出express
log4j2.xml的內容以下:apache
<?xml version="1.0" encoding="utf-8"?> <Configuration> <Appenders> <RollingFile name="file" fileName="${sys:mule.home}${sys:file.separator}logs${sys:file.separator}testproject.log" filePattern="${sys:mule.home}${sys:file.separator}logs${sys:file.separator}testproject-%i.log"> <PatternLayout pattern="%d [%t] %-5p %c - %m%n" /> <SizeBasedTriggeringPolicy size="10 MB" /> <DefaultRolloverStrategy max="10"/> </RollingFile> </Appenders> <Loggers> <!-- CXF is used heavily by Mule for web services --> <AsyncLogger name="org.apache.cxf" level="WARN"/> <!-- Apache Commons tend to make a lot of noise which can clutter the log--> <AsyncLogger name="org.apache" level="WARN"/> <!-- Reduce startup noise --> <AsyncLogger name="org.springframework.beans.factory" level="WARN"/> <!-- Mule classes --> <AsyncLogger name="org.mule" level="INFO"/> <AsyncLogger name="com.mulesoft" level="INFO"/> <!-- Reduce DM verbosity --> <AsyncLogger name="org.jetel" level="WARN"/> <AsyncLogger name="Tracking" level="WARN"/> <AsyncRoot level="INFO"> <AppenderRef ref="file" /> </AsyncRoot> </Loggers> </Configuration>
能夠看出Mule ESB的日誌輸出採用的是異步方式。json
以Debug方式啓動ESB項目, 調用ESB接口,控制檯輸出了Json報文日誌api
INFO 2016-06-29 15:15:33,335 [[testproject].HTTP_Listener_Configuration.worker.01] org.mule.api.processor.LoggerMessageProcessor: {"students":[{"name":"張三","id":"197","class":"1年1班"},{"name":"李四","id":"198","class":"1年2班"},{"name":"趙五","id":"199","class":"1年3班"}]}
同時查看項目對應的日誌文件(位置在Anypoint Studio的workspace目錄的.mule/logs子目錄下)tomcat
打開testproject.log文件,能夠看到上述的日誌信息也寫入了日誌文件。服務器
從日誌信息能夠看出,Logger控件的日誌是在org.mule.api.processor.LoggerMessageProcessor類中輸出的,具體是在log(MuleEvent event)方法中輸出的app
protected void log(MuleEvent event) { if (event == null) { logWithLevel(null); } else { if (StringUtils.isEmpty(message)) { logWithLevel(event.getMessage()); } else { LogLevel logLevel = LogLevel.valueOf(level); if (LogLevel.valueOf(level).isEnabled(logger)) { logLevel.log(logger, expressionManager.parse(message, event)); } } } } public enum LogLevel { INFO { @Override public void log(Log logger, Object object) { logger.info(object); }
而記錄日誌的logger對象是在ESB項目啓動,加載Mule容器時調用LoggerMessageProcessor類的initLogger方法構造的,構造Logger的大體流程是這樣的:
這裏的流程圖只描繪了LoggerMessageProcessor的logger對象構建的幾個主要類和方法,能夠看出Logger控件的日誌輸出與Mule容器的啓動和初始化密切相關。若是ESB項目遷移到Web項目,則實際運行環境變成了Tomcat環境,加載類變成了org.mule.config.builders.MuleXmlBuilderContextListener,而咱們查看MuleXmlBuilderContextListener類的初始化方法
public void initialize(ServletContext context) { String config = context.getInitParameter(INIT_PARAMETER_MULE_CONFIG); .................... try { muleContext = createMuleContext(config, context); context.setAttribute(MuleProperties.MULE_CONTEXT_PROPERTY, muleContext); muleContext.start(); } ....................
能夠看出這裏沒有對MuleContainer的初始化方法調用,Logger Component使用的Logger對象沒有被初始化,所以在Web項目裏使用Logger組件將不會輸出日誌,不管是控制檯仍是文件,咱們須要自定義Logger類輸出日誌。
咱們在用於轉換的Transformer類中添加Log4j2的Logger對象
private static Logger logger = LogManager.getLogger(CustomJsonTransformer.class);
再在json報文轉換結束後使用這個logger對象輸出轉換後的json報文。
try { String jsonMessage = message.getPayloadAsString(); //添加信息 JSONObject jsonMap = updateStudentInfos(jsonMessage); transformJsonStr = jsonMap.toJSONString(); if(!Strings.isBlank(transformJsonStr)) { logger.info("The json message after transformation is:" + transformJsonStr); } } catch (Exception e) { e.printStackTrace(); }
由於咱們使用的是Log4j2在Tomcat容器中進行日誌輸出,根據查閱的資料,咱們須要引入log4j-web這個jar包,由於Mule默認使用的log4j-core版本是2.1,咱們引入的log4j-web也使用2.1版本,將這個jar文件拷貝到mule_libs/opt目錄下。
此外咱們須要修改log4j2.xml文件,ESB項目建立的log4j2.xml的日誌文件輸出到mule的workspace目錄下,咱們將其修改成輸出到tomcat的logs目錄下,修改後的log4j2.xml文件
<?xml version="1.0" encoding="utf-8"?> <Configuration> <properties> <property name="logPath">${sys:catalina.home}/logs/</property> </properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d [%t] %-5p %c - %m%n"/> </Console> <!-- <RollingFile name="file" fileName="${logPath}/testproject.log" filePattern="${logPath}/testproject-%d{MM-dd-yyyy}-%i.log"> --> <RollingFile name="file" fileName="${logPath}/testproject.log" filePattern="${logPath}/testproject-%i.log" append="true" immediateFlush="true"> <PatternLayout pattern="%d [%t] %-5p %c - %m%n" /> <!-- <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> --> <SizeBasedTriggeringPolicy size="10 MB" /> <DefaultRolloverStrategy max="10"/> </RollingFile> </Appenders> <Loggers> <!-- Apache Commons tend to make a lot of noise which can clutter the log--> <AsyncLogger name="org.apache" level="WARN"/> <!-- Reduce startup noise --> <AsyncLogger name="org.springframework.beans.factory" level="WARN"/> <!-- Mule classes --> <AsyncLogger name="org.mule" level="INFO"/> <AsyncLogger name="com.mulesoft" level="INFO"/> <Logger name="com.mule.spring" level="INFO"/> <Root level="INFO"> <AppenderRef ref="Console"/> <AppenderRef ref="file" /> </Root> </Loggers> </Configuration>
這裏的${sys:catalina.home}指的是當前運行的tomcat根目錄,另外基於咱們自定義的代碼包路徑,咱們添加了一個Logger。
須要注意的是在web項目中,log4j2.xml文件必須放置在WEB-INF根目錄下,和web.xml同一級目錄,爲此咱們須要將log4j2.xml文件移動到src/main/app目錄下。
修改完成後,咱們部署從新生成的web項目到tomcat環境,調用接口。
能夠看到Tomcat的運行時窗口輸出了日誌信息:
同時在tomcat的logs目錄下生成了testproject.log文件,
testproject.log文件中輸出了控制檯窗口輸出的json報文日誌
使用自定義Logger,咱們能夠將須要的程序運行信息輸出到控制檯和日誌文件,對於自定義代碼中拋出的異常,咱們能夠直接輸出日誌,但若是是流程運行過程當中拋出的異常信息,該如何捕捉異常信息,並將其輸出呢?
咱們須要使用Mule的Catch Exception Strategy控件。
咱們在流程文件中加入Catch Exception Strategy控件,放置在Error Handling下
咱們在這個控件中拖入兩個控件,Set Payload和Logger控件,
<catch-exception-strategy doc:name="Catch Exception Strategy"> <set-payload value="#[exception.cause.message]" encoding="UTF-8" mimeType="text/plain" doc:name="Set Payload"/> <logger message="#[exception.cause.message]" level="ERROR" doc:name="Logger"/> </catch-exception-strategy>
Set Payload控件將異常信息設置爲Mule Message的Payload,返回給調用端(不然Mule Message的Payload仍然是請求的Payload),logger控件則將異常信息做爲日誌輸出。
#[message.cause.exception]一樣是MEL表達式,表示異常的Root Cause信息。
添加完異常處理控件後,咱們修改自定義的Transformer類的transformMessage方法,將原先返回的轉換好的json報文替換爲null。這樣當運行到Data Weaver數據映射時,流程將會拋出異常,咱們能夠查看異常信息是如何被Catch Exception Strategy控件捕捉並處理的。
調用ESB接口後,系統輸出的異常日誌爲:
ERROR 2016-06-29 18:20:30,035 [[testproject].HTTP_Listener_Configuration.worker.01] org.mule.api.processor.LoggerMessageProcessor: The object transformed is of type: "SimpleDataType{type=org.mule.transport.NullPayload, mimeType='*/*', encoding='null'}", but the expected return type is "SimpleDataType{type=java.lang.String, mimeType='application/json', encoding='UTF-8'}".
調用端返回的響應消息是:
這裏輸出的異常信息僅顯示了異常的Root Cause信息,若是要詳細的堆棧信息,咱們須要修改#[message.cause.exception]爲#[org.mule.util.ExceptionUtils.getFullStackTrace(exception)]
<catch-exception-strategy doc:name="Catch Exception Strategy"> <set-payload value="#[org.mule.util.ExceptionUtils.getFullStackTrace(exception)]" encoding="UTF-8" mimeType="text/plain" doc:name="Set Payload"/> <logger message="#[org.mule.util.ExceptionUtils.getFullStackTrace(exception)]" level="ERROR" doc:name="Logger"/> </catch-exception-strategy>
修改後再調用接口,異常的堆棧信息被輸出到日誌和調用端
因爲添加了Catch Exception Strategy控件,流程運行過程當中的異常被捕捉了,返回的響應狀態代碼變成了200,這顯然不是服務器端真實的狀態,所以咱們須要從新設置響應的Status Code.
咱們在Catch Exception Strategy控件中添加設置Status Code狀態的代碼
<set-property propertyName="http.status" value="#[500]" doc:name="Set Http Status" />
再次調用接口,能夠看到返回的響應Status Code變成了500
在Web項目中,咱們不能使用Logger輸出日誌,咱們有三種方式輸出日誌:
1)自定義Transformer中添加Logger輸出日誌。
2)自定義Component中添加Logger輸出日誌。
3)自定義MessageProcessor在處理Mule Event時輸出日誌。
第一種方式上面已經提到了,這裏再也不贅述,重點說一下第二種和第三種方式。
從Mule 3.8起,自定義Component須要實現接口org.mule.api.lifecycle.Callable的onCall方法
咱們自定義的Component類代碼以下:
package com.mule.spring.components; import org.mule.api.MuleEventContext; import org.mule.api.lifecycle.Callable; import org.mule.util.ExceptionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class CustomComponent implements Callable { private static Logger logger = LogManager.getLogger(CustomComponent.class); @Override public Object onCall(MuleEventContext eventContext) throws Exception { String exceptionMessage = ExceptionUtils.getFullStackTrace(eventContext.getMessage().getExceptionPayload().getException()); logger.error(exceptionMessage); return eventContext.getMessage(); } }
在Catch Exception Strategy中引用這個Component以下:
<component class="com.mule.spring.components.CustomComponent" doc:name="Custom Component"/>
自定義MessageProcessor以下:
package com.mule.spring.messageprocessors; import org.mule.api.MuleEvent; import org.mule.api.MuleException; import org.mule.api.processor.MessageProcessor; import org.mule.util.ExceptionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class LoggerMessageProcessor implements MessageProcessor { private static Logger logger = LogManager.getLogger(LoggerMessageProcessor.class); @Override public MuleEvent process(MuleEvent event) throws MuleException { String exceptionMessage = ExceptionUtils.getFullStackTrace(event.getMessage().getExceptionPayload().getException()); logger.error(exceptionMessage); return event; } }
在Catch Exception Strategy中引用這個Message Processor以下:
<custom-processor class="com.mule.spring.messageprocessors.LoggerMessageProcessor" />
因爲輸出堆棧信息時引用了common-lang的ExceptionUtils類(org.mule.utils.ExceptionUtils的父類),咱們須要在pom.xml中引入common-lang的jar包保證編譯經過。
<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> <scope>provided</scope> </dependency>
Web項目最後的Catch Exception Strategy設置以下:
<catch-exception-strategy doc:name="Catch Exception Strategy"> <set-payload value="#[org.mule.util.ExceptionUtils.getFullStackTrace(exception)]" encoding="UTF-8" mimeType="text/plain" doc:name="Set Payload"/> <!-- <component class="com.mule.spring.components.CustomComponent" doc:name="Custom Component"/> --> <custom-processor class="com.mule.spring.messageprocessors.LoggerMessageProcessor" /> <set-property propertyName="http.status" value="#[500]" doc:name="Set Http Status" /> <message-properties-transformer> <add-message-property key="Content-Type" value="text/plain;charset=utf-8" /> </message-properties-transformer> </catch-exception-strategy>
這裏設置Content-Type爲text/plain,由於返回的異常堆棧信息是純文本形式,不是json或者xml形式。
從新編譯web項目並部署,調用接口,能夠看到返回的響應是500: Internal Server Error
在testproject.log文件中也顯示了異常堆棧信息