Logging request/response with Apache CXF as XML

时光怂恿深爱的人放手 提交于 2019-12-28 05:14:23

问题


Is it possible to log the request/response as XML using CXF, ideally to a separate file so I can monitor what an application is doing?


回答1:


Add the following to your endpoints and clients:

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

This will log everything to the server log.

If you want to log them elsewhere, then look at the source code of the built-in CXF LoggingInInterceptor and LoggingOutInterceptor. You can follow the pattern they use to grab the messages on their way in/out and do with them what you like.

Add your own interceptors to the chain with something like this:

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



回答2:


So, I tried a little more with this. To get the XML Request and Replies logged, and if you are using Log4J, you need to set the Log-level of CXF in the log4j.xml file like this (>= INFO):

<logger name="org.apache.cxf" >
    <level value="INFO" />
</logger>

And the cxf.xml file should contains this:

<cxf:bus>
    <cxf:features>
        <cxf:logging/>
    </cxf:features>
</cxf:bus> 

Both files should be in the CLASSPATH.

To display the soap message add this to your code:

Client client = ClientProxy.getClient(service);
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());



回答3:


The request soap xml can be logged easily by an custom In interceptor. Say, we have an interceptor named "wsLoggingInInterceptor", So in the context file it will be like the following :

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

In the class we can get the request xml as follows:

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 ( );
        }

    }

}

Look, here I have also log the address from where the request is coming. You can also get some more information from the "HttpServletRequest" object. you can have more from : http://cxf.apache.org/docs/interceptors.html

To log response xml you can have a look at this thread




回答4:


If you are using Spring with it´s Java-Configuration, there are 2 easy ways to activate Logging of SOAP-Messages with Apache CXF:

  1. Directly on the SpringBus - that is usefull, if you want to log the Messages of all your CXF-Endpoints:

    @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. Activate Logging seperately on every exposed CXF-Endpoint

    @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; }

Remind the LoggingFeature.setPrettyLogging(true); Method to see pretty printed SOAP-Messages and LoggingFeature.initialize(springBus()); - without the latter, the magic doesn´t happen. For cleaner Code, you could also separate the LoggingFeature as separate Bean and inject it either in your SpringBus or Endpoint-Bean.




回答5:


It's much more easier to add your own logger to the endpoint properties. In this case default logging interceptor will look for your logger in the endpoint properties and if it finds one it will use it otherwise it will create default. Here is my usage example:

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



回答6:


In my case, I had to maintain a jaxb generated client. So I had found with the following situation in the 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>

And I jus turned it into this:

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

My SoapInInterceptor class:

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);
    }
  }
}

My SoapOutInterceptor class:

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) {}
  }
}

And my SoapFaultInterceptor class (almost equals to 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);
    }
  }
}

With the latter defined in the application, I only had to configure my logback.xml file like this:

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

To make all the payloads be in the same file.

If you want to separate it, create different loggers/appenders by specifiying the complete class name of each Interceptor.

For me, this solution fits perfectly for what I wanted.

Hope this helps.

Regards.



来源:https://stackoverflow.com/questions/4592422/logging-request-response-with-apache-cxf-as-xml

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!