|
|
by John Zukowski
The Logging API was last covered in the October 22, 2002 tip Filtering
Logged Messages. While the API hasn't changed much since being introduced with the 1.4 release of Java SE, there are two things many people don't realize when they log a message with something like the following:
logger.log(level, message);
First, the message argument doesn't have to be a hard-coded
string. Second, the message can take arguments. Internally, relying on
MessageFormat, the logger will take any arguments passed in after the
log message string and use them to fill in any blanks in the
message. The first argument after the message string argument to log()
will have index 0 and is represented by the string {0}. The next
argument is {1}, then {2}, and so on. You can also provide additional formatting details, like {1, time} would show only the time portion of a Date argument.
To demonstrate, here's how one formatted log message call might look:
String filename = ...;
String message = "Unable to delete {0} from system.";
logger.log(level, message, filename);
Now, for filename GWBASIC.EXE, the message displayed would be
"Unable to delete GWBASIC.EXE from system."
On its own, this isn't too fancy a deal. Where this extra bit of
formatting really comes in handy is when you treat the message
argument as a lookup key into a resource bundle. When you fetch a
Logger, you can either pass in only the logger name, or both the name
and a resource bundle name.
By combining messages fetched from a resource bundle with local
arguments, you get all the benefits of localized, parameterized
messages, not just in your programs, but in your log messages as
well.
To treat the message argument as a lookup key to a resource bundle,
the manner of fetching the Logger needs to change slightly. If you
want to use resource bundles, avoid creating a Logger object like
this:
private static Logger logger =
Logger.getLogger("com.example");
Instead, add an optional second argument to the getLogger() call. The
argument is the resource bundle that contains localized messages. Then,
when you make a call to log a message, the "message" argument
is the lookup key into the resource bundle, whose name is passed to the
getLogger() call.
private static final String BUNDLE_NAME = "com.example.words";
private static Logger logger =
Logger.getLogger("com.example", BUNDLE_NAME);
The BUNDLE_NAME resource bundle must include the appropriate message for
the key provided to the logging call:
logger.log(level, "messageKey");
If "messageKey" is a valid key in the resource bundle, you
now have the associated message text logged to the Logging
API. That message text can include those {0}-like arguments to
get your message arguments passed into the logger.
String filename = ...; logger.log(level, "messageKey", filename);
While you don't see the {0} formatting string in "messageKey", since
its value was acquired from the resource bundle, you could get your
output formatted with MessageFormat again.
Let us put all the pieces together. We'll create a small application
that shows localized logging.
To keep things simple, these resource bundles will be
PropertyResourceBundle objects instead of ListResourceBundle objects.
Create file messages.properties in the local directory to include the
messages for the default locale, assumed to be US English.
Message1=Hello, World
Message2=Hello, {0}
The second language will be Spanish. Place the following in the file
messages_ES.properties:
Message1=Hola, mundo
Message2=Hola, {0}
Now, we have to create the application. Notice that the
getAnonymousLogger() method also includes a second version that
accepts a resource bundle name. If you want to use a named logger,
feel free to pass in the name and use getLogger() instead.
import java.util.logging.*;
public class LocalLog {
private static Logger logger =
Logger.getAnonymousLogger("message");
public static void main(String argv[]) {
logger.log(Level.SEVERE, "Message1");
logger.log(Level.SEVERE, "Message2", "John");
}
}
The LocalLog program's log messages are "Message1" and
"Message2". When run with the default locale, you'll get messages
similar to the following:
> java LocalLog Aug 4, 2007 12:00:35 PM LocalLog main SEVERE: Hello, World Aug 4, 2007 12:00:35 PM LocalLog main SEVERE: Hello, John
To run the program with a different locale, set the user.language system
property on the command line:
>java -Duser.language=ES LocalLog ago 4, 2007 12:01:18 p.m. LocalLog main GRAVE: Hola, mundo ago 4, 2007 12:01:18 p.m. LocalLog main GRAVE: Hola, John
Notice that your log message contains the localized
resource bundle message, and the logger also uses localized
date strings and localized severity level text.
Keep this feature in mind to create localized log messages. You can
use resource bundles to provide both localized log messages and user
interface text. Those reading log files should be able to see translated
text, too.
For additional information on resource bundles, see the Resource Bundle
Loading tip and the Isolating Locale-Specific Data lesson in The Java Tutorial.