I've written an enhancement to the localization support the generic Logger
class provides.
A new LocalizedLogger overrides basic log4j logging methods (trace(), debug(),
info(), warn(), error(), fatal()) to use a localization mechanism. Instead of
the actual log messages, keys to localization log messages can be passed into
these logging methods. Full parameterization is supported. If keys are not
found in the ResourceBundle, they are logged directly, skipping the
localization.
Internally, LocalizedLogger simply calls the l7dlog() method that already
exists in Logger. LocalizedLogger is just a convenience class for applications
that don't want to call this special method.
A LocalizationResourceBundle and a LocalizedLoggerFactory classes are also
included. These helper classes provide easy integration with your application
by simply changing the loggerFactory defined in the log4j configuration to
point to this new LocalizedLoggerFactory.
I've attached an SVN diff that can be applied to log4j trunk.
Thanks,
Manish
Index: src/main/java/org/apache/log4j/localization/LocalizedLogger.java
===================================================================
--- src/main/java/org/apache/log4j/localization/LocalizedLogger.java (revision 0)
+++ src/main/java/org/apache/log4j/localization/LocalizedLogger.java (revision 0)
@@ -0,0 +1,456 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.localization;
+
+import java.util.Enumeration;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+
+/**
+ * Overrides basic log4j logging methods to use a localization mechanism.
+ *
+ * Instead of the actual log messages, keys to localization log messages can be
+ * passed into the logging methods. Supports parameterization.
+ *
+ * If keys are not found in the {...@link ResourceBundle}, they are logged
+ * directly, skipping the localization.
+ *
+ * @author Manish Motwani
+ */
+public class LocalizedLogger extends Logger {
+ /**
+ * The fully qualified name of the LocalizedLogger class.
+ */
+ private static final String FQCN = LocalizedLogger.class.getName();
+
+ public static Logger getLogger(final String name) {
+ return getLocalizedLogger(name);
+ }
+
+ public static Logger getLogger(final Class clazz) {
+ return getLocalizedLogger(clazz);
+ }
+
+ public static LocalizedLogger getLocalizedLogger(final String name) {
+ final Logger logger = Logger.getLogger(name, LocalizedLoggerFactory
+ .getInstance());
+
+ if (logger instanceof LocalizedLogger) {
+ return (LocalizedLogger) logger;
+ } else {
+ final LocalizedLogger localizedLogger = new LocalizedLogger(name);
+ localizedLogger.additive = logger.getAdditivity();
+ localizedLogger.level = logger.getEffectiveLevel();
+ localizedLogger.parent = logger.getParent();
+ localizedLogger.repository = logger.getLoggerRepository();
+ localizedLogger.resourceBundle = LocalizationResourceBundle
+ .getInstance();
+
+ final Enumeration appenders = logger.getAllAppenders();
+ while (appenders.hasMoreElements())
+ localizedLogger.addAppender((Appender) appenders.nextElement());
+ return localizedLogger;
+ }
+ }
+
+ public static LocalizedLogger getLocalizedLogger(final Class clazz) {
+ return LocalizedLogger.getLocalizedLogger(clazz.getName());
+ }
+
+ protected LocalizedLogger(final String name) {
+ super(name);
+ }
+
+ private String stringifyObject(final Object message) {
+ return message == null ? null : message.toString();
+ }
+
+ public String getLocalizedString(final String pattern, final Object[] params) {
+ final String msg = getResourceBundleString(pattern);
+ if (msg == null) {
+ if (params == null)
+ return "null";
+ return params.toString();
+ }
+ return java.text.MessageFormat.format(msg, params);
+ }
+
+ // Overriding this method to avoid infinite recursion caused by a call to
+ // error() log method,
+ // which is overridden in LocalizedLogger and calls
+ // getResourceBundleString().
+ protected String getResourceBundleString(final String key) {
+ final ResourceBundle rb = getResourceBundle();
+ if (rb == null) {
+ return null;
+ } else {
+ try {
+ return rb.getString(key);
+ } catch (final MissingResourceException mre) {
+ return null;
+ }
+ }
+ }
+
+ // Overriding this method so the stack contains LocalizedLogger's call,
+ // because the overridden
+ // forcedLog() method (below) overrides the fully qualified category name
+ // (FQCN) to be of
+ // LocalizedLogger, which is used by log4j to find the caller class/method
+ // from stack.
+ public void l7dlog(final Priority priority, final String key,
+ final Throwable t) {
+ super.l7dlog(priority, key, t);
+ }
+
+ // Overriding this method so the stack contains LocalizedLogger's call,
+ // because the overridden
+ // forcedLog() method (below) overrides the fully qualified category name
+ // (FQCN) to be of
+ // LocalizedLogger, which is used by log4j to find the caller class/method
+ // from stack.
+ public void l7dlog(final Priority priority, final String key,
+ final Object[] params, final Throwable t) {
+ super.l7dlog(priority, key, params, t);
+ }
+
+ // Overriding this method to override the fully qualified category name to
+ // be the fully qualified
+ // name of this class (LocalizedLogger).
+ protected void forcedLog(final String fqcn, final Priority level,
+ final Object message, final Throwable t) {
+ super.forcedLog(FQCN, level, message, t);
+ }
+
+ public void debug(final Object message, final Throwable t) {
+ l7dlog(Level.DEBUG, stringifyObject(message), t);
+ }
+
+ public void debug(final Object message) {
+ l7dlog(Level.DEBUG, stringifyObject(message), null);
+ }
+
+ public void debug(final Object message, final Object[] params) {
+ l7dlog(Level.DEBUG, stringifyObject(message), params, null);
+ }
+
+ public void debug(final Object message, final Object param1) {
+ debug(message, new Object[] { param1 });
+ }
+
+ public void debug(final Object message, final Object param1,
+ final Object param2) {
+ debug(message, new Object[] { param1, param2 });
+ }
+
+ public void debug(final Object message, final Object param1,
+ final Object param2, final Object param3) {
+ debug(message, new Object[] { param1, param2, param3 });
+ }
+
+ public void debug(final Object message, final Throwable t,
+ final Object[] params) {
+ l7dlog(Level.DEBUG, stringifyObject(message), params, t);
+ }
+
+ public void debug(final Object message, final Throwable t,
+ final Object param1) {
+ debug(message, t, new Object[] { param1 });
+ }
+
+ public void debug(final Object message, final Throwable t,
+ final Object param1, final Object param2) {
+ debug(message, t, new Object[] { param1, param2 });
+ }
+
+ public void debug(final Object message, final Throwable t,
+ final Object param1, final Object param2, final Object param3) {
+ debug(message, t, new Object[] { param1, param2, param3 });
+ }
+
+ public void error(final Object message, final Throwable t) {
+ l7dlog(Level.ERROR, stringifyObject(message), t);
+ }
+
+ public void error(final Object message) {
+ l7dlog(Level.ERROR, stringifyObject(message), null);
+ }
+
+ public void error(final Object message, final Object[] params) {
+ l7dlog(Level.ERROR, stringifyObject(message), params, null);
+ }
+
+ public void error(final Object message, final Object param1) {
+ error(message, new Object[] { param1 });
+ }
+
+ public void error(final Object message, final Object param1,
+ final Object param2) {
+ error(message, new Object[] { param1, param2 });
+ }
+
+ public void error(final Object message, final Object param1,
+ final Object param2, final Object param3) {
+ error(message, new Object[] { param1, param2, param3 });
+ }
+
+ public void error(final Object message, final Throwable t,
+ final Object[] params) {
+ l7dlog(Level.ERROR, stringifyObject(message), params, t);
+ }
+
+ public void error(final Object message, final Throwable t,
+ final Object param1) {
+ error(message, t, new Object[] { param1 });
+ }
+
+ public void error(final Object message, final Throwable t,
+ final Object param1, final Object param2) {
+ error(message, t, new Object[] { param1, param2 });
+ }
+
+ public void error(final Object message, final Throwable t,
+ final Object param1, final Object param2, final Object param3) {
+ error(message, t, new Object[] { param1, param2, param3 });
+ }
+
+ public void fatal(final Object message, final Throwable t) {
+ l7dlog(Level.FATAL, stringifyObject(message), t);
+ }
+
+ public void fatal(final Object message) {
+ l7dlog(Level.FATAL, stringifyObject(message), null);
+ }
+
+ public void fatal(final Object message, final Object[] params) {
+ l7dlog(Level.FATAL, stringifyObject(message), params, null);
+ }
+
+ public void fatal(final Object message, final Object param1) {
+ fatal(message, new Object[] { param1 });
+ }
+
+ public void fatal(final Object message, final Object param1,
+ final Object param2) {
+ fatal(message, new Object[] { param1, param2 });
+ }
+
+ public void fatal(final Object message, final Object param1,
+ final Object param2, final Object param3) {
+ fatal(message, new Object[] { param1, param2, param3 });
+ }
+
+ public void fatal(final Object message, final Throwable t,
+ final Object[] params) {
+ l7dlog(Level.FATAL, stringifyObject(message), params, t);
+ }
+
+ public void fatal(final Object message, final Throwable t,
+ final Object param1) {
+ fatal(message, t, new Object[] { param1 });
+ }
+
+ public void fatal(final Object message, final Throwable t,
+ final Object param1, final Object param2) {
+ fatal(message, t, new Object[] { param1, param2 });
+ }
+
+ public void fatal(final Object message, final Throwable t,
+ final Object param1, final Object param2, final Object param3) {
+ fatal(message, t, new Object[] { param1, param2, param3 });
+ }
+
+ public void info(final Object message, final Throwable t) {
+ l7dlog(Level.INFO, stringifyObject(message), t);
+ }
+
+ public void info(final Object message) {
+ l7dlog(Level.INFO, stringifyObject(message), null);
+ }
+
+ public void info(final Object message, final Object[] params) {
+ l7dlog(Level.INFO, stringifyObject(message), params, null);
+ }
+
+ public void info(final Object message, final Object param1) {
+ info(message, new Object[] { param1 });
+ }
+
+ public void info(final Object message, final Object param1,
+ final Object param2) {
+ info(message, new Object[] { param1, param2 });
+ }
+
+ public void info(final Object message, final Object param1,
+ final Object param2, final Object param3) {
+ info(message, new Object[] { param1, param2, param3 });
+ }
+
+ public void info(final Object message, final Throwable t,
+ final Object[] params) {
+ l7dlog(Level.INFO, stringifyObject(message), params, t);
+ }
+
+ public void info(final Object message, final Throwable t,
+ final Object param1) {
+ info(message, t, new Object[] { param1 });
+ }
+
+ public void info(final Object message, final Throwable t,
+ final Object param1, final Object param2) {
+ info(message, t, new Object[] { param1, param2 });
+ }
+
+ public void info(final Object message, final Throwable t,
+ final Object param1, final Object param2, final Object param3) {
+ info(message, t, new Object[] { param1, param2, param3 });
+ }
+
+ public void log(final Priority priority, final Object message,
+ final Throwable t) {
+ l7dlog(priority, stringifyObject(message), t);
+ }
+
+ public void log(final Priority priority, final Object message) {
+ l7dlog(priority, stringifyObject(message), null);
+ }
+
+ public void log(final Priority priority, final Object message,
+ final Object[] params) {
+ l7dlog(priority, stringifyObject(message), params, null);
+ }
+
+ public void log(final Priority priority, final Object message,
+ final Throwable t, final Object[] params) {
+ l7dlog(priority, stringifyObject(message), params, t);
+ }
+
+ public void log(final String callerFQCN, final Priority level,
+ final Object message, final Throwable t) {
+ if (this.repository.isDisabled(level.toInt())) {
+ return;
+ }
+
+ if (level.isGreaterOrEqual(getEffectiveLevel())) {
+ String msg = getResourceBundleString(stringifyObject(message));
+
+ if (msg == null) {
+ msg = stringifyObject(message);
+ }
+ forcedLog(callerFQCN, level, msg, t);
+ }
+ }
+
+ public void trace(final Object message, final Throwable t) {
+ l7dlog(Level.TRACE, stringifyObject(message), t);
+ }
+
+ public void trace(final Object message) {
+ l7dlog(Level.TRACE, stringifyObject(message), null);
+ }
+
+ public void trace(final Object message, final Object[] params) {
+ l7dlog(Level.TRACE, stringifyObject(message), params, null);
+ }
+
+ public void trace(final Object message, final Object param1) {
+ trace(message, new Object[] { param1 });
+ }
+
+ public void trace(final Object message, final Object param1,
+ final Object param2) {
+ trace(message, new Object[] { param1, param2 });
+ }
+
+ public void trace(final Object message, final Object param1,
+ final Object param2, final Object param3) {
+ trace(message, new Object[] { param1, param2, param3 });
+ }
+
+ public void trace(final Object message, final Throwable t,
+ final Object[] params) {
+ l7dlog(Level.TRACE, stringifyObject(message), params, t);
+ }
+
+ public void trace(final Object message, final Throwable t,
+ final Object param1) {
+ trace(message, t, new Object[] { param1 });
+ }
+
+ public void trace(final Object message, final Throwable t,
+ final Object param1, final Object param2) {
+ trace(message, t, new Object[] { param1, param2 });
+ }
+
+ public void trace(final Object message, final Throwable t,
+ final Object param1, final Object param2, final Object param3) {
+ trace(message, t, new Object[] { param1, param2, param3 });
+ }
+
+ public void warn(final Object message, final Throwable t) {
+ l7dlog(Level.WARN, stringifyObject(message), t);
+ }
+
+ public void warn(final Object message) {
+ l7dlog(Level.WARN, stringifyObject(message), null);
+ }
+
+ public void warn(final Object message, final Object[] params) {
+ l7dlog(Level.WARN, stringifyObject(message), params, null);
+ }
+
+ public void warn(final Object message, final Object param1) {
+ warn(message, new Object[] { param1 });
+ }
+
+ public void warn(final Object message, final Object param1,
+ final Object param2) {
+ warn(message, new Object[] { param1, param2 });
+ }
+
+ public void warn(final Object message, final Object param1,
+ final Object param2, final Object param3) {
+ warn(message, new Object[] { param1, param2, param3 });
+ }
+
+ public void warn(final Object message, final Throwable t,
+ final Object[] params) {
+ l7dlog(Level.WARN, stringifyObject(message), params, t);
+ }
+
+ public void warn(final Object message, final Throwable t,
+ final Object param1) {
+ warn(message, t, new Object[] { param1 });
+ }
+
+ public void warn(final Object message, final Throwable t,
+ final Object param1, final Object param2) {
+ warn(message, t, new Object[] { param1, param2 });
+ }
+
+ public void warn(final Object message, final Throwable t,
+ final Object param1, final Object param2, final Object param3) {
+ warn(message, t, new Object[] { param1, param2, param3 });
+ }
+
+}
Index: src/main/java/org/apache/log4j/localization/LocalizationResourceBundle.java
===================================================================
--- src/main/java/org/apache/log4j/localization/LocalizationResourceBundle.java (revision 0)
+++ src/main/java/org/apache/log4j/localization/LocalizationResourceBundle.java (revision 0)
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.localization;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ResourceBundle;
+
+/**
+ * Loads localized log messages from resources. At least one of the root
+ * resource directories must contain the log message properties file:
+ *
+ * <pre>
+ * logmessages_<language>_<COUNTRY>.properties
+ * </pre>
+ *
+ * For example, if the default locale is "en_US", the resource file name should
+ * be:
+ *
+ * <pre>
+ * logmessages_en_US.properties
+ * </pre>
+ *
+ * System properties "user.language" and "user.country" may be used to modify
+ * the JVM's default locale settings. Alternately,
+ * {...@link Locale#setDefault(Locale)} method can also be used to set a default
+ * {...@link Locale}.
+ *
+ * The log messages properties file prefix can be modified by setting the
+ * following property:
+ *
+ * <pre>
+ * log4j.localization.resource_prefix
+ * </pre>
+ *
+ * If this property is not set, it defaults to "logmessages".
+ *
+ * @author Manish Motwani
+ */
+public class LocalizationResourceBundle extends ResourceBundle {
+ public static volatile Map instances = new HashMap();
+
+ private static String DEFAULT_RESOURCE_NAME = "logmessages";
+ private static String RESOURCE_PREFIX_PROPERTY = "log4j.localization.resource_prefix";
+
+ /**
+ * Creates a localized {...@link ResourceBundle}. Current JVM's default locale
+ * is used to create this {...@link ResourceBundle}.
+ *
+ * @return
+ */
+ public static LocalizationResourceBundle getInstance() {
+ String resourceName = System.getProperty(RESOURCE_PREFIX_PROPERTY,
+ DEFAULT_RESOURCE_NAME);
+ return LocalizationResourceBundle.getInstance(resourceName, Locale
+ .getDefault());
+ }
+
+ /**
+ * Creates a localized {...@link ResourceBundle} using the passed resource
+ * name. Current JVM's default locale is used to create this
+ * {...@link ResourceBundle}.
+ *
+ * @param resourceName
+ * @return
+ */
+ public static LocalizationResourceBundle getInstance(
+ final String resourceName) {
+ return LocalizationResourceBundle.getInstance(resourceName, Locale
+ .getDefault());
+ }
+
+ /**
+ * Creates a localized {...@link ResourceBundle} using the passed resource name
+ * and {...@link Locale}.
+ *
+ * @param resourceName
+ * @param locale
+ * @return
+ */
+ private static LocalizationResourceBundle getInstance(
+ final String resourceName, final Locale locale) {
+ if (!instances.containsKey(resourceName)) {
+ synchronized (LocalizationResourceBundle.class) {
+ if (!instances.containsKey(resourceName)) {
+ final LocalizationResourceBundle instance = new LocalizationResourceBundle(
+ resourceName, locale);
+ instances.put(resourceName, instance);
+ }
+ }
+
+ }
+ return (LocalizationResourceBundle) instances.get(resourceName);
+ }
+
+ private final Properties properties;
+ private static final String RESOURCE_EXTENSION = ".properties";
+
+ private LocalizationResourceBundle(final String resourceName,
+ final Locale locale) {
+ this.properties = new Properties();
+ try {
+ final Enumeration resources = ClassLoader
+ .getSystemResources(resourceName + "_" + locale
+ + RESOURCE_EXTENSION);
+
+ while (resources.hasMoreElements()) {
+ this.properties.load(((URL) resources.nextElement())
+ .openStream());
+ }
+ } catch (final Exception e) {
+ System.err.println("Error loading resource bundle.");
+ e.printStackTrace(System.err);
+ }
+ }
+
+ public Enumeration getKeys() {
+ return this.properties.propertyNames();
+ }
+
+ protected Object handleGetObject(final String key) {
+ if (key != null && this.properties.containsKey(key))
+ return this.properties.get(key);
+
+ return null;
+ }
+}
Index: src/main/java/org/apache/log4j/localization/LocalizedLoggerFactory.java
===================================================================
--- src/main/java/org/apache/log4j/localization/LocalizedLoggerFactory.java (revision 0)
+++ src/main/java/org/apache/log4j/localization/LocalizedLoggerFactory.java (revision 0)
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.localization;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggerFactory;
+
+public class LocalizedLoggerFactory implements LoggerFactory {
+ public static volatile LoggerFactory instance;
+
+ public static LoggerFactory getInstance() {
+ if (instance == null) {
+ synchronized (LocalizedLoggerFactory.class) {
+ if (instance == null) {
+ instance = new LocalizedLoggerFactory();
+ }
+ }
+
+ }
+ return instance;
+ }
+
+ public Logger makeNewLoggerInstance(final String name) {
+ final Logger logger = new LocalizedLogger(name);
+ logger.setResourceBundle(LocalizationResourceBundle.getInstance());
+ return logger;
+ }
+}
Index: build.xml
===================================================================
--- build.xml (revision 789940)
+++ build.xml (working copy)
@@ -424,6 +424,7 @@
${stem}/lf5/**/*.class,
${stem}/lf5/**/*.properties,
${stem}/lf5/**/*.gif,
+ ${stem}/localization/*.class,
${stem}/nt/*.class,
${stem}/xml/*.class,
${stem}/jmx/*.class,
@@ -481,6 +482,7 @@
org.apache.log4j.helpers,
org.apache.log4j.jmx,
org.apache.log4j.lf5,
+ org.apache.log4j.localization,
org.apache.log4j.net,
org.apache.log4j.nt,
org.apache.log4j.or,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]