Spring Integration Bridge - Request Headers - spring-integration

I have a SI application that uses Bridge to bridge some channels. I want the request headers to be copied through the bridge. When I debugged the application, I found that the BridgeHandler has a method which sets copyRequestHeaders() to false.
public class BridgeHandler extends AbstractReplyProducingMessageHandler {
#Override
public String getComponentType() {
return "bridge";
}
#Override
protected Object handleRequestMessage(Message<?> requestMessage) {
return requestMessage;
}
#Override
protected boolean shouldCopyRequestHeaders() {
return false;
}
}
I want to change this to keep it true. I looked up Bridge's XSD and it doesnt seem to support any entry to alter this, nor does it take a reference to a custom bridge handler bean.
What are my options now? Any suggestions would be helpful.
spring-config.xml:
<beans:import resource="classpath*:spring/rabbit.xml" />
<beans:import resource="classpath*:spring/router.xml" />
<beans:import resource="classpath*:spring/file_persister.xml" />
<beans:import resource="classpath*:spring/transformer.xml" />
<!-- Rabbit to Router -->
<int:bridge id="rabbitBridge" input-channel="rabbitInboundChannel" output-
channel="routerInputChannel" />
rabbit.xml:
<int:channel id="rabbitInboundChannel" />
<rabbit:connection-factory id="amqpConnectionFactoryInbound"
host="${rabbit.host}" port="${rabbit.port}"
username="${rabbit.username}" password="${rabbit.password}" channel-cache-
size="${rabbit.channelCacheSize}"
connection-factory="rabbitConnectionFactoryInbound" />
<beans:bean id="rabbitConnectionFactoryInbound"
class="com.rabbitmq.client.ConnectionFactory">
<beans:property name="requestedHeartbeat"
value="${rabbit.requestedHeartBeat}" />
</beans:bean>
<!-- Inbound Adapter to AMQP RabbitMq -->
<int-amqp:inbound-channel-adapter id="rabbitMQInboundChannelAdapter"
channel="rabbitInboundChannel" concurrent-
consumers="${rabbit.concurrentConsumers}" task-executor="rabbit-
executor" connection-factory="amqpConnectionFactoryInbound"
message-converter="byteArrayToStringConverter" queue-
names="${rabbit.queue}" error-channel="errorChannelId"
prefetch-count="${rabbit.prefetchCount}" />
<header-enricher input-channel="rabbitInboundChannel" output-
channel="rabbitLoggingChannel">
<int:header name="Operation" value="${operation.rabbit}" overwrite="true" />
**<int:header name="GUID" expression="#{ 'T(java.util.UUID).randomUUID().toString()' }" />**
<int:header name="operationStartTime" expression="#{ 'T(java.lang.System).currentTimeMillis()' }" />
</header-enricher>
<int:channel id="rabbitLoggingChannel">
<int:interceptors>
<int:wire-tap channel="loggerChannel" />
</int:interceptors>
</int:channel>
<task:executor id="rabbit-executor" rejection-policy="CALLER_RUNS" pool-
size="${rabbit.poolSize}" queue-capacity="${rabbit.queueSize}" />
router.xml:
<int:channel id="routerInputChannel" />
<int:header-enricher input-channel="routerInputChannel" output-
channel="routerLoggingChannel">
<int:header name="Operation" value="${operation.router}" overwrite="true"
/>
<int:header name="file_name" expression="headers['GUID'] + '.xml'" />
<int:header name="operationStartTime" expression="#{
'T(java.lang.System).currentTimeMillis()' }" overwrite="true" />
<int:error-channel ref="errorChannelId" />
</int:header-enricher>
<int:recipient-list-router id="recipientListRouter" input-
channel="routerInputChannel">
<int:recipient channel="filePersistChannel" selector-expression="new
String(payload).length()>0" />
<int:recipient channel="transformerInputChannel" />
</int:recipient-list-router>
<int:channel id="routerLoggingChannel">
<int:interceptors>
<int:wire-tap channel="loggerChannel" />
</int:interceptors>
</int:channel>
Logging O/P:
2017-04-24 13:26:33,360 [rabbit-executor-4] INFO
Operation="RABBIT_INBOUND_ADAPTER" Status="Success" DurationMs="0"
GUID="8d5c67c8-a0fb-4a7e-99dc-f545159dde7e"
2017-04-24 13:26:33,361 [rabbit-executor-4] INFO Operation="ROUTER"
Status="Success" DurationMs="0" GUID=" "
2017-04-24 13:26:33,364 [rabbit-executor-4] INFO Operation="FILE_PERSISTER"
Status="Success" DurationMs="3" GUID=" "
2017-04-24 13:26:33,381 [rabbit-executor-5] INFO
Operation="ORDER_EVENT_TRANSFORMER" Status="Success" DurationMs="27"
GUID=" "
If you notice, the GUID header which gets set in rabbit.xml is not propogated to router.xml, hence the log has empty GUID. Without the bridge, the GUID gets printed.

You don't need to do that. The <bridge> is based on the BridgeHandler which logic is very transparent:
#Override
protected Object handleRequestMessage(Message<?> requestMessage) {
return requestMessage;
}
So, it is obvious - your request headers are transferred, just because the whole requestMessage travels to the outputMessage.
The logic behind that method is like:
protected Message<?> createOutputMessage(Object output, MessageHeaders requestHeaders) {
AbstractIntegrationMessageBuilder<?> builder = null;
if (output instanceof Message<?>) {
if (!this.shouldCopyRequestHeaders()) {
return (Message<?>) output;
}
builder = this.getMessageBuilderFactory().fromMessage((Message<?>) output);
}
else if (output instanceof AbstractIntegrationMessageBuilder) {
builder = (AbstractIntegrationMessageBuilder<?>) output;
}
else {
builder = this.getMessageBuilderFactory().withPayload(output);
}
if (this.shouldCopyRequestHeaders()) {
builder.copyHeadersIfAbsent(requestHeaders);
}
return builder.build();
}
Since our output is Message and we don't copyRequestHeaders(), nothing is changed and everything is good for you.
I wonder what makes you to come to us with such an issue, because there is just no issue.
confused

Related

Spring integration tcp factory performance

I have some performance problem with spring integration tcp factory.
my application have about 70 clients which trying to send data through tcp connection. i used below configuration for tcp server using spring integration but in server i receive data every 5 seconds. but when i implement tcp socket manually without using spring integration i receive about 5 connections in every second. any idea about my problem ? i really want to use spring integration but i don't know how can i increase my performance.
<int:poller id="defaultPoller" default="true" tast-executor="defaultTaskExecutor" fixed-delay="500" />
<task:executor id="defaultTaskExecutor" pool-size="5-20" queue-capacity="50"/>
<bean id="CustomeSerializerDeserializer"
class="CustomeSerializerDeserializer" />
<task:executor id="tcpFactoryTaskExecutor" pool-size="5-20"
queue-capacity="20000" />
<int-ip:tcp-connection-factory id="tcpConnectionFactory"
type="server" port="5423"
single-use="false" so-timeout="5000" task-executor="tcpFactoryTaskExecutor"
serializer="CustomeSerializerDeserializer" deserializer="CustomeSerializerDeserializer" />
<int-ip:tcp-inbound-channel-adapter
id="tcpInboundAdapter" channel="requestChannel" connection-factory="tcpConnectionFactory" />
<int:channel id="requestChannel">
<int:queue capacity="50" />
</int:channel>
<int:service-activator input-channel="requestChannel"
output-channel="responseChannel" ref="MessageHandler" method="parse" />
<bean id="MessageHandler"
class="TCPMessageHandler" />
<int:channel id="responseChannel">
<int:queue capacity="50" />
<int:channel />
<int-ip:tcp-outbound-channel-adapter
id="tcpOutboundAdapter" channel="responseChannel" connection-factory="tcpConnectionFactory" />
UPDATE1: here is my custom serialize/deserializer class:
public class SerializerDeserializer extends AbstractByteArraySerializer{
#Override
public void serialize(byte[] object, OutputStream outputStream)
throws IOException {
if (object != null && object.length != 0) {
outputStream.write(object);
outputStream.flush();
}
}
#Override
public byte[] deserialize(InputStream inputStream) throws IOException {
int c = inputStream.read();
if (c!=0){
// 2 byte
byte[] configMessage = BinaryUtil.readNByteArrayFromStream(inputStream, c)/*(inputStream , c)*/;
return configMessage;
}
int d = inputStream.read();
if (d==0){
// 253 byte
byte[] dataMessage = BinaryUtil.readNByteArrayFromStream(inputStream,253);
return dataMessage;
}
// 15 byte
byte[] hanshakeMessage = BinaryUtil.readNByteArrayFromStream(inputStream,d);
return hanshakeMessage;
}
}
I suspect a problem with your custom deserializer 5 seconds is suspicious since your timeout is also 5 seconds - show the code and explain the protocol.
If the deserializer doesn't receive a full message it will time out.
Also turn on TRACE level logging for org.springframework.integration to debug this - if you can't figure it out from the trace, post the log file somewhere like pastebin and we'll take a look.

Spring integration: handle http error with oubound gateway

How can I handle exceptions in an http outbound gateway?
When i receive status code 500 or 400..., an exception is shown. So What should I do to handle http error using spring integration.
My configuration is like:
<int:inbound-channel-adapter channel="quakeinfotrigger.channel"
expression="''">
<int:poller fixed-delay="60000"></int:poller>
</int:inbound-channel-adapter>
<int:channel id="quakeinfo.channel">
<int:queue capacity="10" />
</int:channel>
<int:channel id="quakeinfotrigger.channel"></int:channel>
<int:channel id="error.channel">
<int:queue capacity="10" />
</int:channel>
<int:service-activator input-channel="error.channel"
ref="httpResponseErrorHandler" method="handleMessage">
<int:poller fixed-delay="5000"></int:poller>
</int:service-activator>
<int:service-activator input-channel="quakeinfo.channel"
ref="httpResponseMessageHandler" method="handleMessage">
<int:poller fixed-delay="5000"></int:poller>
</int:service-activator>
<int:gateway id="requestGateway" service-interface="standalone.HttpRequestGateway"
default-request-channel="quakeinfotrigger.channel" error-channel="error.channel" />
<int-http:outbound-gateway id="quakerHttpGateway"
request-channel="quakeinfotrigger.channel" url="http://fooo/mmmm/rest/put/44545454"
http-method="PUT" expected-response-type="java.lang.String" charset="UTF-8"
reply-timeout="5000" reply-channel="quakeinfo.channel">
</int-http:outbound-gateway>
<bean id="httpResponseMessageHandler" class="standalone.HttpResponseMessageHandler" />
<bean id="httpResponseErrorHandler" class="standalone.HttpResponseErrorHandler" />
I would like to know why exception does'nt go to reply-channel
I would like to know why exception does'nt go to reply-channel
Because it's natural to handle exceptions as, er, Exceptions.
There are (at least) two ways to handle exceptions in Spring Integration.
Add an error-channel and associated flow on whatever starts your flow (e.g. a gateway). The error channel gets an ErrorMessage with a MessagingException payload; the exception has two properties - the failedMessage and the cause.
Add a ExpressionEvaluatingRequestHandlerAdvice (or a custom advice) to the gateway; see Adding Behavior to Endpoints.
If the response status code is in the HTTP series CLIENT_ERROR or SERVER_ERROR, HttpClientErrorException or HttpServerErrorException are thrown respectively. Hence the response doesn't go to reply channel
Refer DefaultResponseErrorHandler
Methods: hasError and handleError
To Handle these exceptions, create your own CustomResponseErrorHandler and override contents of hasError and handlerError methods.
public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return hasError(getHttpStatusCode(response));
}
protected boolean hasError(HttpStatus statusCode) {
return /*Status Code to be considered as Error*/ ;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException { /* Handle Exceptions */
}
}
And add error-handler to your http-outbound-gateway
<int-http:outbound-gateway id="quakerHttpGateway"
request-channel="quakeinfotrigger.channel" url="http://fooo/mmmm/rest/put/44545454"
http-method="PUT" expected-response-type="java.lang.String" charset="UTF-8"
reply-timeout="5000" reply-channel="quakeinfo.channel" error-handler="customResponseErrorHandler">
</int-http:outbound-gateway>
I had occurred the same problem.I threw my own customized Exception and error message but always got "500 Internal server error".It's because there will be no reply message and "reply" is timeout when throw Exception.So I handle the exception by subscribing error-channel and then reply by myself.
Message<?> failedMessage = exception.getFailedMessage();
Object replyChannel = new MessageHeaderAccessor(failedMessage).getReplyChannel();
if (replyChannel != null) {
((MessageChannel) replyChannel).send(MessageFactory.createDataExchangeFailMessage(exception));
}

Spring integration message-store rolled back before error handler

I need to handle certain error conditions within my spring integration flow. My flow is using a message store and setting the error channel on the poller. I had thought that if I handled the message in the error handler that the rollback would not occur, but the messageStore remove (delete) is being rolled back before the error flow is even executed.
Here is a pseudo-flow that duplicates my issue.
<int:channel id="rollbackTestInput" >
<int:queue message-store="messageStore"/>
</int:channel>
<int:bridge input-channel="rollbackTestInput" output-channel="createException" >
<int:poller fixed-rate="50"
error-channel="myErrorChannel">
<int:transactional />
</int:poller>
</int:bridge>
<int:transformer input-channel="createException" output-channel="infoLogger"
expression="T(GarbageToForceException).doesNotExist()" />
<int:channel id="myErrorChannel">
<int:queue/>
</int:channel>
<!-- JDBC Message Store -->
<bean id="messageStore" class="org.springframework.integration.jdbc.JdbcMessageStore">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource"/>
This flow will result in an infinite rollback/poll loop. How can I handle the error and not rollback?
That's correct behavior. The error-channel logic is accepted around the TX advice for the polling task.
The code looks like:
#Override
public void run() {
taskExecutor.execute(new Runnable() {
#Override
public void run() {
int count = 0;
while (initialized && (maxMessagesPerPoll <= 0 || count < maxMessagesPerPoll)) {
try {
if (!pollingTask.call()) {
break;
}
count++;
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
else {
throw new MessageHandlingException(new ErrorMessage(e), e);
}
}
}
}
});
}
Where TX Advice is on the pollingTask.call(), but error handling is done from the taskExecutor:
this.taskExecutor = new ErrorHandlingTaskExecutor(this.taskExecutor, this.errorHandler);
Where your error-channel is configured on that errorHandler as MessagePublishingErrorHandler.
To reach your requirements you can try to follow with synchronization-factory on the <poller>:
<int:transaction-synchronization-factory id="txSyncFactory">
<int:after-rollback channel="myErrorChannel" />
</int:transaction-synchronization-factory>
Or supply your <int:transformer> with <request-handler-advice-chain>:
<int:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onFailureExpression" value="#exception" />
<property name="failureChannel" value="myErrorChannel" />
<property name="trapException" value="true" />
</bean>
</int:request-handler-advice-chain>
I have found a different solution that meets my requirements and is less invasive.
Instead of using transactional on the poller, I can use an advice chain that that specifies which exceptions I should rollback and which I shouldn't. For my case, I do not want to rollback most exceptions. So I rollback Throwable and list any specific exceptions I want to rollback.
<int:bridge input-channel="rollbackTestInput"
output-channel="createException">
<int:poller fixed-rate="50"
error-channel="myErrorChannel">
<int:advice-chain>
<int:ref bean="txAdvice"/>
</int:advice-chain>
</int:poller>
</int:bridge>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" rollback-for="javax.jms.JMSException"
no-rollback-for="java.lang.Throwable" />
</tx:attributes>
</tx:advice>

Resttemplate : HttpMessageNotWritableException: No serializer found for class java.io.ByteArrayInputStream

Using Spring Integration I upload a file via HTTP POST and route it to a service activator.
In the service activator, I make a call using RestTemplate to another server where to dump the file but I can't figure out why I get the following error for the following code:
What I don't understand is why I get the exception below when I call RestTemplate.exchange()
at pdi.integration.MultipartReceiver.printMultiPartContent(MultipartReceiver.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
xml-config
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<bean id="byteArrayHttpMessageConverter"
class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
</bean>
<bean id="formHttpMessageConverter"
class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper">
<property name="inboundHeaderNames" value="*"/>
<property name="outboundHeaderNames" value="*"/>
<property name="userDefinedHeaderPrefix" value=""/>
</bean>
<int:channel id="http.frontend.rx"/>
<int:channel id="http.frontend.tx"/>
<int:channel id="http.backend.mysql.rx"/>
<int:channel id="http.backend.mysql.tx"/>
<int:channel id="http.backend.mongo.rx"/>
<int:channel id="http.backend.mongo.tx"/>
<int:channel id="http.backend.file.tx"/>
<int-http:inbound-gateway
id="frontEndToMySQLXX"
request-channel="http.frontend.rx"
reply-channel="http.frontend.tx"
header-mapper="headerMapper"
path="/gateway"
supported-methods="GET,POST,PUT,DELETE"/>
<int:router id="frontEndRouter" input-channel="http.frontend.rx" expression="headers.service">
<int:mapping value="json" channel="http.backend.mysql.tx" />
<int:mapping value="file" channel="http.backend.mongo.tx" />
<int:mapping value="upload" channel="http.backend.file.tx" />
</int:router>
<!-- removed : message-converters="formHttpMessageConverter,byteArrayHttpMessageConverter" -->
<int-http:outbound-gateway
id="toMongoDB"
request-channel="http.backend.mongo.tx"
reply-channel="http.backend.mongo.rx"
url="http://localhost:5050/api/{path}"
http-method-expression="headers.http_requestMethod"
header-mapper="headerMapper"
expected-response-type="byte[]">
<int-http:uri-variable name="path" expression="headers['urlpath']"/>
</int-http:outbound-gateway>
<int-http:outbound-gateway
id="toMySQLDB"
request-channel="http.backend.mysql.tx"
reply-channel="http.backend.mysql.rx"
url="http://localhost:7070/api/{path}"
http-method-expression="headers.http_requestMethod"
expected-response-type="java.lang.String"
charset="UTF-8">
<int-http:uri-variable name="path" expression="headers['urlpath']"/>
</int-http:outbound-gateway>
<int:service-activator
id="MySQLToFrontEnd"
input-channel="http.backend.mysql.rx"
output-channel="http.frontend.tx"
ref="messageService"
method="printContent">
</int:service-activator>
<int:service-activator
id="MongoToFrontEnd"
input-channel="http.backend.file.tx"
output-channel="http.frontend.tx"
ref="multipartReceiver"
method="printMultiPartContent">
</int:service-activator>
</beans>
bean used by service activator
#Component
public class MultipartReceiver {
public void printMultiPartContent(LinkedMultiValueMap<String, Object> multipartRequest){
System.out.println("### Successfully received multipart request ###");
for (String elementName : multipartRequest.keySet()) {
if (elementName.equals("file")){
System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
((UploadedMultipartFile) multipartRequest
.getFirst("file")).getOriginalFilename());
}
}
RestTemplate template = new RestTemplate();
String uri = "http://localhost:5050/api/upload";
MultiValueMap map = new LinkedMultiValueMap<String,Object>();
map.add("file", multipartRequest.getFirst("file"));
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "multipart/form-data");
HttpEntity request = new HttpEntity(map, headers);
ResponseEntity<byte[]> response = template.exchange(uri, HttpMethod.POST, request, byte[].class);
}
}
stacktrace :: http://pastebin.com/5Wa9VaRb
working code ::
public void printMultiPartContent(LinkedMultiValueMap<String, Object> multipartRequest) throws IOException {
final String filename = ((UploadedMultipartFile) multipartRequest.getFirst("file")).getOriginalFilename();
RestTemplate template = new RestTemplate();
MultiValueMap<String, Object> multipartMap = new LinkedMultiValueMap<String, Object>();
multipartMap.add("name", filename);
multipartMap.add("filename", filename);
byte[] bytes = ((UploadedMultipartFile) multipartRequest.getFirst("file")).getBytes();
ByteArrayResource contentsAsResource = new ByteArrayResource(bytes){
public String getFilename(){
return filename;
}
};
multipartMap.add("file", contentsAsResource);
String result = template.postForObject("http://localhost:5050/api/upload", multipartMap, String.class);
System.out.println(result);
}
The problem is you are passing the complete UploadedMultipartFile object to the RestTemplate. Jackson is trying to serialize the object, including the inputStream property, which it cannot.
It appears you need to extract the file contents
byte[] bytes ((UploadedMultipartFile) multipartRequest.getFirst("file")).getBytes();
And set the content type to the UploadedMultipartFile.getContentType().

log4net: Logging two messages in one row in the database?

I'm trying to log the input and output of a particular method to the database. I'd like to have this information in separate columns. I've investigated the PatternLayout and it seems that it only caters for a single %message parameter, meaning that if you do:
log.Debug("This is a message");
then log4net sees "This is a message" as the message to be logged. I want to do something like:
log.Debug(request, response);
Is this possible using log4net? Keep in mind that my goal is to have "request" and "response" in separate columns.
Your PatternConverter way is a step in the right direction, though the use of the static Input and Output properties makes it all a bit shaky (thread-safety wise).
The trick here is to realize that the message parameter on logger.Debug(...) is object and that you can pass in whatever you like.
You could define a custom message type
public class InputOutput
{
public string Input {get;set;}
public string Output {get;set;}
}
and then let your converters read either property
public class InputPatternConverter : PatternConverter
{
protected override void Convert(System.IO.TextWriter writer, object state)
{
var msg = ((LoggingEvent)state).MessageObject as InputOutput;
if (msg != null)
writer.Write(msg.Input);
}
}
public class OutputPatternConverter : PatternConverter
{
protected override void Convert(System.IO.TextWriter writer, object state)
{
var msg = ((LoggingEvent)state).MessageObject as InputOutput;
if (msg != null)
writer.Write(msg.Output);
}
}
the logging then becomes much cleaner
logger.Debug(new InputOutput { Input = ..., Output = ...});
your config would be the same.
A tip though is to subclass the PatternLayout and add the converters in the constructor of that class. That way you can also trim down your config. This will not cause you to loose the %message token, your %input and %output tokens will come in addition to all the tokens that PatternLayout supports. So you could actually have a pattern like this:
"%date %message %newline%newline %input %newline%newline %output
Here's a quick implementation of a custom pattern layout:
public class InputOutputPatternLayout : PatternLayout
{
public InputOutputPatternLayout()
{
AddConverter("input", typeof(InputPatternConverter));
AddConverter("output", typeof(OutputPatternConverter));
}
}
I've come up with one way to do this using custom PatternConverters
public class InputPatternConverter : PatternConverter
{
private static string _input;
public static string Input
{
get { return _input; }
set { _input = value; }
}
protected override void Convert(System.IO.TextWriter writer, object state)
{
writer.Write(Input);
}
}
public class OutputPatternConverter : PatternConverter
{
private static string _output;
public static string Output
{
get { return _output; }
set { _output = value; }
}
protected override void Convert(System.IO.TextWriter writer, object state)
{
writer.Write(Output);
}
}
Appender Specification:
<appender name="ADONetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="data source=servername;initial catalog=database;Integrated Security=SSPI;" />
<commandText value="INSERT INTO RequestLog ([input], [output]) VALUES (#input, #output)" />
<parameter>
<parameterName value="#input" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<converter>
<name value="input" />
<type value="InputPatternConverter, ApplicationName" />
</converter>
<conversionPattern value="%input" />
</layout>
</parameter>
<parameter>
<parameterName value="#output" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<converter>
<name value="output" />
<type value="OutputPatternConverter, ApplicationName" />
</converter>
<conversionPattern value="%output" />
</layout>
</parameter>
</appender>
Call it using:
InputPatternConverter.Input = inputString;
OutputPatternConverter.Output = outputString;
XmlConfigurator.Configure();
ILog logger = LogManager.GetLogger(typeof(ApplicationClassName));
logger.Debug("");

Resources