是否有可能使用CXF将请求/响应记录为XML,最好是记录到一个单独的文件中,以便我可以监视应用程序在做什么?
将以下内容添加到您的端点和客户端:
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
这会将所有内容记录到服务器日志中。
如果要在其他地方记录它们,请查看内置CXF LoggingInInterceptor和LoggingOutInterceptor的源代码。您可以按照他们使用的模式来抓取进/出消息,并随心所欲地对其进行处理。
使用类似这样的东西将自己的拦截器添加到链中:
<jaxws:inInterceptors>
<ref bean="myLoggingInInterceptor" />
</jaxws:inInterceptors>
所以,我尝试了更多。要获取记录的XML请求和答复,并且如果您使用的是Log4J,则需要在log4j.xml文件中设置CXF的日志级别,如下所示(> = INFO):
<logger name="org.apache.cxf" >
<level value="INFO" />
</logger>
并且cxf.xml文件应包含以下内容:
<cxf:bus>
<cxf:features>
<cxf:logging/>
</cxf:features>
</cxf:bus>
两个文件都应该在CLASSPATH中。
要显示肥皂消息,请将其添加到您的代码中:
Client client = ClientProxy.getClient(service);
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
请求soap xml可以通过自定义In拦截器轻松记录。说,我们有一个名为“ wsLoggingInInterceptor”的拦截器,因此在上下文文件中,它将类似于以下内容:
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
<bean id="logOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
<bean id="wsLoggingInInterceptor" class="org.jinouts.webservice.logging.WSLoggingInInterceptor"/>
<cxf:bus>
<cxf:inInterceptors>
<ref bean="loggingInInterceptor"/>
<ref bean="wsLoggingInInterceptor"/>
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="logOutInterceptor"/>
</cxf:outInterceptors>
</cxf:bus>
在该类中,我们可以按照以下方式获取请求xml:
public class WSLoggingInInterceptor extends AbstractSoapInterceptor
{
public WSLoggingInInterceptor ()
{
super(Phase.RECEIVE);
}
@Override
public void handleMessage ( SoapMessage message ) throws Fault
{
//get the remote address
HttpServletRequest httpRequest = (HttpServletRequest) message.get ( AbstractHTTPDestination.HTTP_REQUEST );
System.out.println ("Request From the address : " + httpRequest.getRemoteAddr ( ) );
try
{
// now get the request xml
InputStream is = message.getContent ( InputStream.class );
CachedOutputStream os = new CachedOutputStream ( );
IOUtils.copy ( is, os );
os.flush ( );
message.setContent ( InputStream.class, os.getInputStream ( ) );
is.close ( );
System.out.println ("The request is: " + IOUtils.toString ( os.getInputStream ( ) ));
os.close ( );
}
catch ( Exception ex )
{
ex.printStackTrace ( );
}
}
}
看,这里我也记录了请求的来源地址。您还可以从“ HttpServletRequest”对象获取更多信息。您可以从中获得更多:http://cxf.apache.org/docs/interceptors.html
要记录响应xml,可以查看this thread
[如果您将Spring与Java配置一起使用,则有2种简单的方法可以使用Apache CXF激活SOAP消息的记录:
直接在SpringBus上-如果要记录所有CXF端点的消息,则很有用:
@Bean(name=Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus springBus = new SpringBus();
LoggingFeature logFeature = new LoggingFeature();
logFeature.setPrettyLogging(true);
logFeature.initialize(springBus);
springBus.getFeatures().add(logFeature);
return springBus;
}
分别在每个暴露的CXF端点上激活日志记录
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), weatherService());
endpoint.publish(SERVICE_NAME_URL_PATH);
endpoint.setWsdlLocation("Weather1.0.wsdl");
LoggingFeature logFeature = new LoggingFeature();
logFeature.setPrettyLogging(true);
logFeature.initialize(springBus());
endpoint.getFeatures().add(logFeature);
return endpoint;
}
提醒LoggingFeature.setPrettyLogging(true);查看漂亮的SOAP消息和LoggingFeature.initialize(springBus());的方法-没有后者,魔术就不会发生。对于更简洁的代码,您还可以将LoggingFeature分离为单独的Bean,并将其注入到SpringBus或Endpoint-Bean中。
将自己的记录器添加到端点属性要容易得多。在这种情况下,默认日志记录拦截器将在端点属性中查找您的日志记录器,如果找到它,它将使用它,否则它将创建默认日志记录器。这是我的用法示例:
<jaxws:endpoint
xmlns:client="http://service.info.client.diasoft.services.stream.integration.cib.sberbank.ru"
address="/diasoft/clientInfoWS"
serviceName="client:ClientWS"
implementor="#clientServiceImpl">
<jaxws:properties>
<entry key="MessageLogger" value-ref="logger"/>
</jaxws:properties>
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature"/>
</jaxws:features>
</jaxws:endpoint>
<bean id="logger" class="org.apache.cxf.common.logging.LogUtils" factory-method="getLogger">
<constructor-arg value="ru.sberbank.cib.integration.stream.services.diasoft.client.info.service.ClientWSImpl"/>
</bean>
就我而言,我必须维护一个jaxb生成的客户端。所以我在applicationContext中发现了以下情况:
<jaxws:client id="aService"
serviceClass="org.xxx.ServiceClass"
address="${service.url}"
username="${service.username}"
password="${service.password}">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
</jaxws:client>
而且我把它变成这样:
<jaxws:client id="aService"
address="${service.url}"
username="${service.username}"
password="${service.password}">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
<jaxws:inInterceptors>
<bean class="com.blah.blah.SoapInInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="com.blah.blah.SoapOutInterceptor"/>
</jaxws:outInterceptors>
<jaxws:inFaultInterceptors>
<bean class="com.blah.blah.SoapFaultInterceptor"/>
</jaxws:inFaultInterceptors>
</jaxws:client>
我的SoapInInterceptor类:
public class SoapInInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SoapInInterceptor .class);
public SoapInInterceptor () {
super(Phase.RECEIVE);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
try {
InputStream is = message.getContent(InputStream.class);
CachedOutputStream os = new CachedOutputStream();
IOUtils.copy(is, os);
os.flush();
message.setContent(InputStream.class, os.getInputStream());
is.close();
LOGGER.debug("RESPONSE: {}", IOUtils.toString(os.getInputStream()));
os.close();
} catch (Exception ex) {
LOGGER.error("Error trying to log response", ex);
}
}
}
我的SoapOutInterceptor类:
public class SoapOutInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SoapOutInterceptor .class);
public SoapOutInterceptor () {
super(Phase.PRE_STREAM);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
CacheAndWriteOutputStream cwos = new CacheAndWriteOutputStream(message.getContent(OutputStream.class));
message.setContent(OutputStream.class, cwos);
cwos.registerCallback(new LoggingOutCallBack());
}
class LoggingOutCallBack implements CachedOutputStreamCallback {
@Override
public void onClose(CachedOutputStream cos) {
try {
if (cos != null) {
LOGGER.debug("REQUEST: {}", IOUtils.toString(cos.getInputStream()));
}
} catch (Exception e) {
LOGGER.error("Error trying to log request", e);
}
}
@Override
public void onFlush(CachedOutputStream arg0) {}
}
}
还有我的SoapFaultInterceptor类(几乎等于SoapInInterceptor):
public class SoapFaultInterceptor extends AbstractSoapInterceptor{
private static final Logger LOGGER = LoggerFactory.getLogger(SoapFaultInterceptor.class);
public SoapFaultInterceptor() {
super(Phase.RECEIVE);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
try {
InputStream is = message.getContent(InputStream.class);
CachedOutputStream os = new CachedOutputStream();
IOUtils.copy(is, os);
os.flush();
message.setContent(InputStream.class, os.getInputStream());
is.close();
LOGGER.debug("FAULT: {}", IOUtils.toString(os.getInputStream()));
os.close();
} catch (Exception ex) {
LOGGER.error("Error trying to log fault", ex);
}
}
}
在应用程序中定义了后者,我只需要像这样配置我的logback.xml文件:
<!—logger-->
<logger name="com.blah.blah">
<level value="DEBUG"/>
<appender-ref ref="aDailyRollingFileAppender"/>
</logger>
<!-- appender -->
<appender name="aDailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/jboss-as-7.2.0.Final/standalone/log/soap-payloads.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/jboss-as-7.2.0.Final/standalone/log/soap-payloads-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} [%file:%line] - %msg %n</pattern>
</encoder>
</appender>
将所有有效负载置于同一文件中。
如果要分开,请通过指定每个拦截器的完整类名称来创建不同的记录器/附加器。
对我来说,此解决方案非常适合我的需求。
希望这会有所帮助。
问候。