Add custom attributes to logging-channel-adapter Spring Integration - java

I have the following configuration:
<int:channel id="responseXmlChannel">
<int:interceptors>
<int:wire-tap channel="responseXmlLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="responseXmlLogger"
logger-name="test.SyncResponseLogger" level="DEBUG"
expression="'message received, headers:' + headers + ' payload:' + payload"/>
I am trying to add some session attributes to be logged. Current user for example.
I was taking a look at the header-enricher approach, but I could not find a good example of adding session attributes to the header using it.
Is there a way to do what I am trying to do? Is there other approach besides using the headers to add custom attributes to be logged?
My goal is to have in the logs something like
current user: $UserName. Payload: $Payload

OK. So, what is your question then? How to use header-enricher? I'm not sure what is your "session" object to be precise with the answer, but header-enricher can be configured to call any arbitrary method on beans. See its docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#header-enricher.
The other approach is too deal with ThreadLocal. That's exactly what the RequestContextHolder in Spring Web does for RequestAttributes with its HTTP session access. Although you need to keep in mind that moving to a different thread with queue or executor channels in between will lose the current thread context and you won't have access to that ThreadLocal. So, headers in the particular message is not so bad choice to do.

Related

Add trace.id and transaction.id Springboot

I have a Springboot micro-service. For logging I'm using Elastic common scheme, implemented using ecs-logging-java.
I want to set the trace.ID and a transaction.ID but I'm not sure how?
Bonus question, I'm I right in thinking trace.ID should be the ID to following the request through multiple system. transaction.ID is just for within the service?
Configure your logging patter as below
<pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} %thread [%X{trace-id}] [%-5level] %class{0} - %msg%n </pattern>
Put trace Id in MDC. (MDC belongs to particular thread context)
`MDC.put("trace-id", "traceid1");`
So whenever your log will print a message, it will print trace id.
Follow below artical.http://logback.qos.ch/manual/mdc.html
Step 1: Add trace id in the thread context.
This can be done using MDC (manages contextual information on a per-thread basis).
Add the below line at the start of any method, from where you want to trace logs.
MDC.put("TRACE_ID", UUID.randomUUID().toString());
Step 2: Add trace id in log format
Logs in java do not add trace id by default, so to make this possible we can add the trace id we previously added in the thread context to the log.
This can be added to the application.properties I have added [%X{TRACE_ID}] in the default log console pattern.
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} [%X{TRACE_ID}] %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
I thought I had documented this but the closest I could come is in Log4j-Audit's RequestContext.. I guess I need to add a new entry to my blog. The short answer to this is that you use Log4j 2's ThreadContextMap. First, when a user logs in create a session map that contains the data you want to capture in each request, such as the user's ip address and loginId. Then create servlet Filter or Spring Interceptor to add that data as well as a unique request id to Log4j 2's Thread Context Map.
All Leg Events will include the data in the ThreadContext. The ECSLayout automatically includes all the fields in the ThreadContextMap.
Lastly, you need to propagate the RequestContext to downstream services. You do that by creating a Spring Interceptor that gets wired into the RestTemplate which converts the RequestContext fields into HTTP headers. The downstream service then has a Filter or Spring Interceptor that converts the headers back into RequestContext attributes. Log4j Audit (referenced above) has examples and implementations of all these components.
I should add that the method described above does not implement tracing as described by the WSC Trace Context spec so it is also not compatible with Elasticsearch's distributed tracing support. It is worth noting however, that if one were to include Elasticsearch's distributed tracing support along with New Relic's distributed tracing support they would step on each other.

Spring Integration HTTP Inbound Gateway Request Overlap

I have an HTTP Inbound Gateway in my Integration Application, which I will call during some save operation. It's like this. If I have one product, I will call the API once, and if I have more than once, then I will call multiple times. The problem is, for single invoke, SI works just fine. But for multiple calls, request and response get messed up. I thought Spring Integration Channels are just like MQ's, but it is not?
Let me explain this. Let's say I have 2 products. First, I invoke SI for Product A and then for B. Response of A got mapped to request B! It happens all the time. I don't want to use some dirty hacks like wait for the first response to come and invoke again. This means the system has to wait for a long time. I guess we can do it in Spring Integration using task executor, but with all the basic samples out there, I can't find the right one. So please help me find out how can I fix this issue!
My Configuration is :
<int:channel id="n2iMotorCNInvokeRequest" />
<int:channel id="n2iMotorCNInvokeResponse" />
<int:channel id="n2iInvoketransformerOut" />
<int:channel id="n2iInvokeobjTransformerOut" />
<int:channel id="n2iInvokegatewayOut" />
<int-http:inbound-gateway id="i2nInvokeFromPOS"
supported-methods="GET"
request-channel="i2nInvokeRequest"
reply-channel="i2nInvokeResponse"
path="/postProduct/{Id}"
mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"
reply-timeout="50000">
<int-http:header name="Id" expression="#pathVariables.Id"/>
</int-http:inbound-gateway>
<int:service-activator id="InvokeActivator"
input-channel="i2nInvokeRequest"
output-channel="i2nInvokeResponse"
ref="apiService"
method="getProductId"
requires-reply="true"
send-timeout="60000"/>
<int:transformer input-channel="i2nInvokeResponse"
ref="apiTransformer"
method="retrieveProductJson"
output-channel="n2iInvokeRequest"/>
<int-http:outbound-gateway request-channel="n2iInvokeRequest" reply-channel="n2iInvoketransformerOut"
url="http://10.xx.xx.xx/api/index.php" http-method="POST"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<int:service-activator
input-channel="n2iInvoketransformerOut"
output-channel="n2iInvokeobjTransformerOut"
ref="apiService"
method="productResponse"
requires-reply="true"
send-timeout="60000"/>
The i2nInvokeFromPOS gateway is what we call from Web Application which is where all the products will be created. This Integration API will fetch that data, and post it to the backend system so that it will get updated to the other POS locations too!
Steps :
I will send the productId to i2nInvokeFromPOS.
apiTransformer -> retrieveProductJson() method will fetch the product details from DB based on the ID
Send the Request JSON to Backend system using http:outbound-gateway
Get the response from Backend and update the product status as uploaded in DB. Happens in apiService -> productResponse()
Once the response for A is received, all I'm getting is HTTP 500 Error for the Request B! But the Backend API is just fine.
The framework is completely thread-safe - if you are seeing cross-talk between different requests/responses then one (or more) of your components that the framework is invoking is not thread-safe.
You can't keep state in fields in, for example, code invoked from a service activator.

Printing log from flow.xml

I am working on a web application with spring web flow. While I am working on a flow.xml file I have get a decision state on it like this -
<decision-state id="checkPermissin">
<if test="requestParameters.canApprove" then="approve" else="warning" />
</decision-state>
When a request comes to the flow.xml then it get the request parameter canApprove from it and test whether it is true or false. Then it goes to the either of approve or warning state.
My question is - can I log/print the status of canApprove from the flow.xml file?
You can add an on-exit tag to the end of the 'decision-state' and call any service methods, spring beans, or static methods on the classpath.
Try this (untested):
<decision-state id="checkPermissin">
<if test="requestParameters.canApprove" then="approve" else="warning" />
<on-exit>
<evaluate expression="T(org.apache.log4j.Logger).getLogger('someLogger').info(requestParameters.canApprove)"/>
</on-exit>
</decision-state>
The above solution is more of a hack to fulfill what you are asking for. The "proper" way to log this is to extend a FlowExecutionListenerAdapter and to listen for your current flow + decision-state id "checkPermissin" then log whatever you desire about that flow but it would involve more setup/coding outside the flow.xml file. (see: Catch "dead" session in Spring webflow: the example is for catching exceptions but can easily be adapted for logging anything in a flow)

error handling/propagation in spring integration

I've read that if exception is thrown in the flow the first thing the framework will do is check message header for error-channel property. Is it always the case?
In my particular case I'm assigning a custom error-channel to a message header and yet the message seems to get propagated up the stream to the nearest error-handler/error-channel.
<int:chain id="buildAggregatedResponseChain" input-channel="aggregatedResultChannel"
output-channel="sendAggregatedChannel">
<int:header-enricher>
<int:error-channel ref="myErrorChannel"/>
</int:header-enricher>
<int:service-activator ref="service" method="doSomething"/>
</int:chain>
I explicitly throw an exception inside doSomething but the exception never ends up in myErrorChannel. Instead, it is "propagated" to the nearest ErrorHandler up the stream or to the error-channel specified up the stream for int-mail:imap-idle-channel-adapter(tried several different flows).
What do I miss? Maybe someone can outline the main principal of error handling/error propagation(for example when talking about several transactions, etc)? There is some information out there, but it is quite scattered and not systematic.
It depends on the upstream flow; if there's an async handoff the header is consulted; otherwise, the exception is thrown back to the inbound endpoint.
In general, I would advise against modifying framework headers such as errorChannel. Instead put an error-channel on the inbound endpoint (such as your imap idle adapter) and handle the errors on that flow.
Modifying the headers directly is rarely needed. If you wish to insert different error-handling mid-flow then you can insert a messaging gateway...
<int:service activator ... ref="gw" />
<int:gateway id="gw" default-request-channel="..."
error-channel="midFlowErrorChannel" />
If the downstream flow (from the gateway) returns no result on success, then be sure to add a default reply timeout of 0 (or use a custom service interface with a method that returns void).

Spring AMQP Multiple Vhosts

I currently need to utilize three vhosts for this application. I am only receiving messages over one as a consumer, and the others for RPC calls. Currently I am using CachingConnectionFactory that I have subclassed one for each virtual host. Then I am making each of those subclasses beans. I can then of course grab the connection factory to create the RabbitTemplate for the correct vhost instance.
I saw in the documentation about the AbstractRoutingConnectionFactory but wanted to know if there are any reasons I should refactor my currently working code. I want the most maintainable and perfomant solution, not the "easiest" one.
Thanks!
I am not sure why you felt it was necessary to subclass the CachingConnectionFactory you can simply declare multiple factories...
<rabbit:connection-factory id="default" host="localhost" />
<rabbit:connection-factory id="foo" host="localhost" virtual-host="/foo" />
<rabbit:connection-factory id="bar" host="localhost" virtual-host="/bar" />
Whether or not you using a routing connection factory (e.g. SimpleRoutingConnectionFactory) depends on your application needs. If you don't use it you would need 3 RabbitTemplates and decide which one to use at runtime.
With a routing connection factory, the RabbitTemplate can make the decision based on the message content with a send-connection-factory-selector-expression.
There's not really a lot of difference except the second decouples your application from the decision. For example, you can set a message header customerId before sending (or during message conversion if you're using a convertAndSend method) and use a selector expression such as #vhostFor.select(messageProperties.headers.customerId).
If you later add a new vhost you wouldn't have to change your main application, just your vhostFor lookup bean to pick the right vhost.

Categories

Resources