Question

I would like to log some information in case of fault. In particular I'd like to log the ip address and port of the client contacting the server, the username if the security in active, and, if possible, also the incoming message.

I added an interceptor in the getOutFaultInterceptors chain of the endpoint, but in the handleMessage I don't know which properties I can use.

Some ideas?

Thank you

Was it helpful?

Solution 2

I solved in this way

public FaultInterceptor() {
    super(Phase.MARSHAL);
}

public void handleMessage(SoapMessage message) throws Fault {
    Fault fault = (Fault) message.getContent(Exception.class);
    Message inMessage = message.getExchange().getInMessage();
    if (inMessage == null) return;

    String xmlMessage = null;
    InputStream is = inMessage.getContent(InputStream.class);
    String rawXml = null;
    if (is != null) {
        rawXml = is.toString();
    }

    String username = null;
    if (rawXml != null && rawXml.length() > 0) {
        try {
            XPath xpath = XPathFactory.newInstance().newXPath();
            XPathExpression xpathExpression;

            xpathExpression = xpath.compile("//*[local-name()=\'Envelope\']/*[local-name()=\'Header\']/*[local-name()=\'Security\']" +
                    "/*[local-name()=\'UsernameToken\']/*[local-name()=\'Username\']");

            InputSource source = new InputSource(new StringReader(rawXml));

            username = xpathExpression.evaluate(source);
        } catch (XPathExpressionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        xmlMessage = XMLUtils.prittyPrinter(is.toString());
    }

    String clientAddress = "<unknown>";
    int clientPort = -1;
    HttpServletRequest request = (HttpServletRequest)inMessage.get(AbstractHTTPDestination.HTTP_REQUEST); 
    if (null != request) {               
        clientAddress = request.getRemoteAddr(); 
        clientPort = request.getRemotePort();
    }

    logger.warn("User: " + username + " [" + clientAddress + ":" + clientPort + "] caused fault: " + fault +
            "\nMessage received: \n" + xmlMessage);



}

I found the "inMessage" property and on it I found the original message (and I can retrieve the username) and the "request" from which I retrieved the host and port.

Thank you.

OTHER TIPS

In your endpoint xml definition, you could add the following to log incoming messages:

<bean id="logInInterceptor" 
    class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<jaxws:inInterceptors>
    <ref bean="logInInterceptor"/>
</jaxws:inInterceptors>

and then use the bus to limit how many characters you want logged:

<cxf:bus>
    <cxf:features>
        <cxf:logging limit="102400"/>
    </cxf:features>
<cxf:bus>

You haven't mentioned what your method of authentication is, so ff you are using an implementation of UsernameTokenValidator, you could log the incoming username there.

To log details like the client's ip address and port, extend LoggingInInterceptor, then use the following code in handleMessage():

handleMessage() {
    HttpServletRequest request =
            (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
    if (null != request) {              
        String clientAddress = request.getRemoteAddr();
        int remotePort = request.getRemotePort();
        // log them
    }
}

Also have a look at this thread.

I think you should view the request input stream as consumed when handling the fault.

I suggest you always log the incoming message, and extract some kind of message correlation id - for example username. Keep it as a Message header.

For fault logging, use a fault interceptor which is limited to looking at the input request.

Tie regular + fault logging together with the message correlation id.

Log the full soap request, not just the payload. Soap requests might have headers in addition to the body.

See this question for regular logging, add in addition an output fault interceptor like so:

public class SoapFaultLoggingOutInterceptor extends AbstractPhaseInterceptor<Message> {

    private static final String LOCAL_NAME = "MessageID";

    private static final int PROPERTIES_SIZE = 128;

    private String name = "<interceptor name not set>";

    protected Logger logger = null;
    protected Level level;

    public SoapFaultLoggingOutInterceptor() {
        this(LogUtils.getLogger(SoapFaultLoggingOutInterceptor.class), Level.WARNING);
    }

    public SoapFaultLoggingOutInterceptor(Logger logger, Level reformatSuccessLevel) {
        super(Phase.MARSHAL);
        this.logger = logger;
        this.level = reformatSuccessLevel;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void handleMessage(Message message) throws Fault {
        if (!logger.isLoggable(level)) {
            return;
        }

        StringBuilder buffer = new StringBuilder(PROPERTIES_SIZE);

        // perform local logging - to the buffer 
        buffer.append(name);

        logProperties(buffer, message);

        logger.log(level, buffer.toString());
    }


    /**
     * Gets theMessageID header in the list of headers.
     *
     */
    protected String getIdHeader(Message message) {
        return getHeader(message, LOCAL_NAME);
    }

    protected String getHeader(Message message, String name) {
        List<Header> headers = (List<Header>) message.get(Header.HEADER_LIST);

        if(headers != null) {
            for(Header header:headers) {
                if(header.getName().getLocalPart().equalsIgnoreCase(name)) {
                    return header.getObject().toString();
                }
            }
        }
        return null;
    }        

    protected void logProperties(StringBuilder buffer, Message message) {
        final String messageId = getIdHeader(message);
        if(messageId != null) {
            buffer.append(" MessageId=");
            buffer.append(messageId);
        }

        Message inMessage = message.getExchange().getInMessage();

        HttpServletRequest request = (HttpServletRequest)inMessage.get(AbstractHTTPDestination.HTTP_REQUEST); 

        buffer.append(" RemoteAddr=");
        buffer.append(request.getRemoteAddr());
    }

    public Logger getLogger() {
        return logger;
    }

    public String getName() {
        return name;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }


}    

where MessageID is the correlation / breadcrumb id.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top