Thursday, June 11, 2009

Parallel processing and aggregation in WebSphere Integration Developer 6.2

When we integrate multiple applications using ESB, it is common requirement that we might need to call multiple services from a particular operation in our ESB application. And often, the calls are independent of other calls and in these cases it is expected that ESB layer call providers in an asynchronous manner to cut down the processing time. Although WebSphere Integration Developer came with mediation primitives for aggregating multiple responses, there was no (easy?) way that one could make asynchronous calls (parallel processing) and aggregate responses using Fan-Out/Fan-In and Service Invoke mediation primitives in WID 6.1.2 or prior versions (However, parallel processing and aggregation could be achieved using java code - using invokeAsync API - in Custom Mediation primitives). In these versions, one could only make multiple sync calls and aggregate responses using Fan-Out/Fan-In mediation primitives and Fan-Out/Fan-In provided a mechanism for looping.
Good news is that WID 6.2 has come up with features supporting parallel processing. Good thing about this is that if you are already using Fan-Out/Fan-In to make multiple sync calls using Service Invokes, it is very easy to change them to make async calls. Let’s see how to make service invokes run in asynchronous mode and aggregate responses of asynchronous calls within Fan-Out/Fan-In mediation primitives.
The following image shows my mediation flow implementation, where I have two Service Invoke mediation primitives enclosed within a set of Fan-Out/Fan-In primitives.


This is simple enough – out terminal of Fan-Out is connected to two Service Invokes – one calling an operation on ServiceA and other calling ServiceB. Fan-In aggregates response from both Service Invoke primitives. Let’s see the settings for Service Invoke mediation primitive to make asynchronous calls.


There is a new property added to Service Invoke named “Invocation Style”. You just need to select ‘Async’ from the drop down list to make Service Invoke make asynchronous call to the target provider. Also note that you need to set appropriate value to Async Timeout text box highlighted above. The response will be lost if this value set to a lower value that time taken by the service invoke to return response!
Let’s see settings for Fan-Out and Fan-In mediation primitives.



I have set Fan-Out out terminal to 'Fire once'. However, since I have wired the output terminal to two Service Invoke primitives, both providers are called and there are two responses to be aggregated. So in the properties window of Fan-In mediation primitive, I set a value 2 in “Fire output terminal when” text box. And this is the only option available when “Fire once” is selected in associated Fan-Out.
Let’s see another scenario, when you have an array of inputs, for example an array of ‘User’s to be created. In this case you might be calling same service multiple times – once for each element in array. In a similar case, you might be iterating through a set of inputs and calling different services based on a filter. In these cases the settings of Service Invoke primitives remain same. However, you need to set different properties for Fan-Out and Fan-In.



In Fan-Out primitive, I have specified a XPATH expression to iterate through the array of users. Out terminal will be fired for each User element in the input array. There are two radio buttons below this - these two are new properties of Fan-Out mediation primitive in WID 6.2. Please note that I have selected the radio button “Check for asynchronous responses after all messages have been fired”. This decides when to start collecting responses. And in properties of Fan-In, I have selected radio button “Fire output terminal when the associated Fan-Out primitive has iterated through all messages.” Alternatively you can set XPath expression to decide this or or set exact number of responses, if you know. In my case, out terminal is fired for each iteration as Fan-Out iterates through all messages, and corresponding responses are received.
Just a note - it seems that Fan-Out/Fan-In primitives in WID6.2 do not properly aggregate the responses from multiple synchronous calls, even though aggregation happens correctly for async calls. This was a working feature in WID 6.1.2, broken in WID 6.2 for some reason. There is a PMR for this and official fix should be available soon.

Tuesday, May 26, 2009

Using Axis WSDL2Java ant task to develop web service client


In my previous post Developing web service client using WebSphere WSDL2Java ant task we have seen how to use WebSphere wsdl2java ant task to develop web service clients. In this post, let us examine how to generate client side java artifacts using Axis wsdl2java ant task and code a simple web service client.
To run axis wsdl2java, all we need is Apache Axis(JAX-RPC compliant web service engine) installed. We also need Apache Xerces2 (XML parser) installed for running the client. I have Axis v1.4 and Xerces 2.9.1 installed in my system.
I have deployed a tiny sample Web Service 'ServiceA' in a application server running locally for this purpose. We will use the WSDL of this web service to generate stubs. I have defined an simple request/response operation 'createUser' in this.
Following is how I used wsdl2java in this example, in it's simplest way.
<axis-wsdl2java
output="${src-gen.dir}"
verbose="true"
url="${wsdl}"/>
</target>
The generated java files will be placed under folder represented by the output attribute. In my case I have assigned property 'src-gen.dir', which points the folder named src-gen under basedir. Attribute 'url' represents the WSDL on which we are running wsdl2java. It can be URL (like http://host:port/.../ServiceA.wsdl) or the path to the WSDL file in local file system. In this example, I have placed the WSDL file directly under the basedir, so the value of wsdl ant property is just 'ServiceA.wsdl'. Even though we can use the wsdl's URL in wsdl ant task, it becomes little tricky when the WSDL URL is HTTPS (URL with HTTPS protocol, like https://host:port/.../ServiceA.wsdl). In one of my previous post Running Axis WSDL2Java on HTTPS WSDL I have posted some info I had collected about this. If we are generating the client code frequently, it's better to use the WSDL URL to generate client code reflecting changes in web service, which is the ideal way. Otherwise it is better to copy the WSDL to local file system and run wsdl2java.
WSDL2Java ant task has many more advanced options, for example, mapping namespace to a package structure of your choice (by default package structure is derived from namespace), so that the generated classes arranged in this package structure, like given below. Please read more by going through Axis documentations.
<axis-wsdl2java
output="${src-gen.dir}"
verbose="true"
url="${wsdl}" >
<mapping
namespace="http://servicea.test.ws.ibswings.com"
package="com.ibswings.ws.test" />
</axis-wsdl2java>
In my example ant build script, I have called wsdl2java from a target viz. gen. Before we call wsdl2java, we need to provide ant with task definition for this, unless the jar containing these classes are placed under ant's lib folder, to be loaded while starting up. I have loaded the ant tasks like given below.
<taskdef resource="axis-tasks.properties" classpathref="axis.classpath" />
Classpath reference 'axis.classpath' loads all the required jars from the lib direct of Axis installation folder, for loading wsdl2java ant task. Axis is installed under C:\Dev\Installed\axis-1_4 in my computer, and an ant property 'axis.home' points to this folder.
<path id="axis.classpath">
<fileset dir="${axis.home}/lib">
<include name="**/*.jar" />
</fileset>
</path>
I have coded a simple java client program to test the web service, using the generated stubs. Here is the code.
package com.ibswings.ws.test.servicea;

public class Main {

public static void main(String ... args) {

try {
ServiceAImpl proxy = new ServiceAPortServiceLocator().getServiceAPort();
proxy.createUser(null);
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
To compile all the generated and above java code, I have defined a ant target named build as shown below.
<target name="build" depends="gen">
<javac srcdir="${src.dir}"
destdir="${build.dir}"
classpathref="axis.classpath"
debug="on">
<src path="${src-gen.dir}"/>
</javac>
</target>
And finally a target to run the client:
<target name="run">
<java classname="com.ibswings.ws.test.servicea.Main" classpathref="master.classpath"/>
</target>
Here is the complete ant script which has all targets needed from generating java stubs from wsdl, compile and finally to run the client.
<project name="axis" default="run">

<property name="axis.home" value="C:\Dev\Installed\axis-1_4"/>
<property name="xerces.home" value="C:\Dev\Installed\xerces-2_9_1"/>
<property name="src-gen.dir" value="src"/>
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="wsdl" value="ServiceA.wsdl"/>

<path id="axis.classpath">
<fileset dir="${axis.home}/lib">
<include name="**/*.jar" />
</fileset>
</path>

<path id="master.classpath">
<pathelement path="${build.dir}"/>
<path refid="axis.classpath"/>
<fileset dir="${xerces.home}">
<include name="xercesImpl.jar"/>
</fileset>
</path>

<taskdef resource="axis-tasks.properties" classpathref="axis.classpath" />

<target name="init">
<mkdir dir="${build.dir}"/>
<mkdir dir="${src-gen.dir}"/>

<delete includeemptydirs="true">
<fileset dir="${build.dir}" includes="**/*.*"/>
</delete>

<delete includeemptydirs="true">
<fileset dir="${src-gen.dir}" includes="**/*.*"/>
</delete>
</target>

<target name="gen" depends="init">
<axis-wsdl2java
output="${src-gen.dir}"
verbose="true"
url="${wsdl}" >
</axis-wsdl2java>
</target>

<target name="build" depends="gen">
<javac srcdir="${src.dir}"
destdir="${build.dir}"
classpathref="axis.classpath"
debug="on">
<src path="${src-gen.dir}"/>
</javac>
</target>

<target name="run">
<java classname="com.ibswings.ws.test.servicea.Main" classpathref="master.classpath"/>
</target>

</project>

Monday, April 20, 2009

Developing web service client using WebSphere WSDL2Java ant task

Clients for web services can be easily generated/coded any eclipse (or eclipse based) or any other modern IDEs for java development. All we need is a WSDL or URL pointing a WSDL. However, very often we are required to automate the process of generating client side java stubs (artifacts) using the build tool like Apache ant. Most of the web service engines/tools come bundled with ant tasks for generating java artifacts. In the following section we will see how to generate client side stubs using WebSphere WSDL2Java ant task and code a sample java web service client.
The foremost thing we need is the structure of "wsdl2java" ant task to generate the java client stubs. Here is the call to WebSphere wsdl2java ant task to generate client classes:
<wsdl2java url="${local.wsdl}"
output="${src-gen.dir}"
role="client"
container="none"
introspect="false"
genjava="true"
verbose="true">
</wsdl2java>
This is pretty simple. local.wsdl ant property points the the WSDL file (in local file system) of the Web Service for which I need to code the client. I have placed the WSDL under a folder wsdl (wsdl\ServiceA.wsdl) under the current directory (basedir, in ant term) from where I am running the ant command. Another property "src-gen.dir" the directory where the generated java files are placed. I am keeping these file in a separated directory, than the java files I coded to make the actual web service call, just for the convenience of managing/deleting these generated files.
As you are aware, to make wsdl2java task available to ant, we have two options. Either we have to place the jar containing this class in the ant's lib folder so that ant loads the tasks in start up, or explicitly load the task in ant build script it self. I have opted for the second option. Here is the taskdef for loading the wsdl2java task:
<taskdef name="wsdl2java" classname="com.ibm.websphere.ant.tasks.WSDL2Java">
<classpath refid="taskdefs.classpath">
</taskdef>
This is self explanatory. The actual class containing the definition of wsdl2java is "com.ibm.websphere.ant.tasks.WSDL2Java". This class in bundled in the jar "ws_runtime.jar" which is available one of the folders under your WAS/RAD installation folder. In my case it is "${WAS_HOME}\runtimes\bi_v61\deploytool\itp\plugins\com.ibm.websphere.v61_6.1.200\ws_runtime.jar". I refer the top level installation folder using a property "WAS_HOME". I am using WID (WebSphere Integration Developer) tool locally, so my WAS_HOME is the top level installation folder of WID - "C:\Program Files\ibm\WID61". You will need to change this accordingly for the WebSphere development environment like RAD that you are using. So the class path definition for this task goes like:
<path id="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\bi_v61\deploytool\itp\plugins\com.ibm.websphere.v61_6.1.200\ws_runtime.jar">
</path>
I have added the call to wsdl2java under target "gen". The target definition is:
<target name="gen" depends="init">
<wsdl2java url="${local.wsdl}"
output="${src-gen.dir}"
role="client"
container="none"
introspect="false"
genjava="true"
verbose="true">
</wsdl2java>
</target>
Running this "gen" taget from ant generates all the client java classes we need to make a call to the web service "ServiceA" to the directory "src-gen" in my case!
I have coded a simple client java class for making the call to the Web Service using the generated stubs and placed it under the folder src. So I have two source folders namely src and src-gen with java source files to compile. By the way, following is the simple java code calling the web service from my client java class "com.ibswings.test.Main":
package com.ibswings.test;
import com.ibswings.ws.test.servicea.ServiceAImpl;
import com.ibswings.ws.test.servicea.ServiceAPortServiceLocator;
import com.ibswings.ws.test.servicea.User;

public class Main {

public static void main(String ... args) {

try {
ServiceAImpl proxy = new ServiceAPortServiceLocator().getServiceAPort();
System.out.println("Created User: " + proxy.createUser(new User()).isSuccess());
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
Here is the ant target for compiling the java source files:
<target name="build" depends="gen">
<javac srcdir="${src.dir}"
destdir="${build.dir}"
classpathref="master.classpath"
debug="on">
<src path="${src-gen.dir}">
</javac>
</target>
To compile the java classes we need some jar files, which are again under some of the WebSphere's installation folder. I have added these jars under classpath definition "master.classpath" like:
<path id="master.classpath">
<pathelement path="${build.dir}">
<path refid="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\runtimes\com.ibm.ws.webservices.thinclient_6.1.0.jar">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\java\jre\lib\xml.jar">
</path>
In this case, I have given the exact path names of the jar files. However, in your case these jars might be in different directories, depending on the IDE/WebSphere environment you are using. Please search for these jars in your filesystem under WAS_HOME and replace the paths accordingly. Optionally, you can use <fileset> ant type to load the jars like:
<path id="master.classpath">
<pathelement path="${build.dir}">
<path refid="taskdefs.classpath">

<fileset dir="${WAS_HOME}\runtimes">
<include name="**/com.ibm.ws.webservices.thinclient_6.1.0.jar">
<include name="**/xml.jar">
</fileset>

</path>
However, ant might take a while to search these jars and load these, slowing your build process. I would suggest you to find the exact path for jars.
Finally, I have added a target for running my client. Here is the ant script:
<target name="run">
<java classname="com.ibswings.test.Main" classpathref="master.classpath">
</target>
Here is the my complete build script (build.xml):
<project name="webserviceclient" default="build">

<property name="WAS_HOME" value="C:\Program Files\ibm\WID61">
<property name="local.wsdl" value="wsdl\ServiceA.wsdl">
<property name="src-gen.dir" value="src-gen">
<property name="src.dir" value="src">
<property name="build.dir" value="build">

<path id="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\bi_v61\deploytool\itp\plugins\com.ibm.websphere.v61_6.1.200\ws_runtime.jar">
</path>

<taskdef name="wsdl2java" classname="com.ibm.websphere.ant.tasks.WSDL2Java">
<classpath refid="taskdefs.classpath">
</taskdef>

<path id="master.classpath">
<pathelement path="${build.dir}">
<path refid="taskdefs.classpath">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\runtimes\com.ibm.ws.webservices.thinclient_6.1.0.jar">
<pathelement location="${WAS_HOME}\runtimes\base_v61_stub\java\jre\lib\xml.jar">
</path>

<target name="init">
<mkdir dir="${build.dir}">
<mkdir dir="${src-gen.dir}">

<delete includeemptydirs="true">
<fileset dir="${build.dir}" includes="**/*.*">
</delete>

<delete includeemptydirs="true">
<fileset dir="${src-gen.dir}" includes="**/*.*">
</delete>
</target>

<target name="gen" depends="init">
<wsdl2java url="${local.wsdl}"
output="${src-gen.dir}"
role="client"
container="none"
introspect="false"
genjava="true"
verbose="true">
</wsdl2java>
</target>

<target name="build" depends="gen">
<javac srcdir="${src.dir}"
destdir="${build.dir}"
classpathref="master.classpath"
debug="on">
<src path="${src-gen.dir}">
</javac>
</target>

<target name="run">
<java classname="com.ibswings.test.Main" classpathref="master.classpath">
</target>

</project>
One final and most important thing - you cannot run ant targets defined above with the ant executable that you have downloaded and installed from apache website! You need to run these using the "ws_ant" (ws_ant.bat in Windows and ws_ant.sh in UNIX) command line utility that comes bundled with webbphere. This is typically available under "<ws_home>\runtimes\bi_vxx\bin\" folder (C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat in my case).
The command run "gen" target defined above, I run following command:
"C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat" gen
To build target above generates the (Depends on 'gen') java artifacts and them compiles all java files (including the client Main.java I coded). 'build' target is also the default target of my ant project. The ant command to build is:
"C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat" build
or simply
"C:\Program Files\ibm\WID61\runtimes\bi_v61\bin\ws_ant.bat"
That's it.. It's that easy to use WebSphere wsdl2java.
Please note that, to run the client, I need to have the actual webservice "ServiceA" running somwhere (locally, per my Main.java) ... Which I have not mentioned here, since that is sort of out of scope!
I have used wsdl1java in simplest way possible in this example. WSDL2Java has many more options. Please read more about WebSphere ant tasks at WebSphere info center. For example, http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp.

Tuesday, March 10, 2009

Log4J configuration – controlling logging to multiple loggers

I recently happened to read a blog about Apache Lo44J java logging API where the blogger mentioned that there is no clean good document/tutorial about using log4j, considering the fact that it is one of the most widely used open source java API. I felt that it is true to some extent, when I had to look for some help regarding a logging issue I encountered. Especially, I could not find one authentic manual/tutorial explaining the log4j configuration.


My requirement was to control the logging of messages to multiple appenders (targets) with different log priority. For example, I wanted to a log message to write log only to file if priority is debug but wanted to write to both console and log file if priority is set to info or above. With the help of some blogs and log4j javadocs, I found ways to do this. In this blog, I am explaining what I understood, assuming that it might help others looking for this info.


I used XML configuration for configuring log4j in my project, so I will use same in this article. I assume that you (reader of this blog) have basic understanding of Log4J configuration, if not please read this short manual at apache. I expect you to understand Log4J terms like Logger, Log Level and the XML tags like <category>, <appender>, <layout> etc., those appear in log4j configuration.


Note: wherever I mention “logger” (starting with lower case l), I referelement <category> in log4j configuration xml. I mean <root> wherever I mention root logger and tag <appender> wherever I mention “appender”.


As I mentioned above, my requirement was to log messages to multiple appenders (targets), console and a file, But I wanted to all messages (any level – level DEBUG or above) logged to file and only messages with log level ERROR or higher to the console. Following is my log4j.xml


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">


<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">

<param name="File" value="TestLogFile.log"/>

<param name="DatePattern" value="'.'yyyy-MM-dd"/>

<param name="Append" value="true"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<category name="com.ibswings">

<priority value="debug"/>

<appender-ref ref="FILE"/>

</category>


<root>

<priority value="error"/>

<appender-ref ref="CONSOLE"/>

</root>

</log4j:configuration>


A simple class I used to test this:


package com.ibswings.loggertest;


import org.apache.log4j.Logger;


public class Test {


public static void main(String[] args) {

new Test();

}


public Test() {

Logger logger = Logger.getLogger(getClass());

logger.debug("Debug Message");

logger.info("Info Message");

logger.error("Error Message");

}

}


I was expecting this to print all three messages from code to be printed to the log TestLogFile.log and only error message to be printed to console, since I have set ‘error’ priority in <root>. However I noticed that all messages are printed to both log file and console like:


2009-03-07 19:32:00,893 DEBUG Test.java:14 - Debug Message

2009-03-07 19:32:00,893 INFO Test.java:15 - Info Message

2009-03-07 19:32:00,893 ERROR Test.java:16 - Error Message


Let’s look into this in detail. By default, when a message is logged, the message will go to the first logger () whose names closely matches the name of the Logger instance you created in Java code. From there, it is directed to the next matching logger up in the logger hierarchy until root logger. In each of these loggers, output is sent to all appenders indentified by definitions. In the above example, I had created logger in my code like:

Logger logger = Logger.getLogger(getClass());


Actually, this is equivalent of

Logger logger = Logger.getLogger(“com.ibswings.loggertest.Test”);


When following line is executed,

logger.debug("Debug Message");


The closest matching logger defined in config is <category name="com.ibswings". This logs output to TestLogFile.log through appender named “FILE” (Note that if I had a logger defined in log4j.xml with name "com.ibswings.loggertest", that would have been the first logger to get the message). Message directed to root logger from here, since there is no other logger matching the name in between. And, message is printed out to standard out by CONSOLE appender. Also, when message is logged by root, it is logged with priority of message it received it from previous logger. Hence all messages are printed to console in this case, even though we have set error priority in root. The priority set in root has two uses – first, it is the default priority of all other loggers without priorities set. Second, all messages which are only received by root logger (i.e. messages with no matching loggers in log4j.xml) will have this default priority enabled. In following section, let’s see how we can avoid duplicating messages in both log file and console.


How to log only my messages to log file and others to console?


As I mentioned above, output from each logger is directed to next logger above in the logger hierarchy, till root logger. To set behavior off, we need use “addictivity” attribute in . If addictivity is set to “false” in a particular logger, output will not be sent to next logger up in the hierarchy. Or, output is sent from logger to logger, until it reaches a logger (of course, before root) in which addictivity is set to false. By default (when not specified) addivity is set to true. That’s how in the previous case messages are printed to both file and console. Let’s use this in out log4j.xml to stop messages from printing in console.


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">


<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">

<param name="File" value="TestLogFile.log"/>

<param name="DatePattern" value="'.'yyyy-MM-dd"/>

<param name="Append" value="true"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<category name="com.ibswings" additivity="false">

<priority value="debug"/>

<appender-ref ref="FILE"/>

</category>


<root>

<priority value="error"/>

<appender-ref ref="CONSOLE"/>

</root>

</log4j:configuration>


With this log4j.xml, all messages from the code are logged to file only. Since we set additivity="false" in the first logger, output is not directed to root logger. In this case, other Logger instances with name not matching “com.ibswings” and any System.out.printxx() calls go to console. In the following section, let’s see how to send all messages from our java code to file and some selected messages (based on priority) to console.


How to log all my messages to log file and selected (based on priority) my messages to console?


Coming to my original requirement, I want to send all my messages (printed from my java code) to log file. Also, I want to send all error messages from my code to console also. At the same time, I want to see all other messages (from other Logger instances and SOP calls) in console.


To achieve this, let’s assume we add appender-ref to console in our first logger. Now question is how to send only error messages, since whatever priority we set to in logger is applicable to all appenders. The solution is to use “Threshold” parameter in appender definition. If we specify a particular priority in appender using threshold, only messages with priority equal of higher to priority specified in ‘threshold’ are printed to the target. However, in this case, we cannot set ‘threshold’ to error in CONSOLE appender, since we need to print all messages (any level) not covered by our logger to console, in root logger. Let’s create a new appender CONSOLE_1 to log messages to console. The configuration is given below.


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">


<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<appender name="CONSOLE_1" class="org.apache.log4j.ConsoleAppender">

<param name="Threshold" value="ERROR"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">

<param name="File" value="TestLogFile.log"/>

<param name="DatePattern" value="'.'yyyy-MM-dd"/>

<param name="Append" value="true"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<category name="com.ibswings" additivity="false">

<priority value="debug"/>

<appender-ref ref="FILE"/>

<appender-ref ref="CONSOLE_1"/>

</category>


<root>

<priority value="error"/>

<appender-ref ref="CONSOLE"/>

</root>

</log4j:configuration>


When I ran my test, I see all message logged to file and error message printed in console. Also, all other messages with any level and SOPs printed to console, since we have appender CONSOLE referenced in root logger. In following section, let’s see how to all my messages and all other error messages, only to log file. Following is the log4j.xml we can use to achieve this.


How to log all my messages and all other error messages, only to log file?


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">


<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">

<param name="File" value="TestLogFile.log"/>

<param name="DatePattern" value="'.'yyyy-MM-dd"/>

<param name="Append" value="true"/>

<layout class="org.apache.log4j.PatternLayout">

<param name="ConversionPattern" value="%d [%t] %-5p %c (%F:%L) - %m%n"/>

</layout>

</appender>


<category name="com.ibswings" additivity="false">

<priority value="debug"/>

<appender-ref ref="FILE"/>

</category>


<root>

<priority value="error"/>

<appender-ref ref="FILE"/>

<!--

<appender-ref ref="CONSOLE"/>

-->

</root>

</log4j:configuration>


Note that I have commented appender-ref CONSOLE in root logger. With this log4j.xml, all messages logged from my java code are printed to log file. At the same time any other message with priority error is also printed to log file. All these will be logged only to the log file, not to console. However, we will still see SOPs in the logger (if we use this in an Application server, we will see lot of such messages in console/standard out or error log files).