Logging is the process of reporting information about a running program.
Add println()
statements to your code to keep track of the code flow. These
println()
statements are taken out if code starts to work properly
If changes are made, and we run into more bugs, these statements need to put
back in.
Better approach would be to create a flag and method in your class
static final boolean activateDebug;debug(String info){ if (activateDebug) System.out.println(String); }
Calling debug()
anywhere will print debug statements. If activateDebug
is
false, the compiler will completely remove the code within the braces (thus the
code doesn�t cause any run-time overhead at all when it isn�t used). However,
recompilation is required every time value of activateDebug
is switched.
Another solution, the value of activateDebug
can be set in an
external configuration file. Not generic, everyone does it differently.
Logging API is the Java's solution for Logging in a simple way.
Logger
objects. Logger
objects allocate LogRecord
objects which are passed to Handler
objects for publication. Handler
object provides a strategy (a plugin) for
publishing the logging information, which is contained in LogRecord
s
objects.Levels
and (optionally)
Filters
to decide if they are interested in a
particular LogRecord
. LogRecord
externally, a
Handler
can (optionally) use a
Formatter
to localize and format the message
before publishing it to an I/O streamBy default all Loggers also send their output to their parent Logger. But
Loggers may also be configured to ignore Handlers higher up the tree.
Handlers can also direct output to other Handlers. For example, the
MemoryHandler
maintains an internal ring buffer of LogRecord
s and on trigger
events it publishes its LogRecord
s through a target Handler. In such cases, any
formatting is done by the last Handler
in the chain.
Logging Example:
import java.util.logging.*; public class InfoLogging { private static Logger logger = Logger.getLogger("InfoLogging"); public static void main(String[] args) { logger.info("Logging an INFO-level message"); logger.log(Level.INFO, "Logging an INFO-level message"); logger.logp(Level.INFO, "InfoLogging", "main", "Logging an INFO-level message"); } }
Output:
Apr 15, 2006 2:25:52 PM InfoLogging main INFO: Logging an INFO-level message Apr 15, 2006 2:25:52 PM InfoLogging main INFO: Logging an INFO-level message Apr 15, 2006 2:25:52 PM InfoLogging main INFO: Logging an INFO-level message
The logging methods are grouped in five main categories:
Each log message has an associated log Level. The Level indicates importance
and urgency of a log message. Log level objects encapsulate an integer value,
with higher values indicating higher priorities.
Logging API provides the ability to change to a different logging level during
program execution. Thus, you can dynamically set the logging level to any of the
Logging Levels.
Levels | Effect | Numeric Values |
OFF | No Logging Messages Reported | Integer.MAX_VALUE |
SEVERE | Only logging messages with the level SEVERE are reported | 1000 |
WARNING | Logging messages with levels of WARNING and SEVERE are reported | 900 |
INFO | Logging messages with levels of INFO and above are reported | 800 |
CONFIG | Logging messages with levels of CONFIG and above are reported | 700 |
FINE | Logging messages with levels of FINE and above are reported | 500 |
FINER | Logging messages with levels of FINER and above are reported | 400 |
FINEST | Logging messages with levels of FINEST and above are reported | 300 |
ALL | All logging messages are reported | Integer.MIN_VALUE |
For defining a custom level, you can inherit from
java.util.Logging.Level
. and define your own level.
Client code sends log requests to Logger objects. Each logger keeps track of a log level that it is interested in, and discards log requests that are below this level. Loggers are normally named entities, using dot-separated names such as java.awt. The namespace is hierarchical and is managed by the LogManager.
There can be multiple logger objects in your program, and these loggers are organized into a hierarchical tree, which can be programmatically associated with the package namespace. Child loggers keep track of their immediate parent and by default passing the logging records up to the parent. The root Logger (named "") has no parent. Loggers may inherit various attributes from their parents in the logger namespace. In particular, a logger may inherit:
The Logger class provides a large set of convenience methods for generating
log messages. For convenience, there are methods for each logging level, named
after the logging level name. Thus rather than calling
logger.log,a developer can simply call the convenience method
logger.warning.
There are two different styles of logging methods, to meet the needs of
different communities of users.
void warning(String sourceClass, String sourceMethod, String msg);
void warning(String msg);
For second set of methods, the Logging framework will make a "best effort" to determine which class and method called into the logging framework and will add this information into the LogRecord. However, it is important to realize that this automatically inferred information may only be approximate. The latest generation of virtual machines perform extensive optimizations and may entirely remove stack frames, making it impossible to reliably locate the calling class and method.
A LogRecord
is an example of a messenger object, whose job is simply to
carry information from one place to another. All the methods in the LogRecord
are getters and setters
Level<FINEST> LoggerName<null> Message<Simple Log Record> CurrentMillis<1145153433003> Params<null> ResourceBundle<null> ResourceBundleName<null> SequenceNumber<0> SourceClassName<null> SourceMethodName<null> Thread Id<10> Thrown<null>
The Java logging framework provides the following Handlers:
try { // Create a file handler that write log record to a file called my.log FileHandler handler = new FileHandler("my.log"); // Add to the desired logger Logger logger = Logger.getLogger("package.class"); logger.addHandler(handler); } catch (IOException e) { }By default, a file handler overwrites the contents of the log file each time it is created. This example creates a file handler that appends.
try { // Create an appending file handler boolean append = true; FileHandler handler = new FileHandler("my.log", append); // Add to the desired logger Logger logger = Logger.getLogger("package.class"); logger.addHandler(handler); } catch (IOException e) { }
The default output format for a FileHandler is XML. If you want to change the format, you must attach a different Formatter object to the handler.
Apr 16, 2006 12:03:19 AM LogToFile main INFO: A message logged to the file
<?xml version="1.0" encoding="windows-1252" standalone="no"?> <!DOCTYPE log SYSTEM "logger.dtd"> <log> <record> <date>2006-04-16T00:03:19</date> <millis>1145163799389</millis> <sequence>0</sequence> <logger>LogToFile</logger> <level>INFO</level> <class>LogToFile</class> <method>main</method> <thread>10</thread> <message>A message logged to the file</message> </record> </log>
Apr 16, 2006 12:03:19 AM LogToFile main INFO: A message logged to the file
You can register multiple handlers with each Logger object. When a logging request comes to the Logger, it notifies all the handlers that have been registered with it, as long as the logging level for the Logger is greater than or equal to that of the logging request. Each handler, in turn, has its own logging level; if the level of the LogRecord is greater than or equal to the level of the handler, then that handler publishes the record.
Apr 15, 2006 9:32:48 PM MultipleHandlers main WARNING: Output to multiple handlers Apr 15, 2006 9:32:48 PM MultipleHandlers main WARNING: Output to multiple handlers Apr 15, 2006 9:32:48 PM MultipleHandlers main WARNING: Output to multiple handlers
The console output occurs twice; that�s because the root logger�s default
behavior is still enabled. If you want to turn this off, make a call to
setUseParentHandlers(false)
You can write custom handlers by inheriting from the Handler class. To do this, you must not only implement the publish() method (which performs the actual reporting), but also flush( ) and close( ), which ensure that the stream used for reporting is properly cleaned up.
Object constructed (ArrayList
) by the custom handler:
[WARNING:, CustomHandler:, main:,, , INFO:, CustomHandler:, main:, , ]
Console handler output:
Apr 15, 2006 9:58:35 PM CustomHandler main WARNING: Logging Warning Apr 15, 2006 9:58:35 PM CustomHandler main INFO: Logging Info
A Formatter is a way to insert a formatting operation into a Handler�s processing steps. If you register a Formatter object with a Handler, then before the LogRecord is published by the Handler, it is first sent to the Formatter. After formatting, the LogRecord is returned to the Handler, which then publishes it.
As with Handlers, it is fairly straightforward to develop new Formatters. Extend the Formatter class and override format(LogRecord record). Then, register the Formatter with the Handler by using the setFormatter() call.
Sending email to report log messages:
To send a logging message to a Logger object, you have to decide what level the logging message should be (the logging API certainly allows you to devise more complex systems wherein the level of the message can be determined dynamically, but this is less common in practice). The Logger object has a level that can be set so that it can decide what level of message to accept; all others will be ignored. This is a basic filtering functionality.
More sophisticated filtering is needed so that you can decide whether to accept or reject a message based on something more than just the current level. Use custom Filter objects for this. Filter is an interface that has a single method, boolean isLoggable(LogRecord record), which decides whether or not this particular LogRecord is interesting enough to report.
Once you create a Filter, you register it with either a Logger or a Handler by using the setFilter( ) method.
The LogManager object is created during class initialization and cannot subsequently be changed. It keeps track of global logging information. This includes:
There is a single LogManager object that can be retrieved using the static LogManager.getLogManager method. This is created during LogManager initialization, based on a system property. This property allows container applications (such as EJB containers) to substitute their own subclass of LogManager in place of the default class.
It�s advisable to give a logger the name of the class in which it is used. This allows you to manipulate the logging level of groups of loggers that reside in the same package hierarchy, at the granularity of the directory package structure.
The logging configuration can be initialized using a logging configuration
file that will be read at startup. This logging configuration file is in
standard java.util.Properties format.
Alternatively, the logging configuration can be initialized by specifying a
class that can be used for reading initialization properties. This mechanism
allows configuration data to be read from arbitrary sources, such as LDAP. JDBC,
etc.
There is a small set of global configuration information. This is specified in
the description of the LogManager class and
includes a list of root-level Handlers to install during startup. The initial
configuration may specify levels for particular loggers. These levels are
applied to the named logger and any loggers below it in the naming hierarchy.
The levels are applied in the order they are defined in the configuration file.
The initial configuration may contain arbitrary properties for use by Handlers
or by subsystems doing logging. By convention these properties should use names
starting with the name of the handler class or the name of the main Logger for
the subsystem.
The default configuration makes only limited use of disk space. It doesn't flood
the user with information, but does make sure to always capture key failure
information.
The default configuration establishes a single handler on the root logger for
sending output to the console. Programmers can update the logging configuration
at run time in a variety of ways:
The preceding configuration file generates rotating log files, which are used to prevent any log file from becoming too large. By setting the FileHandler.limit value, you give the maximum number of bytes allowed in one log file before the next one begins to fill. FileHandler.count determines the number of rotating log files to use; the configuration file shown here specifies three files. If all three files are filled to their maximum, then the first file begins to fill again, overwriting the old contents.
Alternatively, all the output can be put in a single file by giving a FileHandler.count value of one.
In order for a program to use the preceding configuration file, you must specify the parameter java.util.logging.config.file on the command line:
java -Djava.util.logging.config.file=sorcer.logging sorcer.core.util.Log
The principal security requirement is that untrusted code should not be able
to change the logging configuration. Specifically, if the logging configuration
has been set up to log a particular category of information to a particular
Handler, then untrusted code should not be able to prevent or disrupt that
logging.
A new security permission LoggingPermission is
defined to control updates to the logging configuration.
Trusted applications are given the appropriate
LoggingPermission so they can call any of the logging configuration APIs.
Untrusted applets are a different story. Untrusted applets can create and use
named Loggers in the normal way, but they are not allowed to change logging
control settings, such as adding or removing handlers, or changing log levels.
However, untrusted applets are able to create and use their own "anonymous"
loggers, using Logger.getAnonymousLogger. These
anonymous Loggers are not registered in the global namespace and their methods
are not access-checked, allowing even untrusted code to change their logging
control settings.
The logging framework does not attempt to prevent spoofing. The sources of
logging calls cannot be determined reliably, so when a LogRecord is published
that claims to be from a particular source class and source method, it may be a
fabrication. Similarly, formatters such as the XMLFormatter do not attempt to protect themselves against nested log
messages inside message strings. This, a spoof LogRecord might contain a spoof
set of XML inside its message string to make it look as if there was an
additional XML record in the output.
In addition, the logging framework does not attempt to protect itself against
denial of service attacks. Any given logging client can flood the logging
framework with meaningless messages in an attempt to conceal some important log
message.
Sometimes there is a requirement to execute some code to perform
initialization activities such as adding Handlers, Filters, and Formatters to
loggers. This can be achieved by setting the config
property in the logging properties
file. You can have multiple classes whose initialization can be done using the
config
property. These classes should be
specified using space-delimited values like this:
config = ConfigureLogging1 ConfigureLogging2 Bar Baz
Classes specified in this fashion will have their default constructors invoked.