使用Apache CXF作为XML记录请求/响应

问题描述 投票:24回答:6

是否有可能使用CXF将请求/响应记录为XML,最好是记录到一个单独的文件中,以便我可以监视应用程序在做什么?

xml cxf webservice-client
6个回答
17
投票

将以下内容添加到您的端点和客户端:

<jaxws:features>
    <bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>

这会将所有内容记录到服务器日志中。

如果要在其他地方记录它们,请查看内置CXF LoggingInInterceptor和LoggingOutInterceptor的源代码。您可以按照他们使用的模式来抓取进/出消息,并随心所欲地对其进行处理。

使用类似这样的东西将自己的拦截器添加到链中:

<jaxws:inInterceptors>
    <ref bean="myLoggingInInterceptor" />
</jaxws:inInterceptors>

28
投票

所以,我尝试了更多。要获取记录的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());

21
投票

请求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


7
投票

[如果您将Spring与Java配置一起使用,则有2种简单的方法可以使用Apache CXF激活SOAP消息的记录:

  1. 直接在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; }

  2. 分别在每个暴露的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中。


2
投票

将自己的记录器添加到端点属性要容易得多。在这种情况下,默认日志记录拦截器将在端点属性中查找您的日志记录器,如果找到它,它将使用它,否则它将创建默认日志记录器。这是我的用法示例:

<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>

0
投票

就我而言,我必须维护一个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>

将所有有效负载置于同一文件中。

如果要分开,请通过指定每个拦截器的完整类名称来创建不同的记录器/附加器。

对我来说,此解决方案非常适合我的需求。

希望这会有所帮助。

问候。

© www.soinside.com 2019 - 2024. All rights reserved.