Comments
Richard Davies wrote: The UK has a good crop of technology pioneers in cloud computing - for example ElasticHosts, FlexiScale, Flexiant, OnApp - and also some strong government initiatives such as G-Cloud. We will have to see whether this kind of technical leadership converts into swift mass-market adoption or not.
Cloud Expo on Google News


2008 West
DIAMOND SPONSOR:
Data Direct
SOA, WOA and Cloud Computing: The New Frontier for Data Services
PLATINUM SPONSORS:
Red Hat
The Opening of Virtualization
GOLD SPONSORS:
Appsense
User Environment Management – The Third Layer of the Desktop
Cordys
Cloud Computing for Business Agility
EMC
CMIS: A Multi-Vendor Proposal for a Service-Based Content Management Interoperability Standard
Freedom OSS
Practical SOA” Max Yankelevich
Intel
Architecting an Enterprise Service Router (ESR) – A Cost-Effective Way to Scale SOA Across the Enterprise
Sensedia
Return on Assests: Bringing Visibility to your SOA Strategy
Symantec
Managing Hybrid Endpoint Environments
VMWare
Game-Changing Technology for Enterprise Clouds and Applications
Click For 2008 West
Event Webcasts

2008 West
PLATINUM SPONSORS:
Appcelerator
Get ‘Rich’ Quick: Rapid Prototyping for RIA with ZERO Server Code
Keynote Systems
Designing for and Managing Performance in the New Frontier of Rich Internet Applications
GOLD SPONSORS:
ICEsoft
How Can AJAX Improve Homeland Security?
Isomorphic
Beyond Widgets: What a RIA Platform Should Offer
Oracle
REAs: Rich Enterprise Applications
Click For 2008 Event Webcasts
SYS-CON.TV
Top Links You Must Click On


Extending the J2SE 1.4 Logging Package - Monitoring made easy
Extending the J2SE 1.4 Logging Package - Monitoring made easy

The most basic way to capture these elements of interest is through application logs. Most Java-based production systems have them in some form, and most of them probably implement a custom API or use one of a handful of third-party packages that may or may not be cross-compatible. Out comes java.util.logging in the new Java 2 Platform, Standard Edition (J2SE) v1.4. Developed collaboratively with input from several key contributors (see “JSRs: Java Specification Requests” at http://jcp.org/jsr/detail/47.jsp for details), this package can be used as is, extended for additional functionality, and in conjunction with enterprise application services.

How does it work out of the box? What are its limitations and how easy is it to extend its capabilities? I’ll discuss these issues, plus show how to add database-level logging to the package’s framework.

J2SE Logging: Out of the Box
Let’s start by discussing what can be done with the basic logging package. I’ll give a general overview, but I suggest you take a trip to http://java.sun.com/ for a more complete picture.

Here’s how it works: the package provides an API for producing a LogRecord. This record is what the logs are populated with; it contains such properties as the datetime, log level (there are seven currently, from FINEST all the way to SEVERE), and, of course, the message itself.

Formatting and output for log records are done through Formatter and Handler classes. Formatters determine which format the LogRecord will be in, currently either plaintext or XML (the default). Handlers define how the logs are exported (through a file, a socket, to the console, or in-memory). To set up a Handler class to point to a file called “mylog.xml” that writes each LogRecord as an XML message, you’d use the FileHandler class and set its formatter to an instance of an XMLFormatter, as follows:

FileHandler flhandler = new FileHandler( "mylog.xml" );
XMLFormatter xmlfrmtr = new XMLFormatter();
flhandler.setFormatter( xmlfrmtr );

To integrate logging into an application you must obtain a static instance of the Logger class. Through this class a user will define the output type, specify a log format, and publish the individual log records.

To publish an informational level message in the “mylog.xml” file, you first need to retrieve a Logger for your subsystem (commonly the class itself), then attach the handler to it (in our case it’s the FileHandler). Now you’re ready to generate messages.

private static Logger logger =
Logger.getLogger("com.mylogger.TestDriver");
logger.addHandler( flhandler );
logger.info("TestDriver(): Testing INFO");

For the logger.info() method, you can also use the more generic logger.log(), which takes Level as a param, as well as the message:

logger.log(Level.INFO, "TestDriver():
Testing INFO").

The code above will produce an XML <record> message with each field in the LogRecord shown as a separate tag:

<record>
<date>2001-11-20T20:21:03</date>
<millis>1006305663460</millis>
<sequence>0</sequence>
<logger>com.mylogger.TestDriver</logger>
<level>INFO</level>
<class>com.mylogger.TestDriverFile
</class>
<method>main</method>
<thread>10</thread>
<message>TestDriver(): Testing INFO</message>
</record>

A complete example of how logging is added to an application appears in Listing 1. (Listings 1–5 can be downloaded from www.sys-con.com/java/sourcec.cfm.)

What isn’t obvious in this example is the presence of a LogManager. This class contains default configurations for loggers and their supporting classes (read in from a logging.properties file), and is a single global entity shared by each logger within the application. It also maintains a list of global handlers to which, by default, each logger sends its output.

Extending Its Capabilities
As with any thorough framework, the logging package can be extended to increase its capabilities and provide custom functionality on how to format and generate logs. Perhaps e-mailing levels of SEVERE to administrators or providing the ability for other applications to query and report on the logs is necessary. In the latter case we can replace the common log file with a simple database table. I’ll demonstrate how to do this by extending both the Handler and Formatter classes, giving total control of where the database is located and how to write a log record to it.

First, it’s important to understand the class diagram, which shows what we’ll be extending. Referring to Figure 1, you’ll notice an abstract formatter and handler. The formatter decides only how the LogRecord will look. It works on the individual properties of the LogRecord and creates a single string (usually) to be returned that reflects a complete record in a particular format. The handler, which calls the formatter for a LogRecord, worries only about what to do with the outcome of that format’s record when returned.

Take the common FileHandler, for instance. This class specifies the filename to which logs are written, then for each incoming LogRecord calls the default XMLFormatter to retrieve the entire record as an XML document and write it out to the log. With the ease of this framework, it’s only a small change to switch from outputting the XML document from a file to the console, or using a plaintext representation instead of XML.

Database Handler and Customized Formatters
Providing database support to this logging framework will involve implementing only two classes: a formatter to build the SQL necessary to insert an entire LogRecord into the database, and a handler to do the transactions. We’ll also include a third class, another formatter that will split each field in the log record into separate database fields.

As shown in Figure 1, the three new classes are DBHandler, TblXMLFormatter, and TblFieldFormatter. How do they work together? The user instantiates a DBHandler and passes the JDBC driver being used and the appropriate connect string. In this case we’re using MySQL:

DBHandler dbhandler = new DBHandler("org.gjt.mm.mysql.Driver",
"jdbc:mysql://localhost/logger?
user=jim&password=changeme");

Then, instead of using the XmlFormatter, a new TblXmlFormatter is instantiated and attached to the DBHandler. This will return column names and LogRecord values in a format suitable for inclusion in a database transaction.

TblXMLFormatter tblxmlfrmtr = new TblXMLFormatter();
dbhandler.setFormatter( tblxmlfrmtr );

The handler will also write a default table name, which can be changed. The schema of this table depends on which formatter is used. If the user wants the log to consist of XML documents, the TblXmlFormatter is used; this will populate a single column with the entire log (see Figure 2, Logtable). If individual columns have to be populated, the TblFieldFormatter will do the job (see Figure 2, logtable_detail). This class inserts each field of the LogRecord into a separate column and can also control whether a field is written to the table.

Both formatters allow for changing the column names of the table, just as the handler allows for changing the table name. This provides flexibility in the location the logs are written to and allows integration into an existing enterprise schema, if required.

DBHandler
Implementing a new target to send logs to requires developing a new Handler class. For the target of a database table, this class should encapsulate all necessary logic for connecting to the database, inserting formatted log records into the specified table, and handling any SQLExceptions.

For the DBHandler we’ll inherit directly from the Handler base class. This requires minimally implementing the publish(), close(), and flush() methods, as well as a constructor that will set up our connection.

The first step will be designing that constructor. Since we want this class to be database-independent, we’ll need to pass in the JDBC driver that will be used, plus the connect string, including username and password. Some standard configuration activities are required next, such as setting the LogManager and default Level and Formatting. Then the database-specific code is executed, which involves setting class-level variables to the driver and URL, and establishing and holding a connection in another variable.

public DBHandler( String driver, String url )
throws SQLException, ClassNotFoundException, SecurityException {
// … other configuration code here
this.jdbcdriver = driver;
this.jdbcurl = url;
dbconnect(); // connect to the database
}
private void dbconnect()
throws SQLException, ClassNotFoundException {
Class.forName(jdbcdriver);
this.conn = DriverManager.
getConnection(jdbcurl);
}

This connection will be used for each transaction performed by the publish() method, where the bulk of the work is done. This method is responsible for dynamically creating an insert statement from the formatter, executing the transaction, and catching any SQLExceptions.

To do this, it expects the formatter to return both the table column names and the log values. The column names are a static string obtained by the header portion of the formatter (to be discussed in the next section). The log values are obtained by passing in the LogRecord and getting back a single string containing each column’s value. The logic looks like this:

public void publish( LogRecord record ) {
// …
Statement stmt = conn.
createStatement();
String sql = "insert into " + tblname + " (" + frmtr.getHead() + ") values (" + frmtr.format(logrecord) + ")";
stmt.executeUpdate( sql );
stmt.close();
// 
}

Why not pass the entire insert statement back from the format() method? That could be done, but it requires the formatter to know the name of the table. This would break the rules of encapsulation for that class, since the formatter should only work with a LogRecord and not have to worry about the table name or database information.

Another design decision was to stay away from prepared statements. Using them would require an added level of complexity since we’d need to return individual values to set them rather than a single string, which is what format() is designed to do.

The flush() method isn’t applicable to this type of handler, so no implementation is present. For any of the StreamHandlers, however, this would flush the writer. The last required method, close(), will attempt to close the database connection.

A complete listing of this class can be found in Listing 2.

TblXMLFormatter
This formatter will need to produce two columns to incorporate into the SQL generated by the DBHandler. The first column will be the timestamp, which will use the standard System datetime. The second column will be a large string containing the entire XML message.

To extend the formatter requires implementing format() and, optionally, getHeader() and getTail(), which return an empty string by default. Although we can use the base class formatter to create our new class, you’ll notice we’re inheriting directly from the XML Formatter class, so we can take advantage of the code that already produces the XML from the LogRecord.

First, we have the getHeader() method, which is responsible for returning the names and the order of the columns being returned.

String returnColNames = col_time stamp+","+col_msg;

If we use the default column names from this class, the SQL in the DBHandler will go from this:

"insert into LOGTABLE (" + frmtr.getHead() + ") values (" + frmtr.format(logrecord) + ")";

to this:

"insert into LOGTABLE (LOG_TIMESTAMP, LOG_MSG) values (" + frmtr.format(logrecord) + ")"

Next, in the format() method, we call super.format() to extract the XML and concatenate it with the system datetime that will be returned. This matches the order of what getHeader() returns. Also note that we’re using the JDBC escape sequence for the timestamp to ensure database independency.

java.sql.Timestamp tm = new java.sql.Timestamp(System.currentTimeMillis());
// include timestamp, plus xml message from super
String returnVals = "{ts '" + tm.toString() + "'},'"+ super.format(record) + "'";

After format() is called, the final SQL in DBHandler will look similar to this:

"insert into LOGTABLE (LOG_TIMESTAMP, LOG_MSG) values ( {ts ‘2001-11-25 18:25:00.61‘}, ‘<record>xml message elements</record>’)"

The complete code for this class can be found in Listing 3.

TblFieldFormatter
This formatter is provided to give the user the option of using only a selected number of fields from the LogRecord and separating them into individual columns in the database.

As with the TblXMLFormatter, this class also overrides getHeader() and doesn’t use getTail(). Since the behavior of format() is entirely different from SimpleFormatter or XMLFormatter, we extend the Formatter class directly.

For getHeader(), we again return the names and order of the columns being used. Extra logic must also be introduced to provide the flexibility of turning on/off columns, but the order must remain consistent.

The format() method must also apply logic to determine which columns to display, and also to keep the order consistent with what is returned by getHeader(). Unlike TblXMLFormatter, we must manually extract each field from the LogRecord. Following is an example of how to include Level directly from the LogRecord, but only if enabled.

if ( enablecol_level ) returnVals += ",'" + record.getLevel().toString() + "'";

Finally, as with TblXMLFormatter, there are get/set methods that allow changes to the names of the database columns, and an additional “enable” method per column for enabling/disabling their output.

Listing 4 provides the entire code for this class; Listing 5 shows how this formatter is incorporated into our original example application.

Conclusion
By adding the foregoing database capabilities to the new J2SE logging package, we’ve expanded the target of where logs can be stored and increased our options for how to query them. Your logs can now fit into any JDBC-compliant enterprise database schema and use standard database tools to produce reports.

It was my intention in this article to give insight into this framework and outline the steps necessary to customize where your logs go and how they look when they get there. This framework is a powerful utility in the new J2SE, whether used out of the box or customized to your specific requirements. I’ll certainly be watching how it matures over time!

About Jim Mangione
Jim Mangione is Director of Software Engineering for the Report & Query
division of CareScience, Inc. located in Philadelphia, PA. He has over
12 years experience in client/server and web-based application
development and holds a Masters Degree in Computer Science from Drexel
University. He can be reached at jmangione@carescience.com.

In order to post a comment you need to be registered and logged in.

Register | Sign-in

Reader Feedback: Page 1 of 1

Enterprise Open Source Magazine Latest Stories . . .
AMD said late Tuesday that its chief sales officer Emilio Ghilardi had left the company and that CEO and president Rory Read is going to do his job while a replacement is sought. AMD didn’t say why Ghilardi left but it’s assumed Read wants his own people. Read is relatively new to th...
With Cloud Expo 2012 New York (10th Cloud Expo) just four months away, what better time to start introducing you in greater detail to the distinguished individuals in our incredible Speaker Faculty for the technical and strategy sessions at the conference... We have technical and st...
During the lifespan of M3 (Monitis Monitor Manager) there has always been something lacking – timers. M3 execution procedure was outlined in this previous article. The execution mentioned in the latter was a one-time-execution, whereas server monitoring requires periodic invocati...
Red Hat is putting its bought-in Gluster scale-out NAS storage technology, acquired in October, on the Amazon cloud. It’s styled Red Hat Virtual Storage Appliance for Amazon Web Services and other clouds are supposed to follow in short order.
A new episode of the screencast series is now available at the OpenNebula YouTube Channel. This screencast demonstrates the new easily-customizable self-service portal for cloud consumers. Its aim is to offer a simplified access to shared infrastructure for non-IT end users. The scree...
C12G Labs has just announced an update release of OpenNebulaPro, the enterprise edition of the OpenNebula Toolkit. OpenNebula 3.2, released two weeks ago, brings important benefits to cloud providers with a new easily-customizable self-service portal for cloud consumers, and builders w...
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
Click to Add our RSS Feeds to the Service of Your Choice:
Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
Publish Your Article! Please send it to editorial(at)sys-con.com!

Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021


SYS-CON Featured Whitepapers
ADS BY GOOGLE