Community Edition nuances and getting the most from Log4j
Log4j is an open source library for logging services from the Apache Software Foundation. Log4j is widely used in the open source community, including some big names like Apache Geronimo, JBoss, etc. Log4j's architecture revolves around three main concepts: Loggers, Appenders, and Layouts.
Applications first call Logger objects to initiate the logging of a message. When given a message to log, loggers generate LogEvent objects to wrap the given message. The loggers then pass on the LogEvents objects to their associated Appenders. The Appenders send the information contained by the LogEvents to specified output destinations. For example, a ConsoleAppender writes the information to System.out, and a FileApppender writes it to a log file. Before sending the information to the destination, the Appenders can use Layouts to create a text representation of the information in a desired format. For example, Appenders can use XMLLayout that formats the log messages as XML strings.
The LoggingEvents are assigned a level that indicates its priority. The default levels are (from highest to lowest):
- OFF
- FATAL
- ERROR,
- WARN
- INFO
- DEBUG
- ALL
Loggers and Appenders are assigned a level. If a LogEvent has the log
level as WARN and the Appender's log level
is ERROR, the Appender will not write the
LogEvent. In this way, you can control the amount of log output.
All Loggers in Log4j have a name. Log4j organizes Logger instances in a hierarchical, tree structure, according to the names, as in the packaging namespace in the Java language. The Log4j documentation says
"A Logger is said to be an ancestor of another Logger if its name followed by a dot is a prefix of the descendant Logger name. A Logger is said to be a parent of a child Logger if there are no ancestors between itself and the descendant Logger".
For example, a Logger named com.ibm is said
to be the child of the com Logger. The
com.ibm.wasce Logger is the child of the
com.ibm Logger and the grandchild of the
com Logger. If a Logger is not explicitly
assigned a level, it uses the level of its closest ancestor that has
been assigned a level. Loggers inherit Appenders from their ancestors,
although they can also be configured to use only Appenders that are
directly assigned to them.
You configure all the Loggers, Appenders and Layouts in the
log4j.properties or
log4j.xml file. Log4j libraries first look
for log4j.xml file and then
log4j.properties file in the classpath to
configure the logging service.
From the above discussion, you might think that Log4j is similar to java.util.logging. Of course, they are conceptually the same, but Log4j can do more than java.util. Handlers in java.util.logging perform the same tasks as Appenders in Log4j. The Formatters in java.util.logging perform the same task as Layouts in Log4j. However, java.util.logging has only four Handlers, whereas Log4j has a dozen Appenders. In addition, Log4j offers a rich set of Layouts where java.util.logging offers only SimpleFormatter and XMLFormatter. Some of the Appenders available in Log4j are:
- FileAppender: Appends log events to a file.
- RollingFileAppender: Extends FileAppender to backup the log files when they reach a certain size.
- ConsoleAppender: Appends log events to System.out or System.err using a layout specified by the user. The default target is System.out
- SocketAppender: Sends LoggingEvent objects to a remote a log server, usually a SocketNode.
- JMSAppender: A simple Appender that publishes events to a JMS Topic. The events are serialized and transmitted as the JMS message type ObjectMessage.
- NTEventLogAppender: Append to the NT event log system.
Log4j includes XMLLayout, SimpleLayout, TTCCLayout, HTMLLayout. For descriptions of various Appenders and Layouts, see the Log4j documentation.
In this section, we show how to use Log4j in applications deployed on Community Edition. Community Edition, by default, uses the following log4j configuration files:
-
<wasce_home>/var/log/server-log4j.properties: This file configures Appenders and Layouts for the server components as well as applications deployed on the server. This file configures a ConsoleAppender and a RollingFileAppender. The ConsoleAppender logs toSystem.outand the RollingFileAppender logs to the<wasce_home>/var/log/server.logfile. The default logging isWARN. It also overrides log levels for various Loggers in the properties file. -
<wasce_home>/var/log/deployer-log4j.properties: This file configures logging service for the deployer. The deployer component is invoked when any applications are deployed using the command line deployer.
-
<wasce_home>/var/log/client-log4.properties: This file configures logging service for a Java EE application client.
In this section, we will configure Log4j in the following ways
- Using the default server Log4j configuration
- Setting up Log4j at the application level
Using the default server Log4j configuration
As described earlier, server components as well as applications
deployed on Community Edition use the configuration in
<wasce_home>/var/log/server-log4j.properties.
We will use the modified EMPDemo sample that has Log4j for logging. It
also uses the default configuration available in Community Edition.
The sample contains the same EMPDemo servlet modified for logging
using Log4j API. Listing 9 shows the modified EMPDemo, with the
logging statements marked in bold. The servlet obtains the
Logger and sets the log level to ALL.
Listing 9. Modified EMPDemo using the Log4j API
Logger logger = Logger.getLogger(EMPDemo.class.getName());
logger.setLevel(Level.ALL);
Connection con = null;
Statement stmt = null;
PrintWriter out = response.getWriter();
logger.info("Created the PrintWriter on the Response object");
try {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:comp/env");
logger.info("Got environment context: " +envContext);
DataSource ds = (DataSource)envContext.lookup("jdbc/DataSource");
logger.info("Got DataSource: " +ds.toString());
con = ds.getConnection();
logger.info("Got Connection: " +con.toString() +"\n");
stmt = con.createStatement();
logger.info("Created the statement: " +stmt);
ResultSet rs = stmt.executeQuery("SELECT * FROM EMPLOYEE");
logger.info("Gto the result set: " +rs);
logger.info("Table EMP after SELECT:");
out.println("Your EMP table contains the following entries:<BR>");
out.println("<table>");
out.println("<tr>");
out.println("<th>Empno</th>");
out.println("<th>Name</th>");
out.println("<th>Job</th>");
out.println("<th>Manager</th>");
out.println("<th>Salary</th>");
out.println("<th>Commission</th>");
out.println("<th>Deptno</th>");
out.println("</tr>");
while (rs.next()) {
String emp = rs.getString("EMPNO");
String name = rs.getString("ENAME");
String job = rs.getString("JOB");
String mgr = rs.getString("MGR");
String sal = rs.getString("SAL");
String comm = rs.getString("COMM");
String dept = rs.getString("DEPTNO");
out.println("<tr>");
out.println("<td>"+emp+"</td>");
out.println("<td>"+name+"</td>");
out.println("<td>"+job+"</td>");
out.println("<td>"+mgr+"</td>");
out.println("<td>"+sal+"</td>");
out.println("<td>"+comm+"</td>");
out.println("<td>"+dept+"</td>");
out.println("</tr>");
logger.info(emp + " " + name + " " + job);
logger.info(" " + mgr + " " + dept);
out.println("</table>");
rs.close();
stmt.close();
con.close();
logger.debug("Debug");
logger.info("Info");
logger.warn("Warn");
logger.error("Error");
logger.fatal("Fatal");
}
catch(java.lang.Exception e) {
e.printStackTrace();
logger.fatal(e.getClass().getName());
logger.fatal(e.getMessage());
}
|
Follow these steps to deploy and run the application:
- From the sample download, unzip the
EMPDemo sample into a directory. The war file name is
EMPdemo-Log4jLogging1.war. - Deploy the
application.
<wasce_home>/bin>deploy --user system --password manager deploy Log4jLogging1.war. - Access the EMPDemo servlet by pointing your browser to
http://localhost:8080/EMPdemo-log4j1/EMPDemo.
The servlet shows the rows from the employee table on the browser
window.
Observe the console window to note that only the log messages shown in
Listing 10 are logged. This is because the ConsoleAppender’s log level
is WARN by default (in the
server-log4j.properties). So, the log
messages whose log level is WARN or above
are logged in the console.
Listing 10. Log messages in the console
14:13:31,687 WARN [EMPDemo] Warn 14:13:31,687 ERROR [EMPDemo] Error 14:13:31,687 FATAL [EMPDemo] Fatal |
Open the
<wasce_home>/var/log/server.log
file to observe that all the log messages have been logged. This is
because the log level of the FileAppender is
TRACE
by default. Hence it has logged all the messages. Listing 11
shows the logged messages:
Listing 11. server.log file
14:13:31,687 INFO [EMPDemo] Created the PrintWriter on the Response object 14:13:31,687 INFO [EMPDemo] Got environment context: org.apache.xbean.naming.context.ImmutableContext$NestedImmutableContext@5b9e5b9e 14:13:31,687 INFO [EMPDemo] Got DataSource: org.tranql.connector.jdbc.DataSource@8660866 14:13:31,687 INFO [EMPDemo] Got Connection: org.tranql.connector.jdbc.ConnectionHandle@a1e0a1e 14:13:31,687 INFO [EMPDemo] Created the statement: org.tranql.connector.jdbc.StatementHandle@c980c98 14:13:31,687 INFO [EMPDemo] Got the result set: org.tranql.connector.jdbc.ResultSetHandle@11221122 14:13:31,687 INFO [EMPDemo] Table EMP after SELECT: 14:13:31,687 INFO [EMPDemo] 1 PHANI SSE 14:13:31,687 INFO [EMPDemo] NIKHIL 100 14:13:31,687 INFO [EMPDemo] 2 JOE SSE 14:13:31,687 INFO [EMPDemo] NIKHIL 100 14:13:31,687 INFO [EMPDemo] 3 JOHN SSE 14:13:31,687 INFO [EMPDemo] BOB 200 14:13:31,687 DEBUG [EMPDemo] Debug 14:13:31,687 INFO [EMPDemo] Info 14:13:31,687 WARN [EMPDemo] Warn 14:13:31,687 ERROR [EMPDemo] Error 14:13:31,687 FATAL [EMPDemo] Fatal |
Setting up Log4j at the application level
Sometimes you might want to configure Log4j at the application level,
ignoring the server-level configuration. You can do this by packaging
the Log4j libraries and log4j.properties
configuration with the application itself, and deploying the
application. However, there is a hitch here -- Community Edition uses
a “parent first” classloader policy. This means, if the classes are
available in parent classloader, they will be loaded from the parent
classloader. Since the Log4j is used by server components and is
loaded by a classloader (which is higher in the classloader hierarchy)
Log4j configured at the server level is always used even if Log4j
libraries and log4.properties file are
packaged with the application.
To resolve this issue, we need to hide Log4j from the parent
classloader and make it always load from the application classloader.
To hide Loh4j, you specify
<hidden-classes> for the
Log4j classes in the Community Edition application deployment plan.
For information about other various classloader policies available in
Community Edition, see this
technote.
This
section illustrates how to configure Log4j at the application level.
We use the same sample as in the previous section. However, we modify
the Community Edition application deployment plan
(geronimo-web.xml) to hide the Log4j
classes. Listing 12 shows the modified Geronimo deployment plan , with
the relevant portion of the plan marked in bold.
Listing 12. Modified geronimo-web.xml deployment plan
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<web:web-app xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2"
xmlns:conn="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2"
xmlns:name="http://geronimo.apache.org/xml/ns/naming-1.2"
xmlns:ejb="http://openejb.apache.org/xml/ns/openejb-jar-2.2"
xmlns:pkgen="http://openejb.apache.org/xml/ns/pkgen-2.1"
xmlns:app="http://geronimo.apache.org/xml/ns/j2ee/application-2.0"
xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0"
xmlns:web="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1"
xmlns:pers="http://java.sun.com/xml/ns/persistence"
xmlns:client="http://geronimo.apache.org/xml/ns/j2ee/application-client-2.0">
<dep:environment>
<dep:moduleId>
<dep:groupId>com.ibm.wasce.samples</dep:groupId>
<dep:artifactId>EMPdemo-log4j-2</dep:artifactId>
<dep:version>2.1.0.0</dep:version>
<dep:type>war</dep:type>
</dep:moduleId>
<dependencies>
<dependency>
<groupId>console.dbpool</groupId>
<artifactId>EMPLOYEE_DS</artifactId>
</dependency>
</dependencies>
<hidden-classes>
<filter>org.apache.log4j</filter>
</hidden-classes>
</dep:environment>
<web:context-root>/EMPdemo-log4j2</web:context-root>
<name:resource-ref>
<name:ref-name>jdbc/DataSource</name:ref-name>
<name:resource-link>EMPLOYEE_DS</name:resource-link>
</name:resource-ref>
</web:web-app>
|
Also, we copy the log4j.properties file into
the WEB-INF/classes directory and copy
log4j-1.2.14.jar in to the
WEB-INF/lib directory of the EMPDemo
application. Listing 13 shows the
log4j.properties file:
Listing 13. The log4j.properties file
log4j.logger.com.ibm.sample=debug,applog
log4j.appender.applog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.applog.File=C:/temp/applevellogging/Log4J/applog1.log
log4j.appender.applog.layout=org.apache.log4j.PatternLayout
log4j.appender.applog.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c{1}] %m%n
|
This properties file defines a Logger for the servlet, and sets the Log
level to DEBUG. But the servlet overrides
this value to make it ALL. It also defines
a DailyRollingFileAppender that logs
messages to the
C:/temp/applevellogging/Log4J/applog1.log
file. The Layout used by the Appender is the standard Community
Edition layout used in the
server-log4j.properties file. Follow these
steps to deploy the application.
- From the sample download, unzip the
modified Web application. The WAR file name is
EMPdemo-Log4jLogging2.war. - Deploy the
application.
<wasce_home>/bin>deploy --user system --password manager deploy EMPdemo-Log4jLogging2.war - Access the EMPDemo servlet by pointing a browser to
http://localhost:8080/EMPdemo-log4j2/EMPDemo.
The servlet shows the rows from the employee table in the
browser.
Note that no messages have been logged in server console or in the
server.log file, because we have overridden
the server level Log4j configuration and configured it at the
application level. Moreover, the application creates the
C:/temp/applevellogging/Log4J/applog1.log
file and logs the messages in this file. Listing 14 shows the messages
logged in the file:
Listing 14. Messages in the applog1.log file
15:18:36,078 INFO [EMPDemo] Created the PrintWriter on the Response object 15:18:36,093 INFO [EMPDemo] Got environment context: org.apache.xbean.naming.context.ImmutableContext$NestedImmutableContext@d760d76 15:18:36,093 INFO [EMPDemo] Got DataSource: org.tranql.connector.jdbc.DataSource@32a232a2 15:18:36,109 INFO [EMPDemo] Got Connection: org.tranql.connector.jdbc.ConnectionHandle@61e461e4 15:18:36,109 INFO [EMPDemo] Created the statement: org.tranql.connector.jdbc.StatementHandle@67206720 15:18:36,109 INFO [EMPDemo] Got the result set: org.tranql.connector.jdbc.ResultSetHandle@6f9a6f9a 15:18:36,109 INFO [EMPDemo] Table EMP after SELECT: 15:18:36,109 INFO [EMPDemo] 1 PHANI SSE 15:18:36,109 INFO [EMPDemo] NIKHIL 100 15:18:36,109 INFO [EMPDemo] 2 JOE SSE 15:18:36,109 INFO [EMPDemo] NIKHIL 100 15:18:36,109 INFO [EMPDemo] 3 JOHN SSE 15:18:36,109 INFO [EMPDemo] BOB 200 15:18:36,109 DEBUG [EMPDemo] Debug 15:18:36,109 INFO [EMPDemo] Info 15:18:36,109 WARN [EMPDemo] Warn 15:18:36,109 ERROR [EMPDemo] Error 15:18:36,109 FATAL [EMPDemo] Fatal |





