简体   繁体   中英

Spring Integration AOP for Logging outbound Http requests

I was looking at a post from 2014 about using Spring AOP for logging HTTP requests/replies:

Spring integration + logging response time for http adapters(or any endpoint)

To this end, I tried this AOP configuration:

<aop:config >
    <aop:aspect id="myAspect" ref="inboundOutboundHttpLogging">
        <aop:pointcut id="handleRequestMessageMethod"
                      expression="execution(* org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleRequestMessage(*))
                                  and
                                  args(message))" />
        <aop:before method="requestMessageSent" pointcut-ref="handleRequestMessageMethod" arg-names="message"/>
    </aop:aspect>
</aop:config>

Is there perhaps a newer way of using AOP for logging HTTP requests? I want to avoid having to put per-request logging (ie outbound-gateway advice on each gateway).

Thanks for any pointers.

The handleRequestMessage() is essentially an input message to this gateway and output. So, if you don't like implementing an AbstractRequestHandlerAdvice and adding it into each your gateway via their <request-handler-advice-chain> , then consider to use a <wire-tap> for input and output channels of those gateway.

You may implement, though, a BeanPostProcessor.postProcessBeforeInitialization() to add your custom AbstractRequestHandlerAdvice into those HTTP gateways you are interested in.

My point is that <aop:aspect> you are presenting us really might lead to some unexpected behavior, like that final method concern you have edit out from your question...

Based upon the suggestions made by @artem-bilan, I was able to find a solution similar to AOP for injecting logging AbstractRequestHandlerAdvice into HTTP outbound request processing. I'm contributing this as a way of showing a possible solution for anyone else who comes across this question.

As @artem-bilan mentions, there is a mechanism for injecting AbstractRequestHandlerAdvice into a AbstractReplyProducingMessageHandler such as an HttpRequestExecutingMessageHandler . In my case, I'm wanting to log the message contents (header and payload) prior to the HTTP call and also log the return message (header and payload). This works nicely.

@artem-bilan suggests that the BeanPostProcessor mechanism can allow to inject the advice without having to add that declaration to each http outbound bean. The BeanPostProcessor looks like this:

public class AddHttpOutboundAdvicePostProcessor implements BeanPostProcessor {
    final List<Advice> adviceList;

    final AddHttpOutboundAdvicePostProcessor(List<Advice> adviceList) {
        this.adviceList = adviceList;
    }

    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, 
                                                 @NonNull String beanName) 
                                                             throws BeansException {
        if (bean instanceof AbstractHttpRequestExecutingMessageHandler) {
            ((AbstractHttpRequestExecutingMessageHandler) bean).setAdviceChain(adviceList);
        }
        return bean;
    }
}

We need to set up this bean into our context. (I'm a die-hard declarative fan hence this is in XML.)

<bean id    = "addHttpLoggingPostProcessor"
      class = "com.my.package.AddHttpOutboundAdvicePostProcessor" >
    <constructor-arg name="adviceList>
        <util:list>
            <ref bean="outboundLogger" />
        </util:list>
    </constructor-arg>
</bean>

Here, the outboundLogger is a bean that managers the request-handler-advice . In my choice of implementation, I'm sending a copy of the outbound message to a channel for logging beforehand, and a copy of the response message down another channel for logging the response. The XML declaration of the bean takes the two channel names as constructors:

<bean id="outboundLogger" class="com.my.package.HttpRequestProcessorLogger" >
    <constructor-arg name="requestLoggingChannelName"  value="XXX" />
    <constructor-arg name="responseLoggingChannelName" value="YYY" />
</bean>

where XXX and YYY are the names of channels to the components that perform the logging. I've set these channels to be ExecutorChannel s so that the logging is performed asynchronously.

The HttpRequestProcessorLogger bean manages the call to handleRequestMessage() :

public class HttpRequestProcessorLogger extends AbstractRequestHandlerAdvice {
    private MessageChannel requestLoggingChannel;
    private MessageChannel responseLoggingChannel;

    private String requestLoggingChannelName;
    private String responseLoggingChannelName;

    private BeanFactory beanFactory;

    public HttpRequestProcessorLogger(String requestLoggingChannelName, String responseLoggingChannelName) {
        this.requestLoggingChannelName  = requestLoggingChannelName;
        this.responseLoggingChannelName = responseLoggingChannelName;
    }

    @Override
    protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
        getChannels();
        requestLoggingChannel.send(message);
        final Object result = callback.execute();
        final message<?> outputMessage = 
            (MessageBuilder.class.isInstance(result) ? ((MessageBuilder<?>) result).build()
                                                     : (Message<?>) result;
        responseLoggingChannel.send(outputMessage);
        return outputMessage;
    }

    private synchronized void getChannels() {
        if (requestLoggingChannelName != null) {
            final DestinationResolver<MessageChannel> 
                channelResolver = ChannelResolverUtils.getChannelResolver(this.beanFactory);
            requestLoggingChannel  = channelResolver.resolverDestination(requestLoggingChannelName);
            responseLoggingChannel = channelResolver.resolverDestination(responseLoggingChannelName);
            requestLoggingChannelName  = null;
            responseLoggingChannelName = null;
        } 
    }

    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeanException {
        this.beanFactory = beanFactory;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM