| Red Hat Docs > Manuals > Red Hat Web Application Framework > |
This document describes how to use log4j for Red Hat Web Application Framework development. It also describes methods and conventions that should be followed when using that package.
Each log message belongs to a particular category, in a hierarchy of categories. The idea is that you can decide how each of these categories gets handled, for example, output this category to this file, output this category to this file, this file, and the database, but only to the database if the message is of priority ERROR, etc. Categories inherit settings from their parents, making it easier to manage. For example, the category named "com.arsdigita.DbApi" is a child of the category named "com.arsdigita" and inherits all of its properties. There's a magic root category, so every category is guaranteed to have a parent.
Priorities help divide the message type space. The standard priorities that we will use are INFO, DEBUG, WARN, ERROR, and FATAL. Extending the list of priorities is possible but discouraged. Categories are the chosen way to divide the log space.
Priorities may be assigned to categories. If a category doesn't have a priority assigned to it, it inherits the priority of its parent category. All categories have an associated priority, either directly assigned or inherited.
Appenders are the objects that serialize log messages. There are many types of appenders already included in log4j: file appender, rolling file appender, remote syslog daemon appender, asynchronous appender, etc. Appenders are fairly easy to extend for special purposes, such as a database appender.
Log4j allows categories to have multiple appenders, therefore you can output log messages to several locations and using different formats (layouts).
Layouts are used to format log messages however you want. One appender can only have one layout associated with it.
Please read this short introduction to log4j if you are interested in understanding the log4j API in more detail.
Instantiate a category
Configure the category
You can instantiate categories on the fly by using the category API call 'getInstance("category-name")'. There is no need to declare them in advance. This is very convenient as it allows for faster development.
Note that categories only get instantiated once, calling the 'getInstance()' method on the same category a second time only returns a reference to the original object created. This is convenient so that the same category can be used in several places without having to pass around references.
The standard we will use for instantiating categories is the following (we will use the DB API as an example class):
// import log4j classes
import org.apache.log4j.Category;
public class DbApi
{
// define a static category variable so that it references the
// category instance of the same name as this class.
static Category cat = Category.getInstance(DbApi.class.getName());
// use it down here. we will get to this later on.
} |
In Red Hat Web Application Framework you should almost always name categories after the class that they are being used in. So the class "com.arsdigita.DbApi" should log to a category of the same name.
To configure a category you must do the following things:
Create and/or associate one or more appenders with the category.
Set the priority for the category.
Fortunately most if not all configuration information for a category can be inherited from the category's parent category. If there is no parent category, a simple configuration can be set up easily by using the BasicConfigurator API provided by log4j:
// import log4j classes
import org.apache.log4j.BasicConfigurator;
public class Init
{
public static void main(String[] args)
{
// Set up a simple configuration that logs on the console.
BasicConfigurator.configure();
}
} |
More complicated configurations can be made using configuration files that are read in at run-time.
Once you have instantiated and configured a category, you can begin logging messages. Messages must have a priority associated with them. You should use the Category API to log messages with the appropriate priority. Here is an example:
// import log4j Category class
import org.apache.log4j.Category;
public class DbApi
{
// define a static category variable so that it references the
// category instance of the same name as this class.
static Category cat = Category.getInstance(DbApi.class.getName());
public Sql(String name, String sql)
{
// this method processes standard sql statements
if(cat.isDebugEnabled())
cat.debug("Sql: name = " + name + "; sql = " + sql);
// do some processing
}
} |
Log statements are enabled if the priority of statement is greater than or equal to the priority of the category. For example, if category "com.arsdigita.DbApi" has an assigned priority of "WARN", then log messages of type WARN, ERROR, and FATAL are enabled while log messages of type INFO and DEBUG are disabled.
Configurations.
Secure logging allows an application that is handling sensitive data to make sure this data isn't logged by the system to some insecure location such as en error log (which later on might be emailed out over the public internet).
A good example is the billing system. The billing system handles credit card information which you wouldn't want emailed to a group of people or placed somewhere anyone with access to a certain box could access it. The problem is that it is not enough to not put the credit card information in the billing systems' log statements. You also have to prevent the lower level services that the billing system uses, such as the database API, from logging the credit card number as well. To do this you use the secure logging API.
You should log securely any time that you are handling sensitive information, such as credit card numbers, social security numbers, passwords, etc.
You can log messages securely by using the Log class which is part of the com.arsdigita.logging package. It provides a secure method which takes in a class that implements the Runnable interface. This means that it defines a method run. Inside this method you can put any code that you want to make sure logs securely.
// import DbApi class
import com.arsdigita.infrastructure.dbapi.*;
// import log4j Category class
import org.apache.log4j.Category;
public class Sensitive
{
// define a static category variable so that it references the
// category instance of the same name as this class.
static Category cat =
Category.getInstance(Sensitive.class.getName());
public static void main(String[] args)
{
// log some normal stuff
if(cat.isDebugEnabled())
cat.debug("Sql: name = " + sql.name +
"; sql = " + sql.sql);
// we are doing some sensitive stuff here so lets make sure
// it gets logged securely.
Log.secure(new Runnable()
{
public void run()
{
// do some stuff in here like some dbapi calls
sql.do();
}
});
}
} |
When you use the secure logging API all log messages, including those in methods that are higher in the call stack will be only be serialized by appenders which do not have the SecureLogFilter associated with them. Since this filter is associated with the Root Category and all categories inherit the default appenders from the Root Category then they won't allow secure messages through either. There will be one appender dedicated to logging secure messages (meaning it won't have the SecureLogFilter associated with it) and it will serialize to a secure location.
When adding your own appenders you must be careful to add the SecureLogFilter to it or else you'll be allowing secure messages through.