Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Minor: match heading style and case

 

 

Wiki Markup
{span:style=font-size:2em;font-weight:bold} Debugging and Logging {span}

 

 

Table of Contents

Logging Messages

...

For programmatic configuration, the logging interceptors can be added to your service endpoint as follows:

Code Block

import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;

Object implementor = new GreeterImpl();
EndpointImpl ep = (EndpointImpl) Endpoint.publish("http://localhost/service", implementor);

ep.getServer().getEndpoint().getInInterceptors().add(new LoggingInInterceptor());
ep.getServer().getEndpoint().getOutInterceptors().add(new LoggingOutInterceptor());

For web services running on CXFServlet, the below annotations can be used on either the SEI or the SEI implementation class. If placed on the SEI, they activate logging both for client and server; if on the SEI implementation class, they are relevant just for server-side logging.

Code Block

import org.apache.cxf.feature.Features;

@javax.jws.WebService(portName = "MyWebServicePort", serviceName = "MyWebService", ...)
@Features(features = "org.apache.cxf.feature.LoggingFeature")        
public class MyWebServicePortTypeImpl implements MyWebServicePortType {

or (equivalent)

Code Block

import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.interceptor.OutInterceptors;

@javax.jws.WebService(portName = "WebServicePort", serviceName = "WebServiceService", ...)
@InInterceptors(interceptors = "org.apache.cxf.interceptor.LoggingInInterceptor")
@OutInterceptors(interceptors = "org.apache.cxf.interceptor.LoggingOutInterceptor")
public class WebServicePortTypeImpl implements WebServicePortType {

For programmatic client-side logging, the following code snippet can be used as an example:

Code Block

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;

public class WSClient {
    public static void main (String[] args) {
        MyService ws = new MyService();
        MyPortType port = ws.getPort();
        
        Client client = ClientProxy.getClient(port);
        client.getInInterceptors().add(new LoggingInInterceptor());
        client.getOutInterceptors().add(new LoggingOutInterceptor()); 
        
        // make WS calls...

...

Configuring logging levels

...

In the /etc folder of the CXF distribution there is a sample Java SE logging.properties file you can use to configure logging. For example, if you want to change the console logging level from WARNING to FINE, you need to update two properties in this logging.properties file as below:

Code Block
xml
xml

.level= FINE
java.util.logging.ConsoleHandler.level = FINE

Once this is done, you will need to set the -Djava.util.logging.config.file property to the location of the logging.properties file. As an example, the Ant target below has this property set:

Code Block
xml
xml

<target name="runClient">
   <java classname="client.WSClient" fork="true">	    	
      <classpath>
         <pathelement location="${build.classes.dir}"/>
         <fileset dir="${env.CXF_HOME}/lib">
            <include name="*.jar"/>
         </fileset>
      </classpath>
      <jvmarg value="-Djava.util.logging.config.file=/usr/myclientapp/logging.properties"/>
   </java>
</target>

...

As noted above, CXF uses the java.util.logging package ("Java SE Logging") by default. But it is possible to switch CXF to instead use Log4J. This is achieved through the use of configuration files. There are two options to bootstrapping CXF logging and each is listed below:

  • Add the following system property to the classpath from which CXF is initialized:
Code Block

-Dorg.apache.cxf.Logger=org.apache.cxf.common.logging.Log4jLogger
  • Add the file META-INF/cxf/org.apache.cxf.Logger to the classpath and make sure it contains the following content:
Code Block

org.apache.cxf.common.logging.Log4jLogger

...

  • Add the following system property to the classpath from which CXF is initialized:
Code Block

-Dorg.apache.cxf.Logger=org.apache.cxf.common.logging.Slf4jLogger
  • Add the file META-INF/cxf/org.apache.cxf.Logger to the classpath and make sure it contains the following content:
Code Block

org.apache.cxf.common.logging.Slf4jLogger

...

CXF supports the ability to put server stack trace information into the fault message fault details, if you enable the option of 'faultStackTraceEnabled'. It is useful for debugging if the soap fault message is not defined in the WSDL operation.

Code Block

<jaxws:endpoint id="server" address="http://localhost:9002/TestMessage" 
   wsdlURL="ship.wsdl"
   endpointName="s:TestSoapEndpoint"
   serviceName="s:TestService"
   xmlns:s="http://test" >
   <jaxws:properties>		
      <entry key="faultStackTraceEnabled" value="true" />
   </jaxws:properties>
</jaxws:endpoint>

...

CXF doesn't show the cause exception message in the fault message due to security consideration. However, this could potentially cause some trouble on the client side, as the client will not be able to see what the real cause of the exception is. You can let the CXF server return the fault message with the embedded cause exception message by enabling the option of 'exceptionMessageCauseEnabled' like this:

Code Block

<jaxws:endpoint id="server" address="http://localhost:9002/TestMessage" 
   wsdlURL="ship.wsdl"
   endpointName="s:TestSoapEndpoint"
   serviceName="s:TestService"
   xmlns:s="http://test" >
   <jaxws:properties>	
      <entry key="exceptionMessageCauseEnabled" value="true" />		
   </jaxws:properties>
</jaxws:endpoint>

...

In simplest case pushing ATOM Feeds can be declared this way:

Code Block
xml
xml

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="level" value="ALL" />
   </bean>

...

More complex example shows how to specify non-root logger and define batch size:

Code Block
xml
xml

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="logger" value="org.apache.cxf.jaxrs" />
       <property name="level" value="INFO" />
       <property name="batchSize" value="10" />
   </bean>

To push to client events generated by different loggers on different levels, "loggers" property must be used instead of pair "logger" and "level":

Code Block
xml
xml

   <bean class="org.apache.cxf.jaxrs.management.web.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="loggers" value="
           org.apache.cxf:DEBUG,
           org.apache.cxf.jaxrs,
           org.apache.cxf.bus:ERROR,
           myNamedLogger:INFO" />
   </bean>

...

In all above cases, when first delivery fails engine of ATOM push handler is shutdown and no events will be processed and pushed until configuration reload. To avoid this frequent case, retrial can be enabled:

Code Block
xml
xml

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="logger" value="org.apache.cxf.jaxrs" />
       <property name="level" value="INFO" />
       <property name="retryPause" value="linear" />
       <property name="retryPauseTime" value="60" />
       <property name="retryTimeout" value="300" />
   </bean>

...

Ultimate control is given by "converter" and "deliverer" properties. Either beans of predefined or custom classes can be used (see "Programming syle" chapter for more details). Example below shows custom class using different transport protocol than default:

Code Block
xml
xml

   <bean id="soapDeliverer" ...
   ...
   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="deliverer">
           <ref bean="soapDeliverer"/>
       </property>
       <property name="loggers" ... />
   </bean>

...

When ATOM feeds must be delivered to more than one endpoint and additionally each endpoint is fed by different loggers simply use multiple ATOM push beans in Spring config:

Code Block
xml
xml

   <bean id="atom1" class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://someplace.com/foo/bar"/>
       ...
   </bean>
   <bean id="atom2" class="org.apache.cxf.jaxrs.management.web.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://otherplace.com/baz/blah"/>
       ...
   </bean>
   ....

...

  • Properties specify classes of custom deliverers and converters, instead of instances.
  • Custom deliverer must have public constructor with the only String parameters; created instance will have passed URL of client.
  • Multiple client endpoints is not supported out of the box (cannot instantiate multiple handlers as in Spring)

Example:

Code Block

 handlers = org.apache.cxf.management.web.logging.atom.AtomPushHandler, java.util.logging.ConsoleHandler
 .level = INFO
 ...
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.url = http://localhost:9080
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.batchSize = 10
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.deliverer = WebClientDeliverer 
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.converter = foo.bar.MyConverter
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.retry.pause = linear
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.retry.pause.time = 10
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.retry.timeout = 360
 ...

...

Following example:

Code Block

    Deliverer d = new WebClientDeliverer("http://somewhere.com/foo/bar");
    d = new RetryingDeliverer(d, 300, 60, true);
    Converter c = new SingleEntryContentConverter();
    AtomPushHandler h = new AtomPushHandler(1, c, d);    
    Logger l = Logger.getLogger("org.apache.cxf.jaxrs");
    l.setLevel(Level.INFO);
    l.addHandler(h);

is equivalent to Spring configuration:

Code Block

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="logger" value="org.apache.cxf.jaxrs" />
       <property name="level" value="INFO" />
       <property name="retryPause" value="linear" />
       <property name="retryPauseTime" value="60" />
       <property name="retryTimeout" value="300" />
   </bean>

...

When configuring AtomPullServer endpoints, one can set the 'loggers' property the same way as it is done for AtomPushBeans, for example :

Code Block
xml
xml

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPullServer" init-method="init">
       <property name="loggers" value="
           org.apache.cxf:DEBUG,
           org.apache.cxf.jaxrs,
           org.apache.cxf.bus:ERROR,
           myNamedLogger:INFO" />
       <property name="pageSize" value="30"/>
   </bean>

...

One can have a ReadWriteLogStorage bean injected into AtomPushBean if the log records have to be periodically offloaded from memory and persisted across restarts :

Code Block
xml
xml

   <bean id="storage" class="org.apache.cxf.systest.jaxrs.JAXRSLoggingAtomPullSpringTest$Storage"/>

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPullServer" init-method="init">
       <property name="loggers" value="org.apache.cxf.jaxrs" />
       <property name="maxInMemorySize" value="400"/>
       <property name="storage">
           <ref bean="storage"/>
       </property>
   </bean>

...

Once AtomPullServer has been configured, it has to be registered as a service bean with the jaxrs endpoint so that Atom aware clients (blog readers, etc) can start polling it :

Code Block
xml
xml

<jaxrs:server id="atomServer" address="/atom">
 <jaxrs:serviceBeans>
   <ref bean="atomPullServer"/>
 </jaxrs:serviceBeans>

 <jaxrs:providers>
  <ref bean="feed"/>
  <ref bean="entry"/>
 </jaxrs:providers>
</jaxrs:server>

<bean id="feed" class="org.apache.cxf.jaxrs.provider.AtomFeedProvider"/>
<bean id="entry" class="org.apache.cxf.jaxrs.provider.AtomEntryProvider"/>

...

If you would like your users to find about the Atom feeds which are collecting log events from your endpoints then AtomPullServers will need to have a CXF bus injected, as well as be told about the address of the corresponding atom feed endpoint and of the jaxrs or jaxws endpoint :

Code Block
java
java

<bean id="atomPullServer" class="org.apache.cxf.management.web.logging.atom.AtomPullServer" init-method="init">
   <property name="loggers" value="org.apache.cxf.systest.jaxrs.JAXRSLoggingAtomPullSpringTest$Resource:ALL"/>
   <property name="bus">
      <ref bean="cxf"/>
   </property>
   <!-- this is a jaxrs:server/@adrress or jaxws:endpoint/@address of the endpoint generating the log events -->
   <property name="endpointAddress" value="/resource"/>
   <!-- this is a jaxrs:server/@address of the endpoint for which this atomPullServer bean is registered as a service bean -->
   <property name="serverAddress" value="/atom"/>
</bean>