Monday, April 21, 2014

Using Logback logging framework in Eclipse - Simple example/tutorial

Logback is a logging architecture that is very popular and customizable. It is intended as a successor of log4j. Long story short, use logback instead of log4j for your new projects. (The author is the same for both log4j and logback frameworks. Look at the links at the end of the tutorial if you are interested in finding out the history, which framework is better etc.).

In this tutorial, we will learn on how to setup logback in a Java project, how to create appenders and how to write the logs to a specific location. This tutorial uses RSA (Rational Software Architect), but you can use Eclipse or any other IDE of your choice.

Go to File -> New -> Web -> Dynamic Web Project


 Give it a name and add the project to an EAR using the Wizard screen as shown below. (If you are not using Websphere, you can use any other server or any other Project wizard of your choice)


Click Next, Next and click Finish (you can check the ‘Generate web.xml deployment descriptor’ if you want to)

A new Web project is created by now with the above steps.

To run logback in your project, we need a façade for the logback framework. We use slf4 as the façade in this example.

JAR files needed (Use one of the three methods mentioned below to get the jar files)

We need to have the following jar files in your Project’s build path
  1. slf4j-api.jar
  2. logback-classic.jar
  3. logback-core.jar


 Method 1: If you have Ivy setup, you can add the following dependencies (Refer to http://runningexample.blogspot.com/2014/02/Using-Ivy-for-dependency-management.html on how to setup Ivy. I STRONGLY RECOMMEND SETTING UP IVY ONCE AND FOR ALL) 

<dependency org="org.slf4j" name="slf4j-api" rev="1.7.5"  />
<dependency org="ch.qos.logback" name="logback-classic" rev="1.0.13" transitive="false"/>
<dependency org="ch.qos.logback" name="logback-core" rev="1.0.13" transitive="false"/>
 Method 2: If you have Maven setup, you can add the following dependencies (Refer to http://runningexample.blogspot.com/2011/01/apache-maven-tutorial-basic-setup.html on how to setup Maven)

<properties>
 <slf4j.version>1.7.5</slf4j.version>
 <logback.version>1.0.13</logback.version>
</properties>

<dependencies>

 <dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>${slf4j.version}</version>
 </dependency>

 <dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>${logback.version}</version>
 </dependency>

 <dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-core</artifactId>
  <version>${logback.version}</version>
 </dependency>

</dependencies>
 Method 3: Download the jars manually from:
      http://www.slf4j.org/download.html  (this is for slf4j-api.jar)
      http://logback.qos.ch/download.html (this is for the two logback jars logback-classic and logback-core)

     Now that the jar files are in the classpath, let’s write a simple servlet that says “Hello World”. 

       Using File -> New -> Servlet, create a new Servlet and name it LogbackTestServlet




    Add the following imports to the created Servlet.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

      Create a new Logger object using the following line of code at the beginning of the class
private static final Logger logger = 
LoggerFactory.getLogger(LogbackTestServlet.class);

     Add the following code to the doGet method in the Servlet
logger.debug("Inside the doGet method");
String message = "Hello World";
// Set response content type
response.setContentType("text/html");

logger.debug("Before writing the output");
// Actual logic goes here.
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");

    The Servlet class should look something like this




    We created the code to write the log statements.
    Now comes the part where we need to create the logback configuration file (where the log levels, log location etc. are specified)

     For logback to run, the ONLY file we need to have is logback.xml, that we need to place is on the classpath (In this example, we will create a new file called logback.xml at the src level of the Project Structure in the Web Project).

    Create a new file using File -> New -> File and name it logback.xml. See the below picture to see where the file is created in the Package Explorer




     Copy paste the following code into that file.  (SNIPPET 1)
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>LogbackTutorial.log</file>
    <append>true</append>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
 
 <root level="debug">
  <appender-ref ref="FILE" />
 </root>
</configuration>

    I will explain the logback configuration file entries now.

    First, the debug=’true’ in line number 2 of this file will help to find out what logback is doing (whether it is starting fine or not etc. on to the console or System.out log ). Always use this flag and it will save you a lot of time.
   We create something called an ‘Appender’ to write the log statements. Think of Appenders like handles that do that logging tasks. You create one or more Appenders and those will perform the logging based on the properties you set on them.
    In the code above, we created an Appender, named it to ‘FILE’ (we named it FILE as we are writing the logs to a File and not to the Console).  We asked the Appender to create a logfile named ‘LogbackTutorial.log’.  To specify the Pattern of the log statement, (for example: 15:15:24.273 [WebContainer : 0] DEBUG LogbackTestServlet - Inside the doGet method), we use the <pattern> tag inside the <encoder> tags.

   After the Appender is created, we set the loglevel to ‘debug’ and associate the appender.

  Now, run the Servlet by going to the URL
   http://localhost:9080/LogbackTutorial/LogbackTestServlet (change the host name/ports as needed)

   Go to the default log file location (Websphere/AppServer/Profiles/AppSrv01) or your server’s default log location and there you will find the LogbackTutorial.log created. It will have the following entries:
15:15:24.273 [WebContainer : 0] DEBUG LogbackTestServlet - Inside the doGet method
15:15:24.276 [WebContainer : 0] DEBUG LogbackTestServlet - Before writing the output

    That’s it !! Logback is up and logging your log statements. 


  BONUS:
  For writing logs to a specific location based on a Websphere name space binding. (SNIPPET 2):


  SKIP THIS SNIPPET IF YOU WANT THE DEFAULT LOG LOCATION.

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
 <insertFromJNDI env-entry-name="cell/persistent/LOGFOLDER" as="logFolder"/> 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">

  <if condition='property("logFolder").contains("log")'>
   <then>
    <file>${logFolder}/Tutorials/LogbackTutorial.log</file>
   </then>
   <else>
    <file>LogbackTutorial.log</file> 
   </else>
  </if>
    <append>true</append>
    <encoder>
      <pattern>>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
 
 <root level="debug">
  <appender-ref ref="FILE" />
 </root>
</configuration>
   The <insertFromJNDI> tag is useful if you want to write to the log file to a particular location dynamically without changing the code and redeploying. For this to happen, in Websphere, you need to create a name space binding called LOGFOLDER and set it to any folder that you want to write the logs to. In my case, on Websphere Admin console, I went to ‘Name Space bindings’ and created a binding called ‘LOGFOLDER’ and set the String value to /dev/waslogs. If you run the Servlet, the logs will be written to the location that you wanted it to. In the future, if you wanted to write the logs to a different location, without changing the application code, just change value of the name space binding and restart the server. The application should write the logs to the new file location.


   Other useful links/reads:

   http://logback.qos.ch/reasonsToSwitch.html

 Please leave your comments if you find this article helpful !




Friday, February 14, 2014

Using Ivy for dependency management - IvyDE on Eclipse Tutorial

In this running example, you will understand what Ivy dependency management is and how to use it in Eclipse (I am using Rational RSA IDE in this example which is built on the top of eclipse) using the IvyDE plugin.

In one of my previous posts, I explained a few concepts about Maven (http://runningexample.blogspot.com/2011/01/apache-maven-tutorial-basic-setup.html ). Maven has the capacity to do dependency management along with a host of other things. However, if your requirement is JUST TO MANAGE JAVA JAR DEPENDENCIES (and not worry about project/shared code management, build.xml configuration, continuous integration etc. ), Ivy might be the tool for you.

Example scenario: You are building a simple project and you need some classes from a shared Apache jar (commons or codec or whatever). To use the jar file in your project, you can manually download the jar file from a server/internet. However, if you need a different version of that jar later or if you need many jars later in the project  it makes sense to set up a system where all you need to is declare the jar files you need and that system/mechanism downloads the jars (the exact versions that you need) from a server/internet.

The key concepts in Ivy:

1.  Ivy settings file: This is where you specify the repository paths from where the jar files that you need will be downloaded. We manually create this file JUST ONCE and save it in a folder. You can have only one settings file for all your projects across your organization.
Explanation: Repository paths are the places where the jar files are stored in a particular structure according to the versions. These repositories can be the ones that are maintained by open source/commerical project servers(Apache/JBoss etc.) or the ones that are hosted by your organization.

2.  Ivy.xml:  This is the back bone of this mechanism. This class has the jar file declarations that you need in your project. This is also called the Ivy configuration file.  Each project will have it’s own ivy.xml file. We can manually create this file or we can have the IvyDE plugin generate this file for us. 

In this post, we let the plugin generate the file for us. A few sample lines from a sample file

 <dependencies>
 <dependency org="commons-validator" name="commons-validator" rev="1.3.1" transitive="false" />
                </dependencies>



In this example, to use Ivy in Eclipse, we use the IvyDE plugin.
To setup IvyDE, go to Help à Install New Software



Click on Add. Enter the following
Name: IvyDE
Click OK.


IvyDE will install as a plugin for Eclipse. You might need to restart your workspace for this to take effect.
After that, go to Window à Preferences à Ivy -à Settings



Here you can point the Ivy Settings path to your custom settings file. (A sample settings file below. You can create the below sample files and save them to your file location and point the path here),

Sample ivysettings.xml.
<ivysettings>
 <settings defaultResolver="default"/>
 <include url="ivysettings-public.xml"/>
 <include url="ivysettings-shared.xml"/>
 <include url="ivysettings-local.xml"/>
 <include url="ivysettings-main-chain.xml"/>
 <include url="ivysettings-default-chain.xml"/>
</ivysettings>
This settings file can have the pointers to multiple settings files.

Please note: In the above settings file, you can delete the entries that you don’t have. Such separation of entries will help manage local jars/ shared jars in your organization on a shared drive, jars on the internet respositories etc. If you plan to download the jars from the public respositories on the internet, you can retain just the ivysettings-public.xml entry and delete the remaining lines. Create a new file like the one shown below.
Sample ivysettings-public.xml
<?xml version="1.0" encoding="UTF-8"?>
<ivysettings>
    <resolvers>
        <chain name="public">
            
            <ibiblio name="ibiblio" m2compatible="true"/>
                                                <ibiblio name="JbossCommunityRepository" root="http://repository.jboss.org/nexus/content/groups/public/" m2compatible="true"/>
                                
        </chain>
    </resolvers>
</ivysettings>
Note: Keep both the files in the same folder.

Create a new Dynamic Web Project (or any other project as per your preference).


Right click on the Web Project, Select New à Ivy file (You may have to click on Show All Wizards checkbox at the bottom if you don’t see this option)



Enter your application specific details:




This creates the ivy.xml file for your project.

<?xml version="1.0" encoding="ISO-8859-1"?>
<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info
        organisation="com.test"
        module="Tutorial"
        status="integration">
                </info>
</ivy-module>

Let us create a sample class. Right click on the Web Project à New à Class


In this class, we do a simple validation to check if an email string is of a valid format. We use Apache’s GenericValidator class that is inside the commons-validator jar.
package com.test;
public class Sample {

                public void emailValidation()
                {
                                String email = "test@test.com";

                                if(! GenericValidator.isEmail(email))
                                {
                                                System.out.println("Not a valid email");
                                }
                               
                }
}

To resolve the Apache’s GenericValidator class,add the following lines to the ivy.xml file manually:
<dependencies>
  <dependency org="commons-validator" name="commons-validator" rev="1.3.1" transitive="false" />
</dependencies>

Save the file.

After that in any view (Package Explorer/Navigator etc.), right click on the ivy.xml file and select Add Ivy Library. This step will do the magic and download the jar file from the repository that we specified in the ivy settings file (ivysettings-public.xml to be more specific)

If you look the Package explore view (or other views), you will see that the jar file is downloaded and kept under the ivy.xml java library.


Configure the build path in this step if you are still getting Compilation errors. If not, ignore this step. Right click on the Web Project à Build Path à Add Libraries à IvyDE Managed Dependencies


Now go to the class Sample.java and press Ctrl+Space at the compilation error. This will bring up the list of suggested classes, one of which is the desired Apache’s class

That’s pretty much it. Now, just keep adding as many entries in the ivy.xml file as you need when you need any jar file.

Note: The transitive=”false” at the end of the entry in the ivy.xml file is used to prevent downloading all the dependent jars for any specific jar. If you need ALL the dependent jars downloaded, remove the transitive=”false”. But, my suggestion is use it by default and remove when necessary.

BONUS:
After you are done testing and when you export the EAR file, you might want the jars that were resolved by Ivy bundled in your EAR. To do this, right click on the Web project à Properties à Deployment Assembly



Click on Add à Java Build Path Entries


Click on Next and select ivy.xml




Click Finish.

All the Ivy jars will now become a part of WEB-INF/lib when you export the EAR file.