oldDave
oldDave

Reputation: 405

CXF 3.1 wsdl2java logging with Log4j2

I have generated my service using CXF 3.1 wsdl2java, so no cxf.xml.

I am trying to add the request and response XML for the service to my log4j2 setup. There is a lot of conflicting information due to version changes etc. My request/response xml CXF logging file is always empty.

This was the closest to my setup: How to log CXF webservice requests with log4j2?

So I have the following in my log4j2.xml

    <logger name="org.apache.cxf" additivity="false" level="info">
            <AppenderRef ref="APP_LOG_FILE"/>
    </logger>
    <logger name="org.apache.cxf.interceptor.LoggingInInterceptor" additivity="false" level="info">
        <AppenderRef ref="WS_LOG_FILE" />
    </logger>
    <logger name="org.apache.cxf.interceptor.LoggingOutInterceptor" additivity="false" level="info">
        <AppenderRef ref="WS_LOG_FILE" />
    </logger>

Not showing the appenders, but they are there, the APP_LOG_FILE is populated but the WS_LOG_FILE appender files is always empty, while messages being exchanged.

I added a META-INF/cxf/org.apache.cxf.Logger file with this content

org.apache.cxf.common.logging.Slf4jLogger

I also tried using the additional command line parameter when running my jar as an alternative to the cxf.Logger file:

-Dorg.apache.cxf.Logger=org.apache.cxf.common.logging.Slf4jLogger

I added the additional dependency:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.0</version>
</dependency>

I tried the following both with the logging interceptors (are they required or an alternative to the config in the log4j2.xml?) and without them, as shown currently commented out.

    GetComparisonDecisionService equinitiComparisonService = new GetComparisonDecisionService();
    GetComparisonDecisionInterface comparison = equinitiComparisonService.getGetComparisonDecision();
/*
    Client client = ClientProxy.getClient(equinitiComparisonService.getPort(GetComparisonDecisionInterface.class));
    client.getInInterceptors().add(new LoggingInInterceptor());
    client.getOutInterceptors().add(new LoggingOutInterceptor());
*/
    GetComparisonDecisionCallParameter comparisonDecisionCallParameter = new GetComparisonDecisionCallParameter();
    comparisonDecisionCallParameter.setSecurityToken(token);
    comparisonDecisionCallParameter.setComparisonApplication(equinitiApp);

    GetComparisonDecisionResponseParameter eqDescn = comparison.getComparisonDecision(comparisonDecisionCallParameter);

I have run out of ideas at this point. The cxf documentation is very terse.

Upvotes: 5

Views: 2327

Answers (3)

Tom
Tom

Reputation: 114

Here is an alternative way around it, which I came up with since I was unable to make the accepted solution work. The idea is to define a handler for your service, and delegate to that handler the responsibility of logging the SOAP envelopes. We are kind of reinventing the wheel, since this feature apparently already exists and just needs to be activated, but if you're unable to make it work you might want to try this.

Define a custom class implementing the SOAPHandler interface

public class LoggingMessageHandler implements SOAPHandler<SOAPMessageContext> {

    // this is a  static instance of Logback's LoggerFactory.getLogger
    // exposed by the my.personal.package.logging.soap.SOAPMessageLogger class
    // all SOAP envelopes are logged by this logger
    private Logger logger = SOAPMessageLogger.getLogger();

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        final StringWriter sw = new StringWriter();
        try {
            TransformerFactory.newInstance().newTransformer().transform(
                    new DOMSource(context.getMessage().getSOAPPart()),
                    new StreamResult(sw));

            // I'm formatting the log output so that every request or response only takes a single row in the log file
            // this way the file is easier to go through, weighs less, and SOAP messages are easier to copy-paste
            String SOAPMessageString = sw.toString().replaceAll("[\\n\\t ]", "");
            if((Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) {
                logger.info("{} REQUEST:\n{}", new Object[]{(context.get(MessageContext.WSDL_OPERATION)), SOAPMessageString});
            }
            else {
                logger.info("{} RESPONSE:\n{}", new Object[]{(context.get(MessageContext.WSDL_OPERATION)), SOAPMessageString});
            }
        } catch (Exception e) {
            logger.warn("Could not log SOAP envelope: "  + e.getMessage());
        }
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        logger.warn("handleFault handler was fired, will attempt to retrieve and log message details...");
        final StringWriter sw = new StringWriter();
        try {
            TransformerFactory.newInstance().newTransformer().transform(
                    new DOMSource(context.getMessage().getSOAPPart()),
                    new StreamResult(sw));
            logger.warn(sw.toString());
        } catch (Exception e) {
            logger.warn("Could not log SOAP envelope: "  + e.getMessage());
        }
        return true;
    }

    @Override
    public void close(MessageContext context) {
        logger.trace("Closing!");
    }

    @Override
    public Set<QName> getHeaders() {
        return null;
    }
}

Define a custom class implementing the HandlerResolver interface

public class LoggingMessageHandlerResolver implements HandlerResolver {
    @Override
    public List<Handler> getHandlerChain(PortInfo portInfo) {
        return Collections.singletonList(new LoggingMessageHandler());
    }
}

Finally, when you instantiate the proxy to your remote SOAP web service, set your custom LoggingHandlerResolver as its resolver:

public MyRemoteServicePort getServicePort() {
    MyRemoteService myRemoteService = new MyRemoteService(new URL(webServicesBaseURL+myRemoteWsdlPath));
    myRemoteService.setHandlerResolver(new LoggingMessageHandlerResolver());
    return myRemoteService.getMyRemotePort();
}

This way every time a message is sent/received through the MyRemoteServicePort it is intercepted by the handleMessage method, which will log it. You can of course configure Logback (or whatever logging mechanism you want to use) in order to customize that log output to your likings.

Upvotes: 1

Frank
Frank

Reputation: 2066

As requested as an answer: Setting the LoggingInterceptors the way you show in the last code-fragment is recommended for CXF < 3.1. You should use the @Feature-Annotation like this: @Features(features = "org.apache.cxf.feature.LoggingFeature").

Upvotes: 2

oldDave
oldDave

Reputation: 405

See Frank's comment, it fixed the problem.

I note that the obvious place to look for information on logging on the CXF site - http://cxf.apache.org/docs/debugging-and-logging.html does not mention this method!?

Upvotes: 0

Related Questions