I wrote a custom CXF interceptor to log all the SOAP request and responses into the database and it seems to be working fine with positive test cases and server errors.
To capture faults, you need to register the interceptor as a fault interceptor. For example
<cxf:outFaultInterceptors>
<bean class="DbLogOutInterceptor" />
</cxf:outFaultInterceptors>
See the CXF Configuration page under "Enabling message logging using custom CXF bean elements" for an example using the CXF logging interceptors to capture in/out messages and in/out faults.
So in my custom interceptor I write the following code:
Fault fault = new Fault(message.getContent(Exception.class));
Now this is in some legacy code that was throwing exceptions from Java and letting the framework convert it to a fault. I won't get into my feelings on that, but this will get you the fault that is generated.
Now if you are throwing a fault from your service method, do
Fault fault = message.getContect(Fault.class);
Hopefully this will help you get the answer to what you want. Make sure you register the interceptor like below
<jaxws:endpoint
id="fooService" implementor="com.bar.FooService" address="/FooServices">
<jaxws:outFaultInterceptors>
<ref class="com.bar.interceptor.DbLogOutInterceptor"/>
</jaxws:outFaultInterceptors>
</jaxws:endpoint>
<jaxws:endpoint
The Best way is to implement the Fault listener and use org.slf4j.Logger to log the error message instead of using java logging.
It would be wise to override the LoggingInInterceptor and write ur custome interceptor to get the request payload.
public class CxfInputFaultInterceptor extends AbstractLoggingInterceptor {
private static final Logger LOG = LogUtils.getLogger(CxfInputFaultInterceptor.class);
public CxfInputFaultInterceptor() {
super(Phase.RECEIVE);
}
Step 1: cxf-beans.xml
<cxf:bus>
<cxf:inInterceptors>
<ref bean="cxfInputFaultInterceptor"/>
</cxf:inInterceptors>
<cxf:inFaultInterceptors>
<ref bean="cxfInputFaultInterceptor"/>
</cxf:inFaultInterceptors>
<cxf:properties>
<entry key="org.apache.cxf.logging.FaultListener">
<bean id="cxfFaultListener" class="pkg.common.ws.interceptor.CxfFaultListenerImpl" >
<property name="loggedUser" ref="loggedUser"/>
</bean>
</entry>
</cxf:properties>
</cxf:bus>
Step 2 : your listener which should implements org.apache.cxf.logging.FaultListener
import java.io.InputStream;
import org.apache.cxf.interceptor.LoggingMessage;
import org.apache.cxf.logging.FaultListener;
import org.apache.cxf.message.Message;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.service.model.InterfaceInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Listener to faults on the processing of messages by CXF intercepter chain. Here we override
* java.util.logging.Logger and use org.slf4j.Logger to print error to console.
*/
public class CxfFaultListenerImpl implements FaultListener{
private static final Logger logger = LoggerFactory.getLogger(CxfFaultListenerImpl.class);
private static final String NEWLINE="\n";
public boolean faultOccurred(final Exception exception,final String description,final Message message) {
createErrorLog(message);
logger.error(" --------------------------------------------------------------------------------------------------");
logger.error(" Stack Trace : ");
logger.error(" --------------------------------------------------------------------------------------------------");
logger.error(NEWLINE,exception);
logger.error(" --------------------------------------------------------------------------------------------------");
return true;
}
private void createErrorLog(final Message message){
final Message inMessage = message.getExchange().getInMessage();
final InputStream is = inMessage.getContent(InputStream.class);
final EndpointInfo endpoint = message.getExchange().getEndpoint().getEndpointInfo();
String logName=null;
if(endpoint!=null && endpoint.getService()!=null){
final String serviceName = endpoint.getService().getName().getLocalPart();
final InterfaceInfo iface = endpoint.getService().getInterface();
final String portName = endpoint.getName().getLocalPart();
final String portTypeName = iface.getName().getLocalPart();
logName = serviceName + "." + portName + "." + portTypeName;
}
final LoggingMessage buffer
= new LoggingMessage("Error occured on Service Call : "+NEWLINE +"----------------------------", logName);
final Integer responseCode = (Integer)message.get(Message.RESPONSE_CODE);
if (responseCode != null) {
buffer.getResponseCode().append(responseCode);
}
final String encoding = (String)message.get(Message.ENCODING);
if (encoding != null) {
buffer.getEncoding().append(encoding);
}
final String httpMethod = (String)message.get(Message.HTTP_REQUEST_METHOD);
if (httpMethod != null) {
buffer.getHttpMethod().append(httpMethod);
}
final String ct = (String)message.get(Message.CONTENT_TYPE);
if (ct != null) {
buffer.getContentType().append(ct);
}
final Object headers = message.get(Message.PROTOCOL_HEADERS);
if (headers != null) {
buffer.getHeader().append(headers);
}
final String uri = (String)message.get(Message.REQUEST_URL);
if (uri != null) {
buffer.getAddress().append(uri);
final String query = (String)message.get(Message.QUERY_STRING);
if (query != null) {
buffer.getAddress().append("?").append(query);
}
}
final String requestXml= is.toString();
if(requestXml !=null){
buffer.getMessage().append("LoggedIn User: ");
buffer.getMessage().append(getCurrentUsername()+NEWLINE);
buffer.getMessage().append("Request payload : "+NEWLINE);
buffer.getMessage().append(requestXml);
}else{
buffer.getMessage().append("LoggedIn User: ");
buffer.getMessage().append(getCurrentUsername()+NEWLINE);
buffer.getMessage().append("No inbound request message to append.");
}
logger.error(buffer.toString());
}
}
Hope that helps someone who just wants the stack trace and payload only when an error occurs on the service call and thus avoid huge log files.