Tuesday, November 3, 2009

Log4J in a Web Application





























Chapter 18 -
Log4J
byVivek Chopra, Ben Galbraithet al.
Wrox Press 2003































Log4J in a Web Application


Now that we have the basics of Log4J we shall move on to using it in a Java web application. The developer of the application would usually add the logging statements before it is handed to the administrator. However, it is useful to know the syntax and usage of the different options available. We'll start off by using the simple example again, but this time we'll use it from a JSP page in a web application called logging. First though, we need to configure the Logger.




Configuring a Logger in a Web Application


As we saw earlier, the easiest way to configure a Logger was to use a configuration file. When using Log4J in a web application it is a good idea to set up a Logger for the entire web application at startup. This can be achieved by using an initialization servlet that loads the configuration file into the web application.



The Initialization Servlet


Here is the initialization servlet:




package wrox;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.PrintWriter;
import java.io.IOException;

import org.apache.log4j.PropertyConfigurator;

public class Log4JInitServlet extends HttpServlet {



The application-wide configuration happens in the init() method. This method is called as the servlet is first loaded by Tomcat, making the Logger available from startup:




// Initialize the servlet by loading the Log4J properties file
public void init() throws ServletException {


The properties file will be stored in the $CATALINA_HOME/webapps/logging/WEB-INF/classes directory of our web application. We need to get the path to the properties file before we can load it, so we get the full path to the web application on the filesystem and then append the name of the file. In this case, the name of the file comes from the properties initialization parameter specified in web.xml. This allows us to specify the location of the properties file outside of the application's code:




// First we get the fully qualified path to the web application
String path = getServletContext().getRealPath("/");
String properties = path + getInitParameter("properties");


The final step is to call the configure () method to make the Logger available for all the other files in the application:




// Next we set the properties for all the servlets and JSP
// pages in this web application
PropertyConfigurator.configure(properties);
}


The final methods are required to be in any servlet:




// The doPost() method forwards all requests to the doGet() method
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
doGet(req, resp);
}

// The doGet() method informs users of the purpose of this servlet
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
PrintWriter out = resp.getWriter();

out.println("Initialization servlet for Log4J");
}

public void destroy() {}
}


Compile this class and place it in $CATALINA_HOME/webapps/logging/WEB-INF/classes/wrox.





web.xml


Next we need a web.xml file for our web application so that we can specify the location of our properties file:





<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<servlet>
<servlet-name>log4Jinit</servlet-name>
<servlet-class>wrox.Log4JInitServlet</servlet-class>
<init-param>
<param-name>properties</param-name>
<param-value>WEB-INF/classes/log4j.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>


The <load-on-startup> element loads the servlet when Tomcat is started. This is exactly what we want as we need the Logger to be available to the web application as soon as Tomcat starts.








Logging To Files


In this section we will start with a simple file example, then show the following formats that are made available to Log4J:





  • HTMLFormat





  • PatternFormat





Simple File


The configuration file for this first example is very similar to the one we saw before. However, this time we'll log to the $CATALINA_HOME/logs directory:




# A simple logger
log4j.logger.wrox.simple=DEBUG, simple

# This simple example will log to a file
log4j.appender.simple=org.apache.log4j.FileAppender
log4j.appender.simple.File=<CATALINA_HOME>/logs/log4jsimple.log

# We will use a simple layout for this example
log4j.appender.simple.layout=org.apache.log4j.SimpleLayout


Note that Log4j does not consider <CATALINA_HOME> to be a shortcut; it is merely a neat piece of notation for us. Be sure and provide the full path in your files.


Save this file as log4j.properties in logging/WEB-INF/classes.



Finally, we need a JSP page to use the Logger (logsimple.jsp):




<%@ page import="org.apache.log4j.Logger" %>
<html>
<head><title>Logging with Log4J</title></head>

<%
Logger logger = Logger.getLogger("wrox.simple");

logger.debug("A debug message");
logger.info("An info message");
logger.warn("A warn message");
logger.error("An error message");
logger.fatal("A fatal message");
%>

<body>
<h1>Logging completed</h1>
</body>
<html>


Place this in $CATALINA_HOME/webapps/logging. Now, point your browser at http://localhost:8080/logging/logsimple.jsp. You should see the confirmation message. This should have set off the Logger and written the logging messages to $CATALINA_HOME/logs/log4jsimple.log.





HTML


Now we will look at a different kind of Layout, the HTMLLayout. This Layout formats the logging information into HTML tables that can be customized to fit most needs. We shall send the logging information to an HTML file in the logging web application so that we can view it online. However, it would be possible to put this file under a password-protected context, such as admin in Tomcat 4.1.3 onwards.


Here is the configuration file:




# An HTML logger
log4j.logger.wrox.html=DEBUG, html

# This simple example will log to an HTML file
log4j.appender.html=org.apache.log4j.FileAppender
log4j.appender.html.File=<CATALINA_HOME>/webapps/logging/log.html

# This line stops additional log sessions being appended to the end
# of the file
log4j.appender.html.Append=false

# We will use an HTML Layout for this example
log4j.appender.html.layout=org.apache.log4j.HTMLLayout

# This line shows where in the servlet/JSP page the logging
# statement is placed
log4j.appender.html.layout.LocationInfo=true

# The Title parameter sets the HTML <title> tag for the HTML page
log4j.appender.html.layout.Title=Wrox Log



The JSP page for this example, loghtml.jsp, has only one change:




<%@ page import="org.apache.log4j.Logger" %>
<html>
<head><title>Logging with Log4J</title></head>

<%
Logger logger = Logger.getLogger("wrox.html");

logger.debug("A debug message");
logger.info("An info message");
logger.warn("A warn message");
logger.error("An error message");
logger.fatal("A fatal message");
%>

<body>
<h1>Logging completed</h1>
</body>
<html>


Access this page at http://localhost:8080/logging/loghtml.jsp and then view http://localhost:8080/logging/log.html. You should see something similar to the following:








Pattern


The final Layout that we will look at is the PatternLayout. This Layout allows us to specify a custom format for the logging messages. The format can be configured in a number of ways with various format modifiers and conversion characters playing their part. We saw one example earlier, but here is the example we will be using in this section:




%-5p : %m %d{dd-MM-yy HH:mm:ss}%n



We have already seen the %m and %n conversion characters, as well as a different version of the %d character. This time we have specified a custom format for the date between the {} markers. The initial %-5p section can be further broken down into the -5 and %p blocks. %p displays a string representation of the priority (a throwback to earlier versions of Log4J when a Priority did the same job as a Level), that is, 'DEBUG', 'WARN', and so on. -5 is a format modifier that says 'right pad with spaces if the priority is less than 5 characters long'.




A full list of the format modifiers and conversion characters is available in the Log4J documentation: http://jakarta.apache.org/log4j/docs/api/org/apache/log4j/PatternLayout.html.



We specify this pattern for our file as follows:




# A pattern logger
log4j.logger.wrox.pattern=DEBUG, pattern

# This simple example will log to a custom format file
log4j.appender.pattern=org.apache.log4j.FileAppender
log4j.appender.pattern.File=<CATALINA_HOME>/logs/log4jpattern.log

# We will use a custom layout for this example
log4j.appender.pattern.layout=org.apache.log4j.PatternLayout
# Here we set our custom pattern
log4j.appender.pattern.layout.ConversionPattern=%-5p : %m %d{dd-MM-yy HH:mm:ss}%n


The only line that has changed since our last JSP page is:




Logger logger = Logger.getLogger("wrox.pattern");


Point your browser to http://localhost:8080/logger/logpattern.jsp and then view $CATALINA_HOME/logs/log4jpattern.log. The log entries should look something like this:




DEBUG : A debug message 06-08-02 01:54:21
INFO : An info message 06-08-02 01:54:21
WARN : A warn message 06-08-02 01:54:21
ERROR : An error message 06-08-02 01:54:21
FATAL : A fatal message 06-08-02 01:54:21








Logging To the Console


Log4J allows us to log to other destinations other than files. In this section we shall send logging messages to the console. This allows an application to instantly notify the administrator of any problems that occur. To log to the console we need to attach a ConsoleAppender to our Logger:




# A console Logger
log4j.logger.wrox.console=DEBUG, console

# This simple example will log to the console
log4j.appender.console=org.apache.log4j.ConsoleAppender
# Set the target to Sysytem.out
log4j.appender.console.Target=System.out

# We will use the simple format for the console example
log4j.appender.console.layout=org.apache.log4j.SimpleLayout


The Target parameter must be one of System.out (the default) or System.err.ConsoleAppenders accept the same Layouts as FileAppenders: in this case we are using a SimpleLayout.


Again, our JSP page (logconsole.jsp) only needs one change:




Logger logger = Logger.getLogger("wrox.console");


Accessing http://localhost:8080/logging/logconsole.jsp will cause the log messages to be logged to Tomcat's console:









Logging To Multiple Destinations


One of the more useful features of Log4J is the ability to log to several destinations at once. This can allow us to send serious error messages to a location where they will be instantly noticed, for example the console, while other more routine messages can be sent to a regular logging file.


We shall look at the following examples in this section:




  • Logging FATAL events to the console and all events to a file




  • Logging FATAL events to the console and all events to Unix's syslog system Logger




  • Logging FATAL events to the console and FATAL and ERROR level events to WinNT's Event Logger





Console and a File


Our first example will log all FATAL level events to the console so that an administrator will be instantly alerted to any serious problems with an application. Also, we still need a record of other events (and a permanent record of FATAL events) so we will log all events to a file.



First we assign two Appenders to the wrox.multifile Logger: fatalconsole and errorfile. We also set the default logging level:




# A multi-destination logger
# FATAL errors will go to the console
# All errors will go to a file
log4j.logger.wrox.multifile=DEBUG, fatalconsole, errorfile


Now we set the fatalconsole Appender's type to ConsoleAppender, set the target to System.out, and set the logging level to FATAL, using the Threshold parameter of the Appender:




# All fatal messages for this example go to the console
log4j.appender.fatalconsole=org.apache.log4j.ConsoleAppender
log4j.appender.fatalconsole.Target=System.out
log4j.appender.fatalconsole.Threshold=FATAL


Next we set up the file where all log messages will be sent. We also set the logging level to DEBUG:




# All messages go to a file
log4j.appender.errorfile=org.apache.log4j.FileAppender
log4j.appender.errorfile.File=<CATALINA_HOME>/logs/log4jerrors.log
log4j.appender.errorfile.Threshold=DEBUG

# We will use simple layouts for both the console and the file
log4j.appender.fatalconsole.layout=org.apache.log4j.SimpleLayout
log4j.appender.errorfile.layout=org.apache.log4j.SimpleLayout


Now all we need to do to set up this example is change the same line of code in our JSP page (fatalconsole.jsp):




Logger logger = Logger.getLogger("wrox.multifile");


The logging method calls remain the same. Now each time a logging message is logged by the wrox.multifile Logger it is compared to the level of each Appender and only sent to its destination if it is less than or equal to this level.


Now if you point a browser at http://localhost:8080/logging/fatalconsole.jsp you should see the following in the Tomcat console:






Now look at $CATALINA_HOME/logs/log4jerrors.jsp:




DEBUG - A debug message
INFO - An info message
WARN - A warn message
ERROR - An error message
FATAL - A fatal message


As you can see, Log4J has only sent FATAL level messages to the console, but has sent all messages to the regular logging file.





Console and Syslog


The Unix syslog program is an integral part of all Unix systems. It was designed with two important functions in mind: liberating programmers from having to write log files and putting logging in the hands of the administrator.


Log4J provides the SyslogAppender for working with syslog. It needs a bit more configuring than the other Appenders we have seen before, but it is still relatively straightforward. In this example, we will again send FATAL level events to the console, but this time we will send all events to syslog as user-level events:




# A multi-destination logger
# FATAL errors will go to the console
# All errors will go to syslog
log4j.logger.wrox.multisyslog=DEBUG, fatalconsolesys, syslog

# All fatal messages for this example go to the console
log4j.appender.fatalconsolesys=org.apache.log4j.ConsoleAppender
log4j.appender.fatalconsolesys.Target=System.out
log4j.appender.fatalconsolesys.Threshold=FATAL


Here is the SyslogAppender configuration. After setting the syslog Appender type, we set its logging level. We then need to set the syslog facility using the SyslogAppender's Facility parameter. syslog uses facilities alongside a severity index to decide where the logging message should be sent, much like Log4J does with the logging level and Appender name. Many system functions have their own facility, for example sendmail has the mail facility and the kernel has the kern facility. In our example we will use the user facility that is designed for user-level processes.


The final piece of configuration for a SyslogAppender is the SyslogHost parameter. This should specify the central logging host of your network, or localhost if you are logging to the local machine:




# All messages go to syslog
log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
log4j.appender.syslog.Threshold=DEBUG
log4j.appender.syslog.Facility=USER
log4j.appender.syslog.SyslogHost=localhost

# We will use simple layouts for both the console and syslog
log4j.appender.fatalconsolesys.layout=org.apache.log4j.SimpleLayout
log4j.appender.syslog.layout=org.apache.log4j.SimpleLayout








Important 

Note that if syslog is running on a Linux box over a network, you will have to start it with the -r switch to enable network logging. This can be set in the /etc/init.d/syslog file. Run /etc/init.d/syslog restart to restart syslog.



Again, there is only one change to the JSP file:




Logger logger = Logger.getLogger("wrox.multisyslog");


Point a browser at http://localhost:8080/logging/fatalsyslog.jsp and then examine the log file where syslog sends all user-level log messages. You should see the messages listed in syslog's format, which should look like the following:




Aug 5 16:48:40 matthew user: DEBUG - A debug message





Console and WinNT Event Logger


Our final example will be logging messages to the console and WinNT's Event Logger. The Event Logger can be found under Start | Settings | Control Panel | Administrative Tools | Event Viewer. This is where WinNT logs its system messages. These messages include serious errors in applications like Internet Explorer, as well as less important messages such as print job completion messages. WinNT messages have error types that are similar in concept to Log4J Levels. For example, WinNT Errors correspond to Log4J's FATAL and ERROR levels.


In this example, we will again send only FATAL errors to the console, but this time only FATAL and ERROR messages will be sent to the Event Logger:




# A multi-destination logger
# FATAL errors will go to the console
# FATAL and ERROR will go to the NT Event Logger
log4j.logger.wrox.multiwinnt=DEBUG, winntconsole, winntlogger

# All fatal messages for this example go to the console
log4j.appender.winntconsole=org.apache.log4j.ConsoleAppender
log4j.appender.winntconsole.Target=System.out
log4j.appender.winntconsole.Threshold=FATAL


Here is where we configure the NTEventLogAppender that allows us to write to the Event Logger:




# All fatal and error messages go to the WinNT logger
log4j.appender.winntlogger=org.apache.log4j.nt.NTEventLogAppender
log4j.appender.winntlogger.Threshold=ERROR


The Event Logger displays the source of the message in its window. By default, Log4J messages are given the name 'log4j'. This isn't very useful if we have a number of applications using Log4J, so we will add our own source name:




# We will set the Source parameter so we know which application
# this message came from
log4j.appender.winntlogger.Source=Wrox Logging



The Event Logger can take the same Layouts as other Layouts, but only SimpleLayout and PatternLayout make sense in this situation (as we shall see when we examine the log message):




# We will use a simple layout for both the console and the Event Logger
log4j.appender.winntconsole.layout=org.apache.log4j.SimpleLayout
log4j.appender.winntlogger.layout=org.apache.log4j.SimpleLayout


Here's that line of code again:




Logger logger = Logger.getLogger("wrox.multiwinnt");


Viewing the page http://localhost:8080/logging/fatalwinnt.jsp will send the usual FATAL message to the console as well as an ERROR message and a FATAL message to the Event Logger:





Both messages are logged as WinNT errors, but are given different categories. Double-clicking on a log message will show its properties:






The Description box explains why the SimpleLayout and PatternLayout are the only sensible options for this kind of Appender: putting HTML tables into this box would make it almost unreadable.





















No comments: