|
Articles Index
By Tom Harpin
September 2001
Logging Classes and Interfaces
New in Java 2, Standard Edition (J2SE) version 1.4, the package java.util.logging provides the classes and interfaces of a core
logging facility for the Java platform. The central goal of the logging API is to support the
maintenance and servicing of Java software in a production environment.
There are four main target uses of the logging package:
- Problem diagnosis by end users and system administrators.
This consists of simple logging of common problems that can be fixed
or tracked locally, such as running out of resources, security failures,
and simple configuration errors.
- Problem diagnosis by field service engineers. The logging information
used by field service engineers may be considerably more complex and
verbose than that required by system administrators. Typically such information
will require extra logging within particular subsystems.
- Problem diagnosis by the development organization.
When a problem occurs in the field, it may be necessary to return the captured logging
information to the original development team for diagnosis. This logging
information may be extremely detailed. Such information might include
detailed tracing on the internal execution of particular subsystems.
- Problem diagnosis during development. The Logging APIs may also be
used to help debug an application under development. This may
include logging information generated by the target application
as well as logging information generated by lower level libraries.
Note however that the logging APIs are not intended to replace the normal debugging
and profiling tools that may already exist in the development environment.
Developers should be careful about using the logging API for debugging
as it can lead to real code bloat if overdone. This could be especially bad practice
for things like applets that travel over a network.
The key classes in the package include:
-
Logger: The main entity on which applications make
logging calls. A Logger object is used to log messages
for a specific system or application
component.
-
LogRecord: Used to pass logging requests between the logging
framework and individual log handlers.
-
Handler: Exports LogRecord objects to a variety of destinations
including memory, output streams, consoles, files, and sockets.
A variety of Handler subclasses exist for this purpose. Additional Handlers
may be developed by third parties and delivered on top of the core platform.
-
Level: Defines a set of standard logging levels that can be used
to control logging output. Programs can be configured to output logging
for some levels while ignoring output for others.
-
Filter: Provides fine-grained control over what gets logged,
beyond the control provided by log levels. The logging APIs support a general-purpose
filter mechanism that allows application code to attach arbitrary filters to
control logging output.
-
Formatter: Provides support for formatting LogRecord objects. This
package includes two formatters, SimpleFormatter and
XMLFormatter, for formatting log records in plain text
or XML respectively. As with Handlers, additional Formatters
may be developed by third parties.
The Logging APIs offer both static and dynamic configuration control.
Static control enables field service staff to set up a particular configuration and then relaunch the
application with the new logging settings. Dynamic control allows for updates to the
logging configuration within a currently running program. The APIs also allow for logging to be
enabled or disabled for different functional areas of the system. For example,
a field service engineer might be interested in tracing all AWT events, but might have no interest in
socket events or memory management.
Logging Class Interactions
Applications make logging calls on Logger objects. These Logger objects allocate LogRecord objects which
are passed to Handler objects for publication. Both Loggers and Handlers may use logging Levels
and (optionally) Filters to decide if they are interested in a particular LogRecord. When it is necessary
to publish a LogRecord externally, a Handler can (optionally) use a Formatter to localize and
format the message before publishing it to an I/O stream.
There is a single global LogManager object that is used to maintain a set of shared state about Loggers and log
services.
The LogManager class keeps track of a set of global Handlers. By default all Loggers send their output to this
standard set of global Handlers. However, Loggers may also be configured to ignore the global Handler list and/or to
send output to specific target Handlers. Some Handlers may direct output to other Handlers. For example, the
MemoryHandler maintains an internal ring buffer of LogRecords and on trigger events it publishes its LogRecords
through a target Handler. In such cases, any formatting is done by the last Handler in the chain.
The APIs are structured so that calls on the Logger APIs can be cheap when logging is disabled. If logging is disabled for a given log level, then the Logger can
make a cheap comparison test and return. If logging is enabled for a given log level, the Logger is still careful to minimize costs before passing the LogRecord into the
Handlers. In particular, localization and formatting (which are relatively expensive) are deferred until the Handler requests them. For example, a MemoryHandler can
maintain a circular buffer of LogRecords without having to pay formatting costs.
The java.util.logging package provides the following Handlers,
StreamHandler: A simple handler for writing formatted records to an OutputStream.
ConsoleHandler: A simple handler for writing formatted records to System.err
FileHandler: A handler that writes formatted log records either to a single file, or to a set of rotating log files.
SocketHandler: A handler that writes formatted log records to remote TCP ports.
MemoryHandler: A handler that buffers log records in memory.
...and two standard Formatters:
SimpleFormatter: Writes brief "human-readable" summaries of log records.
XMLFormatter: Writes detailed XML-structured information.
Logging Configuration
The APIs are structured so that an initial set of configuration information is read as properties from a configuration
file. The configuration information may then be changed programatically by calls on the various logging classes and
objects. In addition, there are methods on LogManager that allow the configuration file to be re-read. When this
happens, the configuration file values will override any changes that have been made programatically.
The default logging configuration that ships with the JRE makes only limited use of disk space. It does not flood the
user with information, but does make sure to capture key failure information. The default configuration
establishes two global Handlers, one for a file in the system temporary directory and one for the console. Only
high priority messages are recorded.
Programmers can update the logging configuration at runtime in a variety of ways:
- FileHandlers, MemoryHandlers, and PrintHandlers can all be created with various attributes.
- The LogManager allows new global Handlers to be added and old ones removed.
- New Loggers can be created and can be supplied with specific Handlers.
- The
LogManager.setLevel method allows new logging levels to be specified for trees within the Logger namespace.
Remote Access and Serialization
As with most Java platform APIs, the logging APIs are designed for use inside a single address space. All calls are intended to be local.
However, it is expected that some Handlers will want to forward their output to other systems. There are a variety of
ways of doing this:
- Some Handlers (such as the
SocketHandler) may write data to other systems using the XMLFormatter. This provides a
simple, standard, interchange format that can be parsed and processed on a variety of systems.
- Some Handlers may wish to pass
LogRecord objects over RMI. The LogRecord class is therefore serializable. However
there is a problem in how to deal with the LogRecord parameters. Some parameters may not be serializable and other
parameters may have been designed to serialize much more state than is required for logging. To avoid these problems
the LogRecord class has a custom writeObject method that converts the parameters to strings (using Object.toString)
before writing them out. See the LogRecord API Specification for details.
- Most of the logging classes are not intended to be serializable. Both Loggers and Handlers are stateful classes
that are tied into a specific virtual machine. In this respect they are analogous to the
java.io classes, which are
also not serializable.
Log Monitoring for Systems Management
Systems management products, such as BMC PATROL, can be configured to monitor system or application logfiles.
Typically, a template is associated with each file to be monitored. If a string specified in the template
matches a string found in the logfile, an alarm or other event is triggered. Depending on the configuration, the event
may cause an update to appear on the management console, an email or page to be sent to a system administrator, or an
SNMP trap to be generated. This mechanism can be used in combination with the J2SE Logging API to accomplish the monitoring
of a set of Java programs. Events worth monitoring might include loss of a network connection, loss of a
database connection, overflow of a JMS message queue, or exceeding some preset threshold in the application.
Perform the following steps to create a PATROL template file to apply to your J2SE logfile:
- Make sure that a PATROL Agent is running on the host where the logfile will be written.
- Start the PATROL Console and connect to the Agent on the target host.
- Double click on the icon which represents the target host to expose the icons for that host.
- Right click on the Log icon and choose Edit Templates from the popup menu to open the
Log Search Templates dialog.
- Choose the Add radio button and click on Apply to open the Log Template (New) dialog.
- Enter the name of the new template file and the string or regular expression to match on and click Apply.
- In this dialog, you can also choose an alarm level (OK, Warn, Alarm) to associate with the specified string.
Then configure PATROL to monitor your J2SE logfile, using the template you have just created:
- Go back to the icon window for the target host.
- Right click on the Log icon and choose Edit List of Monitored Files from the popup menu to open the
Log Files dialog.
- Choose the Add radio button and click on Apply to open the Log File (New) dialog.
- Enter the file name and path of the log file you want to monitor in the File Name field.
- From the Select/Deselect Templates list, select the template you have previously created for this log file.
- After you click on Accept, the Log Files dialog box appears with the new log file name in the List of
Monitored Files.
- Click Close to close the Log Files dialog box and a LOGMON instance icon for this log file appears in the
LOGS container window.
- PATROL begins to monitor the log file. File size and growth rate will be monitored as well as occurrences of any
search strings specified in the associated template(s).
Depending on the alarm severity associated with a search string (OK, Warn, or Alarm), an occurrence of that string
in the monitored logfile will cause the LOGS icon in the PATROL Console to turn or flash red. If you also have
a PATROL Event Manager window open, you will see green, yellow or red messages appear.
Conclusion
Although other means exist to monitor Java applications, the logging API provides a simple, straightforward mechanism
to convey status information about a running Java application. Multiple severity levels are supported as well as multiple
output handlers. This, combined with circular buffering and rotating logfiles, makes the mechanism suitable for production
environments. The use of sequential, ASCII files provides for easy integration with third party systems' management products.
For More Information
Java Logging APIs
About the Author
Tom Harpin works in Market Development Engineering at Sun's Burlington, MA campus. He works primarily
on Distributed System Management vendor integration efforts with WBEM, and JMX. Prior to working with Sun, Tom
helped design and implement several client-server systems for Delta Airlines.
Have a question about programming? Use
Java Online
Support.
|
|