You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2012/09/20 12:35:58 UTC
svn commit: r1387950 [1/2] - in
/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util: ./
logging/
Author: desruisseaux
Date: Thu Sep 20 10:35:57 2012
New Revision: 1387950
URL: http://svn.apache.org/viewvc?rev=1387950&view=rev
Log:
Added org.apache.sis.util.logging package.
Added:
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java (with props)
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java (with props)
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java (with props)
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java (with props)
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java (with props)
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java (with props)
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/package-info.java (with props)
Modified:
incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java
Modified: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java?rev=1387950&r1=1387949&r2=1387950&view=diff
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java (original)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Static.java Thu Sep 20 10:35:57 2012
@@ -39,6 +39,9 @@ package org.apache.sis.util;
* <td>Perform argument checks and throw {@link IllegalArgumentException} if needed.</td></tr>
* <tr><td>{@link org.apache.sis.util.Exceptions}</td>
* <td>Format a stack trace summary or change the exception message.</td></tr>
+ * <tr><td>{@link org.apache.sis.util.logging.Logging}</td>
+ * <td>Get a JDK {@linkplain java.util.logging.Logger logger}, which may be a wrapper around
+ * the <cite>Apache Commons Logging</cite> or <cite>Log4J</cite> framework.</td></tr>
* </table>
*
* @author Martin Desruisseaux (Geomatys)
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,101 @@
+/*
+ * 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.sis.util.logging;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Redirects logging to two loggers. This is used only when more than one {@link LoggerFactory}
+ * is found on the classpath. This should never happen, but if it happen anyway we will send the
+ * log records to all registered loggers in order to have a behavior slightly more determinist
+ * than picking an arbitrary logger.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-3.20)
+ * @version 0.3
+ * @module
+ */
+final class DualLogger extends LoggerAdapter {
+ /**
+ * The two loggers.
+ */
+ private final Logger first, second;
+
+ /**
+ * Creates a new logger which will redirects the event to the two specified loggers.
+ */
+ DualLogger(final String name, final Logger first, final Logger second) {
+ super(name);
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Sets the level for the two loggers.
+ */
+ @Override
+ public void setLevel(final Level level) {
+ first .setLevel(level);
+ second.setLevel(level);
+ }
+
+ /**
+ * Returns the finest level from the two loggers.
+ */
+ @Override
+ public Level getLevel() {
+ final Level v1 = first .getLevel();
+ final Level v2 = second.getLevel();
+ return (v1.intValue() < v2.intValue()) ? v1 : v2;
+ }
+
+ /**
+ * Returns {@code true} if the specified level is loggable by at least one logger.
+ */
+ @Override
+ public boolean isLoggable(final Level level) {
+ return first.isLoggable(level) || second.isLoggable(level);
+ }
+
+ /**
+ * Logs a record at the specified level.
+ */
+ @Override
+ public void log(final Level level, final String message) {
+ first .log(level, message);
+ second.log(level, message);
+ }
+
+ /**
+ * Logs a record at the specified level.
+ */
+ @Override
+ public void log(final Level level, final String message, final Throwable thrown) {
+ first .log(level, message, thrown);
+ second.log(level, message, thrown);
+ }
+
+ @Override public void severe (String message) {first.severe (message); second.severe (message);}
+ @Override public void warning(String message) {first.warning(message); second.warning(message);}
+ @Override public void info (String message) {first.info (message); second.info (message);}
+ @Override public void config (String message) {first.config (message); second.config (message);}
+ @Override public void fine (String message) {first.fine (message); second.fine (message);}
+ @Override public void finer (String message) {first.finer (message); second.finer (message);}
+ @Override public void finest (String message) {first.finest (message); second.finest (message);}
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,77 @@
+/*
+ * 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.sis.util.logging;
+
+import java.util.logging.Logger;
+
+
+/**
+ * Redirects logging to two loggers. This is used only when more than one {@link LoggerFactory}
+ * is found on the classpath. This should never happen, but if it happen anyway we will send the
+ * log records to all registered loggers in order to have a behavior slightly more determinist
+ * than picking an arbitrary logger.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-3.20)
+ * @version 0.3
+ * @module
+ */
+final class DualLoggerFactory extends LoggerFactory<DualLogger> {
+ /**
+ * The factories of loggers on which to delegate logging events.
+ */
+ private final LoggerFactory<?> first, second;
+
+ /**
+ * Creates a new factory which will delegate the logging events to the loggers
+ * created by the two given factories.
+ */
+ DualLoggerFactory(final LoggerFactory<?> first, final LoggerFactory<?> second) {
+ super(DualLogger.class);
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Returns the implementation to use for the logger of the specified name,
+ * or {@code null} if the logger would delegates to Java logging anyway.
+ */
+ @Override
+ protected DualLogger getImplementation(final String name) {
+ return new DualLogger(name, first.getLogger(name), second.getLogger(name));
+ }
+
+ /**
+ * Wraps the specified {@linkplain #getImplementation implementation} in a Java logger.
+ */
+ @Override
+ protected Logger wrap(final String name, final DualLogger implementation) {
+ return implementation;
+ }
+
+ /**
+ * Returns the {@linkplain #getImplementation implementation} wrapped by the specified logger,
+ * or {@code null} if none.
+ */
+ @Override
+ protected DualLogger unwrap(final Logger logger) {
+ if (logger instanceof DualLogger) {
+ return (DualLogger) logger;
+ }
+ return null;
+ }
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,726 @@
+/*
+ * 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.sis.util.logging;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Filter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.regex.Pattern;
+
+import org.apache.sis.util.Debug;
+
+
+/**
+ * An adapter that redirect all JDK logging events to an other logging framework. This
+ * class redefines the {@link #severe(String) severe}, {@link #warning(String) warning},
+ * {@link #info(String) info}, {@link #config(String) config}, {@link #fine(String) fine},
+ * {@link #finer(String) finer} and {@link #finest(String) finest} methods as <em>abstract</em>
+ * ones. Subclasses should implement those methods in order to map JDK logging levels to
+ * the backend logging framework.
+ * <p>
+ * All {@link #log(Level,String) log} methods are overridden in order to redirect to one of the
+ * above-cited methods. Note that this is the opposite approach than the JDK logging framework
+ * one, which implements everything on top of {@link Logger#log(LogRecord)}. This adapter is
+ * defined in terms of {@link #severe(String) severe} ⦠{@link #finest(String) finest} methods
+ * instead because external frameworks like <a href="http://commons.apache.org/logging/">Commons-logging</a>
+ * don't work with {@link LogRecord}, and sometime provides nothing else than convenience methods
+ * equivalent to {@link #severe(String) severe} … {@link #finest(String) finest}.
+ *
+ * {@section Restrictions}
+ * Because the configuration is expected to be fully controlled by the external logging
+ * framework, every configuration methods inherited from {@link Logger} are disabled:
+ * <p>
+ * <ul>
+ * <li>{@link #addHandler(Handler)}
+ * since the handling is performed by the external framework.</li>
+ *
+ * <li>{@link #setUseParentHandlers(boolean)}
+ * since this adapter never delegates to the parent handlers. This is consistent with the
+ * previous item and avoid mixing loggings from the external framework with JDK loggings.</li>
+ *
+ * <li>{@link #setParent(Logger)}
+ * since this adapter should not inherits any configuration from a parent logger using the
+ * JDK logging framework.</li>
+ *
+ * <li>{@link #setFilter(Filter)}
+ * for keeping this {@code LoggerAdapter} simple.</li>
+ * </ul>
+ * <p>
+ * Since {@code LoggerAdapter}s do not hold any configuration by themselves, it is not strictly
+ * necessary to {@linkplain java.util.logging.LogManager#addLogger add them to the log manager}.
+ * The adapters can be created, garbage-collected and recreated again while preserving their
+ * behavior since their configuration is entirely contained in the external logging framework.
+ *
+ * {@section Localization}
+ * This logger is always created without resource bundles. Localizations must be performed through
+ * explicit calls to {@code logrb} or {@link #log(LogRecord)} methods. This is sufficient for
+ * SIS needs, which performs all localizations through the later. Note that those methods
+ * will be slower in this {@code LoggerAdapter} than the default {@link Logger} because this
+ * adapter localizes and formats records immediately instead of letting the {@linkplain Handler}
+ * performs this work only if needed.
+ *
+ * {@section Logging levels}
+ * If a log record {@linkplain Level level} is not one of the predefined ones, then this class
+ * maps to the first level below the specified one. For example if a log record has some level
+ * between {@link Level#FINE FINE} and {@link Level#FINER FINER}, then the {@link #finer finer}
+ * method will be invoked. See {@link #isLoggable(Level)} for implementation tips taking advantage
+ * of this rule.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ *
+ * @see Logging
+ */
+public abstract class LoggerAdapter extends Logger {
+ /**
+ * The pattern to use for detecting {@link MessageFormat}.
+ */
+ private static final Pattern MESSAGE_FORMAT = Pattern.compile("\\{\\d+\\}");
+
+ /**
+ * Creates a new logger.
+ *
+ * @param name The logger name.
+ */
+ protected LoggerAdapter(final String name) {
+ super(name, null);
+ /*
+ * Must invokes the super-class method, because LoggerAdapter overrides it as a no-op.
+ */
+ super.setUseParentHandlers(false);
+ /*
+ * Sets the level to ALL as a matter of principle, but we will never check the level
+ * anyway (we will let the external logging framework do its own check). Note that we
+ * must invoke the method in the super-class because we want to set the java logging
+ * level, not the external framework level.
+ */
+ super.setLevel(Level.ALL);
+ }
+
+ /**
+ * Sets the level for this logger. Subclasses must redirect the call to the external
+ * logging framework, or do nothing if the level can not be changed programmatically.
+ *
+ * @param level The new value for the log level (may be null).
+ */
+ @Override
+ public abstract void setLevel(Level level);
+
+ /**
+ * Returns the level for this logger. Subclasses shall get this level from the
+ * external logging framework.
+ *
+ * @return The logger's level.
+ */
+ @Override
+ public abstract Level getLevel();
+
+ /**
+ * Returns the level for {@link #entering}, {@link #exiting} and {@link #throwing} methods.
+ * The default implementation returns {@link Level#FINER}, which is consistent with the
+ * value used in the JDK logging framework. Subclasses should override this method if
+ * a different debug level is wanted.
+ *
+ * @return The level to use for debugging informations.
+ */
+ @Debug
+ protected Level getDebugLevel() {
+ return Level.FINER;
+ }
+
+ /**
+ * Returns {@code true} if the specified level is loggable.
+ *
+ * {@section Implementation tip}
+ * Given that {@link Level#intValue} for all predefined levels are documented in the {@link Level}
+ * specification and are multiple of 100, given that integer divisions are rounded toward zero and
+ * given rule documented in this <a href="#skip-navbar_top">class javadoc</a>, then logging levels
+ * can be efficiently mapped to predefined levels using {@code switch} statements as below. This
+ * statement has good chances to be compiled to the {@code tableswitch} bytecode rather than
+ * {@code lookupswitch} (see
+ * <a href="http://java.sun.com/docs/books/jvms/second_edition/html/Compiling.doc.html#14942">Compiling
+ * Switches</a> in <cite>The Java Virtual Machine Specification</cite>).
+ *
+ * {@preformat java
+ * public boolean isLoggable(Level level) {
+ * final int n = level.intValue();
+ * switch (n / 100) {
+ * default: {
+ * // MAX_VALUE is a special value for Level.OFF. Otherwise and
+ * // if positive, fallthrough since we are greater than SEVERE.
+ * switch (n) {
+ * case Integer.MIN_VALUE: return true; // Level.ALL
+ * case Integer.MAX_VALUE: return false; // Level.OFF
+ * default: if (n < 0) return false;
+ * }
+ * }
+ * case 10: return isSevereEnabled();
+ * case 9: return isWarningEnabled();
+ * case 8: return isInfoEnabled();
+ * case 7: return isConfigEnabled();
+ * case 6: // fallthrough
+ * case 5: return isFineEnabled();
+ * case 4: return isFinerEnabled();
+ * case 3: return isFinestEnabled();
+ * case 2: // fallthrough
+ * case 1: // fallthrough
+ * case 0: return false;
+ * }
+ * }
+ * }
+ *
+ * @param level A message logging level.
+ * @return {@code true} if the given message level is currently being logged.
+ */
+ @Override
+ public abstract boolean isLoggable(Level level);
+
+ /**
+ * Logs a {@link Level#SEVERE SEVERE} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void severe(String message);
+
+ /**
+ * Logs a {@link Level#WARNING WARNING} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void warning(String message);
+
+ /**
+ * Logs an {@link Level#INFO INFO} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void info(String message);
+
+ /**
+ * Logs an {@link Level#CONFIG CONFIG} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void config(String message);
+
+ /**
+ * Logs a {@link Level#FINE FINE} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void fine(String message);
+
+ /**
+ * Logs a {@link Level#FINER FINER} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void finer(String message);
+
+ /**
+ * Logs a {@link Level#FINEST FINEST} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void finest(String message);
+
+ /**
+ * Logs a method entry to the {@linkplain #getDebugLevel debug level}. Compared to the
+ * default {@link Logger}, this implementation bypass the level check in order to let
+ * the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of method that is being entered.
+ */
+ @Override
+ public void entering(final String sourceClass, final String sourceMethod) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "ENTRY");
+ }
+
+ /**
+ * Logs a method entry to the {@linkplain #getDebugLevel debug level} with one parameter.
+ * Compared to the default {@link Logger}, this implementation bypass the level check in
+ * order to let the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of method that is being entered.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void entering(String sourceClass, String sourceMethod, Object param) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "ENTRY {0}", param);
+ }
+
+ /**
+ * Logs a method entry to the {@linkplain #getDebugLevel debug level} with many parameters.
+ * Compared to the default {@link Logger}, this implementation bypass the level check in
+ * order to let the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of method that is being entered.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void entering(final String sourceClass, final String sourceMethod, final Object[] params) {
+ final String message;
+ if (params == null) {
+ message = "ENTRY";
+ } else switch (params.length) {
+ case 0: message = "ENTRY"; break;
+ case 1: message = "ENTRY {0}"; break;
+ case 2: message = "ENTRY {0} {1}"; break;
+ default: {
+ final StringBuilder builder = new StringBuilder("ENTRY");
+ for (int i=0; i<params.length; i++) {
+ builder.append(" {").append(i).append('}');
+ }
+ message = builder.toString();
+ break;
+ }
+ }
+ logp(getDebugLevel(), sourceClass, sourceMethod, message, params);
+ }
+
+ /**
+ * Logs a method return to the {@linkplain #getDebugLevel debug level}. Compared to the
+ * default {@link Logger}, this implementation bypass the level check in order to let
+ * the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ */
+ @Override
+ public void exiting(final String sourceClass, final String sourceMethod) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "RETURN");
+ }
+
+ /**
+ * Logs a method return to the {@linkplain #getDebugLevel debug level}. Compared to the
+ * default {@link Logger}, this implementation bypass the level check in order to let
+ * the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param result Object that is being returned.
+ */
+ @Override
+ public void exiting(String sourceClass, String sourceMethod, Object result) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "RETURN {0}", result);
+ }
+
+ /**
+ * Logs a method failure to the {@linkplain #getDebugLevel debug level}. Compared to the
+ * default {@link Logger}, this implementation bypass the level check in order to let
+ * the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param thrown The Throwable that is being thrown.
+ */
+ @Override
+ public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "THROW", thrown);
+ }
+
+ /**
+ * Logs a record. The default implementation delegates to
+ * {@link #logrb(Level,String,String,String,String,Object[]) logrb}.
+ *
+ * @param record The log record to be published.
+ */
+ @Override
+ public void log(final LogRecord record) {
+ /*
+ * The filter should always be null since we overrode the 'setFilter' method as a no-op.
+ * But we check it anyway as matter of principle just in case some subclass overrides the
+ * 'getFilter()' method. This is the only method where we can do this check cheaply. Note
+ * that this is NOT the check for logging level; Filters are for user-specified criterions.
+ */
+ final Filter filter = getFilter();
+ if (filter != null && !filter.isLoggable(record)) {
+ return;
+ }
+ Level level = record.getLevel();
+ String sourceClass = record.getSourceClassName();
+ String sourceMethod = record.getSourceMethodName();
+ String bundleName = record.getResourceBundleName();
+ String message = record.getMessage();
+ Object[] params = record.getParameters();
+ Throwable thrown = record.getThrown();
+ ResourceBundle bundle = record.getResourceBundle();
+ boolean localized = false;
+ if (bundle != null) try {
+ message = bundle.getString(message);
+ localized = true; // Sets only if the above succeed.
+ } catch (MissingResourceException e) {
+ // The default Formatter.messageFormat implementation ignores this exception
+ // and uses the bundle key as the message, so we mimic its behavior here.
+ }
+ final boolean useThrown = (thrown != null) && (params == null || params.length == 0);
+ if (localized) {
+ // The message is already localized.
+ if (useThrown) {
+ logp(level, sourceClass, sourceMethod, message, thrown);
+ } else {
+ logp(level, sourceClass, sourceMethod, message, params);
+ }
+ } else {
+ // The message needs to be localized. The bundle was null but maybe bundleName is not.
+ // Futhermore subclass may have overridden the 'logrb' methods.
+ if (useThrown) {
+ logrb(level, sourceClass, sourceMethod, bundleName, message, thrown);
+ } else {
+ logrb(level, sourceClass, sourceMethod, bundleName, message, params);
+ }
+ }
+ }
+
+ /**
+ * Logs a record at the specified level. The default implementation delegates to one of the
+ * {@link #severe(String) severe}, {@link #warning(String) warning}, {@link #info(String) info},
+ * {@link #config(String) config}, {@link #fine(String) fine}, {@link #finer(String) finer} or
+ * {@link #finest(String) finest} methods according the supplied level.
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ */
+ @Override
+ @SuppressWarnings("fallthrough")
+ public void log(final Level level, final String message) {
+ final int n = level.intValue();
+ switch (n / 100) {
+ default: {
+ if (n < 0 || n == Integer.MAX_VALUE) break;
+ // MAX_VALUE is a special value for Level.OFF. Otherwise and
+ // if positive, fallthrough since we are greater than SEVERE.
+ }
+ case 10: severe (message); break;
+ case 9: warning(message); break;
+ case 8: info (message); break;
+ case 7: config (message); break;
+ case 6:
+ case 5: fine (message); break;
+ case 4: finer (message); break;
+ case 3: finest (message); break;
+ case 2: /* Logging OFF */
+ case 1: /* Logging OFF */
+ case 0: /* Logging OFF */ break;
+ }
+ }
+
+ /**
+ * Logs a record at the specified level. The default implementation discards the exception
+ * and delegates to <code>{@linkplain #log(Level,String) log}(level, message)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ * @param thrown Throwable associated with log message.
+ */
+ @Override
+ public void log(final Level level, final String message, final Throwable thrown) {
+ log(level, message);
+ }
+
+ /**
+ * Logs a record at the specified level. The default implementation delegates to
+ * <code>{@linkplain #log(Level,String,Object[]) log}(level, message, params)</code>
+ * where the {@code params} array is built from the {@code param} object.
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void log(final Level level, final String message, final Object param) {
+ log(level, message, asArray(param));
+ }
+
+ /**
+ * Logs a record at the specified level.
+ * The defaut implementation formats the message immediately, then delegates to
+ * <code>{@linkplain #log(Level,String) log}(level, message)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void log(final Level level, final String message, final Object[] params) {
+ log(level, format(message, params));
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation discards
+ * the source class and source method, then delegates to
+ * <code>{@linkplain #log(Level,String) log}(level, message)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message)
+ {
+ log(level, message);
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation discards
+ * the source class and source method, then delegates to
+ * <code>{@linkplain #log(Level,String,Throwable) log}(level, message, thrown)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ * @param thrown Throwable associated with log message.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message, final Throwable thrown)
+ {
+ log(level, message, thrown);
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation delegates to
+ * <code>{@linkplain #logp(Level,String,String,String,Object[]) logp}(level, sourceClass,
+ * sourceMethod, message, params)</code> where the {@code params} array is built from the
+ * {@code param} object.
+ * <p>
+ * Note that {@code sourceClass} and {@code sourceMethod} will be discarded unless the
+ * target {@link #logp(Level,String,String,String) logp} method has been overridden.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message, final Object param)
+ {
+ logp(level, sourceClass, sourceMethod, message, asArray(param));
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation formats the message
+ * immediately, then delegates to <code>{@linkplain #logp(Level,String,String,String)
+ * logp}(level, sourceClass, sourceMethod, message)</code>.
+ * <p>
+ * Note that {@code sourceClass} and {@code sourceMethod} will be discarded unless the
+ * target {@link #logp(Level,String,String,String) logp} method has been overridden.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message, final Object[] params)
+ {
+ logp(level, sourceClass, sourceMethod, format(message, params));
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to <code>{@linkplain #logp(Level,String,String,String)
+ * logp}(level, sourceClass, sourceMethod, message)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, final String message)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message));
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to <code>{@linkplain #logp(Level,String,String,String,
+ * Throwable) logp}(level, sourceClass, sourceMethod, message, thrown)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ * @param thrown Throwable associated with log message.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, final String message, final Throwable thrown)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message), thrown);
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to <code>{@linkplain #logp(Level,String,String,String,
+ * Object) logp}(level, sourceClass, sourceMethod, message, param)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, final String message, final Object param)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message), param);
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to <code>{@linkplain #logp(Level,String,String,String,
+ * Object[]) logp}(level, sourceClass, sourceMethod, message, params)</code>.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, String message, final Object[] params)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message), params);
+ }
+
+ /**
+ * Do nothing since this logger adapter does not supports handlers.
+ * The configuration should be fully controlled by the external logging framework
+ * (e.g. <a href="http://commons.apache.org/logging/">Commons-logging</a>) instead,
+ * which is not expected to use {@link Handler} objects.
+ *
+ * @param handler A logging handler, ignored in default implementation.
+ */
+ @Override
+ public void addHandler(Handler handler) {
+ }
+
+ /**
+ * Do nothing since this logger adapter does not support handlers.
+ *
+ * @param handler A logging handler, ignored in default implementation.
+ */
+ @Override
+ public void removeHandler(Handler handler) {
+ }
+
+ /**
+ * Do nothing since this logger never use parent handlers. This is consistent
+ * with {@link #addHandler} not allowing to add any handlers, and avoid mixing
+ * loggings from the external framework with JDK loggings.
+ *
+ * @param useParentHandlers Ignored in default implementation.
+ */
+ @Override
+ public void setUseParentHandlers(boolean useParentHandlers) {
+ }
+
+ /**
+ * Do nothing since this logger adapter does not support arbitrary parents.
+ * More specifically, it should not inherits any configuration from a parent
+ * logger using the JDK logging framework.
+ *
+ * @param parent Ignored in default implementation.
+ */
+ @Override
+ public void setParent(Logger parent) {
+ }
+
+ /**
+ * Do nothing since this logger adapter does not support filters. It is difficult to query
+ * efficiently the filter in this {@code LoggerAdapter} architecture (e.g. we would need to
+ * make sure that {@link Filter#isLoggable} is invoked only once even if a {@code log} call
+ * is cascaded into many other {@code log} calls, and this test must works in multi-threads
+ * environment).
+ *
+ * @param filter Ignored in default implementation.
+ */
+ @Override
+ public void setFilter(Filter filter) {
+ }
+
+ /**
+ * Wraps the specified object in an array. This is a helper method for
+ * {@code log(..., Object)} methods that delegate their work to {@code log(..., Object[])}
+ */
+ private static Object[] asArray(final Object param) {
+ return (param != null) ? new Object[] {param} : null;
+ }
+
+ /**
+ * Formats the specified message. This is a helper method for
+ * {@code log(..., Object[])} methods that delegate their work to {@code log(...)}
+ */
+ private static String format(String message, final Object[] params) {
+ if (params != null && params.length != 0) {
+ if (MESSAGE_FORMAT.matcher(message).find()) try {
+ message = MessageFormat.format(message, params);
+ } catch (IllegalArgumentException e) {
+ // The default Formatter.messageFormat implementation ignores this exception
+ // and uses the pattern as the message, so we mimic its behavior here.
+ }
+ }
+ return message;
+ }
+
+ /**
+ * Localizes the specified message. This is a helper method for
+ * {@code logrb(...)} methods that delegate their work to {@code logp(...)}
+ */
+ private static String localize(final String bundleName, String message) {
+ if (bundleName != null) try {
+ message = ResourceBundle.getBundle(bundleName).getString(message);
+ } catch (MissingResourceException e) {
+ // The default Formatter.messageFormat implementation ignores this exception
+ // and uses the bundle key as the message, so we mimic its behavior here.
+ }
+ return message;
+ }
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,114 @@
+/*
+ * 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.sis.util.logging;
+
+import java.util.logging.Logger;
+import net.jcip.annotations.ThreadSafe;
+
+
+/**
+ * A factory for Java {@link Logger} wrapping an other logging framework. This factory is used
+ * only when wanting to log to an other framework than JDK logging. The {@link #getLogger(String)}
+ * method returns some subclass of {@link Logger} (typically {@link LoggerAdapter}) that forward
+ * directly all log methods to an other framework.
+ *
+ * @param <L> The type of loggers used for the implementation backend.
+ * This is the type used by external frameworks like Log4J.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ *
+ * @see Logging
+ * @see LoggerAdapter
+ */
+@ThreadSafe
+public abstract class LoggerFactory<L> {
+ /**
+ * The logger class. We ask for this information right at construction time in order to
+ * force a {@link NoClassDefFoundError} early rather than only the first time a message
+ * is logged.
+ */
+ private final Class<L> loggerClass;
+
+ /**
+ * Creates a new factory.
+ *
+ * @param loggerClass The class of the wrapped logger.
+ */
+ protected LoggerFactory(final Class<L> loggerClass) {
+ this.loggerClass = loggerClass;
+ }
+
+ /**
+ * Returns the logger of the specified name, or {@code null} if the JDK logging framework
+ * should be used.
+ *
+ * @param name The name of the logger.
+ * @return The logger, or {@code null} if the JDK logging framework should be used.
+ */
+ public Logger getLogger(final String name) {
+ final L target = getImplementation(name);
+ if (target == null) {
+ return null;
+ }
+ return wrap(name, target);
+ }
+
+ /**
+ * Returns the base class of objects to be returned by {@link #getImplementation(String)}.
+ * The class depends on the underlying logging framework (Log4J, SLF4J, <i>etc.</i>).
+ *
+ * @return The type of loggers used for the implementation backend.
+ */
+ public Class<L> getImplementationClass() {
+ return loggerClass;
+ }
+
+ /**
+ * Returns the implementation to use for the logger of the specified name. The object to be
+ * returned depends on the logging framework (Log4J, SLF4J, <i>etc.</i>). If the target
+ * framework redirects logging events to JDK logging, then this method shall return
+ * {@code null} since we should not use wrapper at all.
+ *
+ * @param name The name of the logger.
+ * @return The logger as an object of the target logging framework (Log4J, SLF4J,
+ * <i>etc.</i>), or {@code null} if the target framework would redirect
+ * to the JDK logging framework.
+ */
+ protected abstract L getImplementation(String name);
+
+ /**
+ * Wraps the specified {@linkplain #getImplementation(String) implementation} in a JDK logger.
+ *
+ * @param name The name of the logger.
+ * @param implementation An implementation returned by {@link #getImplementation(String)}.
+ * @return A new logger wrapping the specified implementation.
+ */
+ protected abstract Logger wrap(String name, L implementation);
+
+ /**
+ * Returns the {@linkplain #getImplementation(String) implementation} wrapped by the specified
+ * logger, or {@code null} if none. If the specified logger is not an instance of the expected
+ * class, then this method should returns {@code null}.
+ *
+ * @param logger The logger to test.
+ * @return The implementation wrapped by the specified logger, or {@code null} if none.
+ */
+ protected abstract L unwrap(Logger logger);
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,637 @@
+/*
+ * 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.sis.util.logging;
+
+import java.util.Comparator;
+import java.util.ServiceLoader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+
+import org.apache.sis.util.Configuration;
+import org.apache.sis.util.Static;
+import org.apache.sis.util.Exceptions;
+import org.apache.sis.util.Classes;
+
+import static java.util.Arrays.binarySearch;
+import static org.apache.sis.util.Arrays.insert;
+
+
+/**
+ * A set of utilities method for configuring loggings in SIS. Library implementors shall fetch
+ * their logger using the {@link #getLogger(Class)} static method provided in this class rather
+ * than {@link Logger#getLogger(String)}, in order to give SIS a chance to redirect the logging
+ * to an other framework like <A HREF="http://commons.apache.org/logging/">Commons-logging</A> or
+ * <A HREF="http://logging.apache.org/log4j">Log4J</A>.
+ * <p>
+ * This method provides also a {@link #log(Class, String, LogRecord)} convenience static method, which
+ * {@linkplain LogRecord#setLoggerName set the logger name} of the given record before to log it.
+ * An other worthy static method is {@link #unexpectedException(Class, String, Throwable)}, for
+ * reporting an anomalous but nevertheless non-fatal exception.
+ *
+ * {@section Configuration}
+ * The log records can be redirected explicitly to an other logging framework using the
+ * following method call (replace {@link LoggerFactory#COMMONS_LOGGING} by
+ * {@link LoggerFactory#LOG4J} or an other framework if desired):
+ *
+ * {@preformat java
+ * Logging.SIS.setLoggerFactory(LoggerFactory.COMMONS_LOGGING);
+ * }
+ *
+ * Note however that the above method invocation is performed automatically if the
+ * {@code sis-logging-commons.jar} or the {@code sis-logging-log4j.jar} file is
+ * found on the classpath, so it usually doesn't need to be invoked explicitely.
+ * See the {@link #scanLoggerFactory()} method for more details on automatic logger
+ * factory registration.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+public final class Logging extends Static {
+ /**
+ * Compares {@link Logging} or {@link String} objects for alphabetical order.
+ */
+ private static final Comparator<Object> COMPARATOR = new Comparator<Object>() {
+ @Override public int compare(final Object o1, final Object o2) {
+ final String n1 = (o1 instanceof Logging) ? ((Logging) o1).name : o1.toString();
+ final String n2 = (o2 instanceof Logging) ? ((Logging) o2).name : o2.toString();
+ return n1.compareTo(n2);
+ }
+ };
+
+ /**
+ * An empty array of loggings. Also used for locks.
+ */
+ private static final Logging[] EMPTY = new Logging[0];
+
+ /**
+ * Logging configuration that apply to all packages.
+ */
+ public static final Logging ALL = new Logging();
+ static { // Must be run after ALL assignation and before SIS (or any other Logging) creation.
+ ALL.scanLoggerFactory();
+ }
+
+ /**
+ * Logging configuration that apply only to "{@code org.apache.sis}" packages.
+ */
+ public static final Logging SIS = getLogging("org.apache.sis");
+
+ /**
+ * The name of the base package.
+ */
+ public final String name;
+
+ /**
+ * The children {@link Logging} objects.
+ * <p>
+ * The plain array used there is not efficient for adding new items (an {@code ArrayList}
+ * would be more efficient), but we assume that very few new items will be added. Furthermore
+ * a plain array is efficient for reading, and the later is way more common than the former.
+ */
+ private Logging[] children = EMPTY;
+
+ /**
+ * The factory for creating loggers, or {@code null} if none. If {@code null}
+ * (the default), then the standard JDK logging framework will be used.
+ *
+ * @see #setLoggerFactory(LoggerFactory)
+ */
+ private LoggerFactory<?> factory;
+
+ /**
+ * {@code true} if every {@link Logging} instances use the same {@link LoggerFactory}.
+ * This is an optimization for a very common case.
+ */
+ private static boolean sameLoggerFactory = true;
+
+ /**
+ * Creates an instance for the root logger. This constructor should not be used for anything
+ * else than {@link #ALL} construction; use the {@link #getLogging(String)} method instead.
+ */
+ private Logging() {
+ name = "";
+ }
+
+ /**
+ * Creates an instance for the specified base logger. This constructor should
+ * not be public; use the {@link #getLogging(String)} method instead.
+ *
+ * @param parent The parent {@code Logging} instance.
+ * @param name The logger name for the new instance.
+ */
+ private Logging(final Logging parent, final String name) {
+ this.name = name;
+ factory = parent.factory;
+ assert name.startsWith(parent.name) : name;
+ }
+
+ /**
+ * Logs the given record to the logger associated to the given class.
+ * This convenience method performs the following steps:
+ * <p>
+ * <ul>
+ * <li>Get the logger using {@link #getLogger(Class)};</li>
+ * <li>{@linkplain LogRecord#setLoggerName(String) Set the logger name} of the given record,
+ * if not already set;</li>
+ * <li>Unconditionally {@linkplain LogRecord#setSourceClassName(String) set the source class
+ * name} to the {@linkplain Class#getCanonicalName() canonical name} of the given class;</li>
+ * <li>Unconditionally {@linkplain LogRecord#setSourceMethodName(String) set the source method
+ * name} to the given value;</li>
+ * <li>{@linkplain Logger#log(LogRecord) Log} the modified record.</li>
+ * </ul>
+ *
+ * @param classe The class for which to obtain a logger.
+ * @param method The name of the method which is logging a record.
+ * @param record The record to log.
+ */
+ public static void log(final Class<?> classe, final String method, final LogRecord record) {
+ record.setSourceClassName(classe.getCanonicalName());
+ record.setSourceMethodName(method);
+ final Logger logger = getLogger(classe);
+ if (record.getLoggerName() == null) {
+ record.setLoggerName(logger.getName());
+ }
+ logger.log(record);
+ }
+
+ /**
+ * Returns a logger for the specified class. This convenience method invokes
+ * {@link #getLogger(String)} with the package name as the logger name.
+ *
+ * @param classe The class for which to obtain a logger.
+ * @return A logger for the specified class.
+ */
+ public static Logger getLogger(Class<?> classe) {
+ Class<?> outer;
+ while ((outer = classe.getEnclosingClass()) != null) {
+ classe = outer;
+ }
+ String name = classe.getName();
+ final int separator = name.lastIndexOf('.');
+ name = (separator >= 1) ? name.substring(0, separator) : "";
+ return getLogger(name);
+ }
+
+ /**
+ * Returns a logger for the specified name. If a {@linkplain LoggerFactory logger factory} has
+ * been set, then this method first {@linkplain LoggerFactory#getLogger ask to the factory}.
+ * It gives SIS a chance to redirect logging events to
+ * <A HREF="http://commons.apache.org/logging/">commons-logging</A>
+ * or some equivalent framework.
+ * <p>
+ * If no factory was found or if the factory choose to not redirect the loggings, then this
+ * method returns the usual <code>{@linkplain Logger#getLogger Logger.getLogger}(name)</code>.
+ *
+ * @param name The logger name.
+ * @return A logger for the specified name.
+ */
+ public static Logger getLogger(final String name) {
+ synchronized (EMPTY) {
+ final Logging logging = sameLoggerFactory ? ALL : getLogging(name, false);
+ if (logging != null) { // Paranoiac check ('getLogging' should not returns null).
+ final LoggerFactory<?> factory = logging.factory;
+ assert getLogging(name, false).factory == factory : name;
+ if (factory != null) {
+ final Logger logger = factory.getLogger(name);
+ if (logger != null) {
+ return logger;
+ }
+ }
+ }
+ }
+ return Logger.getLogger(name);
+ }
+
+ /**
+ * Returns a {@code Logging} instance for the specified base logger. This instance is
+ * used for controlling logging configuration in SIS. For example methods like
+ * {@link #forceMonolineConsoleOutput(Level)} are invoked on a {@code Logging} instance.
+ * <p>
+ * {@code Logging} instances follow the same hierarchy than {@link Logger}, i.e.
+ * {@code "org.apache.sis"} is the parent of {@code "org.apache.sis.referencing"},
+ * {@code "org.apache.sis.metadata"}, <i>etc</i>.
+ *
+ * @param name The base logger name.
+ * @return The logging instance for the given name.
+ */
+ public static Logging getLogging(final String name) {
+ synchronized (EMPTY) {
+ return getLogging(name, true);
+ }
+ }
+
+ /**
+ * Returns a logging instance for the specified base logger. If no instance is found for
+ * the specified name and {@code create} is {@code true}, then a new instance will be
+ * created. Otherwise the nearest parent is returned.
+ *
+ * @param base The root logger name.
+ * @param create {@code true} if this method is allowed to create new {@code Logging} instance.
+ * @return The logging instance for the given name.
+ */
+ private static Logging getLogging(final String base, final boolean create) {
+ assert Thread.holdsLock(EMPTY);
+ Logging logging = ALL;
+ if (!base.isEmpty()) {
+ int offset = 0;
+ do {
+ Logging[] children = logging.children;
+ offset = base.indexOf('.', offset);
+ final String name = (offset >= 0) ? base.substring(0, offset) : base;
+ int i = binarySearch(children, name, COMPARATOR);
+ if (i < 0) {
+ // No exact match found.
+ if (!create) {
+ // We are not allowed to create new Logging instance.
+ // 'logging' is the nearest parent, so stop the loop now.
+ break;
+ }
+ i = ~i;
+ children = insert(children, i, 1);
+ children[i] = new Logging(logging, name);
+ logging.children = children;
+ }
+ logging = children[i];
+ } while (++offset != 0);
+ }
+ return logging;
+ }
+
+ /**
+ * Returns the logger factory, or {@code null} if none. This method returns the logger set
+ * by the last call to {@link #setLoggerFactory(LoggerFactory)} on this {@code Logging}
+ * instance or on one of its parent.
+ *
+ * @return The current logger factory.
+ */
+ public LoggerFactory<?> getLoggerFactory() {
+ synchronized (EMPTY) {
+ return factory;
+ }
+ }
+
+ /**
+ * Sets a new logger factory for this {@code Logging} instance and every children. The
+ * specified factory will be used by <code>{@linkplain #getLogger(String) getLogger}(name)</code>
+ * when {@code name} is this {@code Logging} name or one of its children.
+ * <p>
+ * If the factory is set to {@code null} (the default), then the standard Logging framework
+ * will be used.
+ *
+ * @param factory The new logger factory, or {@code null} if none.
+ */
+ @Configuration
+ public void setLoggerFactory(final LoggerFactory<?> factory) {
+ synchronized (EMPTY) {
+ this.factory = factory;
+ for (int i=0; i<children.length; i++) {
+ children[i].setLoggerFactory(factory);
+ }
+ sameLoggerFactory = sameLoggerFactory(ALL.children, ALL.factory); // NOSONAR: really want static field.
+ }
+ }
+
+ /**
+ * Returns {@code true} if all children use the specified factory.
+ * Used in order to detect a possible optimization for this very common case.
+ */
+ private static boolean sameLoggerFactory(final Logging[] children, final LoggerFactory<?> factory) {
+ assert Thread.holdsLock(EMPTY);
+ for (int i=0; i<children.length; i++) {
+ final Logging logging = children[i];
+ if (logging.factory != factory || !sameLoggerFactory(logging.children, factory)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Scans the classpath for logger factories. The fully qualified factory classname shall be
+ * declared in the following file:
+ *
+ * {@preformat text
+ * META-INF/services/org.apache.sis.util.logging.LoggerFactory
+ * }
+ *
+ * The first factory found on the classpath is given to {@link #setLoggerFactory(String)}.
+ * If it can't be used (for example because of missing dependency), then the second factory
+ * is tried, <i>etc.</i> until an acceptable factory is found.
+ * <p>
+ * This method usually doesn't need to be invoked explicitly, since it is automatically
+ * invoked on {@code Logging} class initialization. However developers may invoke it if
+ * new {@code LoggerFactory}s are added later on the classpath of a running JVM.
+ */
+ @Configuration
+ public void scanLoggerFactory() {
+ LoggerFactory<?> factory = null;
+ for (final LoggerFactory<?> found : ServiceLoader.load(LoggerFactory.class)) {
+ if (factory == null) {
+ factory = found;
+ } else {
+ factory = new DualLoggerFactory(factory, found);
+ }
+ }
+ setLoggerFactory(factory);
+ }
+
+ /**
+ * Flushes all {@linkplain Handler handlers} used by the logger named {@link #name}.
+ * If that logger {@linkplain Logger#getUseParentHandlers() uses parent handlers},
+ * then those handlers will be flushed as well.
+ * <p>
+ * If the log records seem to be interleaved with the content of {@link System#out}
+ * or {@link System#err}, invoking this method before to write to the standard streams
+ * may help.
+ *
+ * @see Handler#flush()
+ */
+ public void flush() {
+ for (Logger logger=getLogger(name); logger!=null; logger=logger.getParent()) {
+ for (final Handler handler : logger.getHandlers()) {
+ handler.flush();
+ }
+ if (!logger.getUseParentHandlers()) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Invoked when an unexpected error occurs. This method logs a message at the
+ * {@link Level#WARNING WARNING} level to the specified logger. The originating
+ * class name and method name are inferred from the error stack trace, using the
+ * first {@linkplain StackTraceElement stack trace element} for which the class
+ * name is inside a package or sub-package of the logger name. For example if
+ * the logger name is {@code "org.apache.sis.image"}, then this method will uses
+ * the first stack trace element where the fully qualified class name starts with
+ * {@code "org.apache.sis.image"} or {@code "org.apache.sis.image.io"}, but not
+ * {@code "org.apache.sis.imageio"}.
+ *
+ * @param logger Where to log the error.
+ * @param error The error that occurred.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the {@link Level#WARNING WARNING} level.
+ */
+ public static boolean unexpectedException(final Logger logger, final Throwable error) {
+ return unexpectedException(logger, null, null, error, Level.WARNING);
+ }
+
+ /**
+ * Invoked when an unexpected error occurs. This method logs a message at the
+ * {@link Level#WARNING WARNING} level to the specified logger. The originating
+ * class name and method name can optionally be specified. If any of them is
+ * {@code null}, then it will be inferred from the error stack trace as in
+ * {@link #unexpectedException(Logger, Throwable)}.
+ * <p>
+ * Explicit value for class and method names are sometime preferred to automatic
+ * inference for the following reasons:
+ * <p>
+ * <ul>
+ * <li>Automatic inference is not 100% reliable, since the Java Virtual Machine
+ * is free to omit stack frame in optimized code.</li>
+ * <li>When an exception occurred in a private method used internally by a public
+ * method, we sometime want to log the warning for the public method instead,
+ * since the user is not expected to know anything about the existence of the
+ * private method. If a developer really want to know about the private method,
+ * the stack trace is still available anyway.</li>
+ * </ul>
+ *
+ * @param logger Where to log the error.
+ * @param classe The class where the error occurred, or {@code null}.
+ * @param method The method where the error occurred, or {@code null}.
+ * @param error The error.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the {@link Level#WARNING WARNING} level.
+ *
+ * @see #recoverableException(Logger, Class, String, Throwable)
+ * @see #severeException(Logger, Class, String, Throwable)
+ */
+ public static boolean unexpectedException(final Logger logger, final Class<?> classe,
+ final String method, final Throwable error)
+ {
+ final String classname = (classe != null) ? classe.getName() : null;
+ return unexpectedException(logger, classname, method, error, Level.WARNING);
+ }
+
+ /**
+ * Invoked when an unexpected error occurs. This method logs a message at the
+ * {@link Level#WARNING WARNING} level to a logger inferred from the given class.
+ *
+ * @param classe The class where the error occurred.
+ * @param method The method where the error occurred, or {@code null}.
+ * @param error The error.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the {@link Level#WARNING WARNING} level.
+ *
+ * @see #recoverableException(Class, String, Throwable)
+ */
+ public static boolean unexpectedException(Class<?> classe, String method, Throwable error) {
+ return unexpectedException((Logger) null, classe, method, error);
+ }
+
+ /**
+ * Implementation of {@link #unexpectedException(Logger, Class, String, Throwable)}.
+ *
+ * @param logger Where to log the error, or {@code null}.
+ * @param classe The fully qualified class name where the error occurred, or {@code null}.
+ * @param method The method where the error occurred, or {@code null}.
+ * @param error The error.
+ * @param level The logging level.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the specified level.
+ */
+ private static boolean unexpectedException(Logger logger, String classe, String method,
+ final Throwable error, final Level level)
+ {
+ /*
+ * Checks if loggable, inferring the logger from the classe name if needed.
+ */
+ if (error == null) {
+ return false;
+ }
+ if (logger == null && classe != null) {
+ final int separator = classe.lastIndexOf('.');
+ final String paquet = (separator >= 1) ? classe.substring(0, separator-1) : "";
+ logger = getLogger(paquet);
+ }
+ if (logger != null && !logger.isLoggable(level)) {
+ return false;
+ }
+ /*
+ * Loggeable, so complete the null argument from the stack trace if we can.
+ */
+ if (logger==null || classe==null || method==null) {
+ String paquet = (logger != null) ? logger.getName() : null;
+ final StackTraceElement[] elements = error.getStackTrace();
+ for (int i=0; i<elements.length; i++) {
+ /*
+ * Searches for the first stack trace element with a classname matching the
+ * expected one. We compare preferably against the name of the class given
+ * in argument, or against the logger name (taken as the package name) otherwise.
+ */
+ final StackTraceElement element = elements[i];
+ final String classname = element.getClassName();
+ if (classe != null) {
+ if (!classname.equals(classe)) {
+ continue;
+ }
+ } else if (paquet != null) {
+ if (!classname.startsWith(paquet)) {
+ continue;
+ }
+ final int length = paquet.length();
+ if (classname.length() > length) {
+ // We expect '.' but we accept also '$' or end of string.
+ final char separator = classname.charAt(length);
+ if (Character.isJavaIdentifierPart(separator)) {
+ continue;
+ }
+ }
+ }
+ /*
+ * Now that we have a stack trace element from the expected class (or any
+ * element if we don't know the class), make sure that we have the right method.
+ */
+ final String methodName = element.getMethodName();
+ if (method != null && !methodName.equals(method)) {
+ continue;
+ }
+ /*
+ * Now computes every values that are null, and stop the loop.
+ */
+ if (paquet == null) {
+ final int separator = classname.lastIndexOf('.');
+ paquet = (separator >= 1) ? classname.substring(0, separator-1) : "";
+ logger = getLogger(paquet);
+ if (!logger.isLoggable(level)) {
+ return false;
+ }
+ }
+ if (classe == null) {
+ classe = classname;
+ }
+ if (method == null) {
+ method = methodName;
+ }
+ break;
+ }
+ /*
+ * The logger may stay null if we have been unable to find a suitable
+ * stack trace. Fallback on the global logger.
+ */
+ if (logger == null) {
+ logger = getLogger(Logger.GLOBAL_LOGGER_NAME);
+ if (!logger.isLoggable(level)) {
+ return false;
+ }
+ }
+ }
+ /*
+ * Now prepare the log message. If we have been unable to figure out a source class and
+ * method name, we will fallback on JDK logging default mechanism, which may returns a
+ * less relevant name than our attempt to use the logger name as the package name.
+ */
+ final StringBuilder buffer = new StringBuilder(Classes.getShortClassName(error));
+ String message = error.getLocalizedMessage();
+ if (message != null) {
+ buffer.append(": ").append(message);
+ }
+ message = buffer.toString();
+ message = Exceptions.formatChainedMessages(message, error);
+ final LogRecord record = new LogRecord(level, message);
+ if (classe != null) {
+ record.setSourceClassName(classe);
+ }
+ if (method != null) {
+ record.setSourceMethodName(method);
+ }
+ if (level.intValue() > 500) {
+ record.setThrown(error);
+ }
+ record.setLoggerName(logger.getName());
+ logger.log(record);
+ return true;
+ }
+
+ /**
+ * Invoked when a recoverable error occurs. This method is similar to
+ * {@link #unexpectedException(Class,String,Throwable) unexpectedException}
+ * except that it doesn't log the stack trace and uses a lower logging level.
+ *
+ * @param classe The class where the error occurred.
+ * @param method The method name where the error occurred.
+ * @param error The error.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the {@link Level#FINE FINE} level.
+ *
+ * @see #unexpectedException(Class, String, Throwable)
+ */
+ public static boolean recoverableException(final Class<?> classe, final String method,
+ final Throwable error)
+ {
+ return recoverableException(null, classe, method, error);
+ }
+
+ /**
+ * Invoked when a recoverable error occurs. This method is similar to
+ * {@link #unexpectedException(Logger,Class,String,Throwable) unexpectedException}
+ * except that it doesn't log the stack trace and uses a lower logging level.
+ *
+ * @param logger Where to log the error.
+ * @param classe The class where the error occurred.
+ * @param method The method name where the error occurred.
+ * @param error The error.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the {@link Level#FINE FINE} level.
+ *
+ * @see #unexpectedException(Logger, Class, String, Throwable)
+ * @see #severeException(Logger, Class, String, Throwable)
+ */
+ public static boolean recoverableException(final Logger logger, final Class<?> classe,
+ final String method, final Throwable error)
+ {
+ final String classname = (classe != null) ? classe.getName() : null;
+ return unexpectedException(logger, classname, method, error, Level.FINE);
+ }
+
+ /**
+ * Invoked when a severe error occurs. This method is similar to
+ * {@link #unexpectedException(Logger,Class,String,Throwable) unexpectedException}
+ * except that it logs the message at the {@link Level#SEVERE SEVERE} level.
+ *
+ * @param logger Where to log the error.
+ * @param classe The class where the error occurred.
+ * @param method The method name where the error occurred.
+ * @param error The error.
+ * @return {@code true} if the error has been logged, or {@code false} if the logger
+ * doesn't log anything at the {@link Level#SEVERE SEVERE} level.
+ *
+ * @see #unexpectedException(Logger, Class, String, Throwable)
+ * @see #recoverableException(Logger, Class, String, Throwable)
+ */
+ public static boolean severeException(final Logger logger, final Class<?> classe,
+ final String method, final Throwable error)
+ {
+ final String classname = (classe != null) ? classe.getName() : null;
+ return unexpectedException(logger, classname, method, error, Level.SEVERE);
+ }
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,179 @@
+/*
+ * 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.sis.util.logging;
+
+import java.util.logging.Level;
+import java.util.concurrent.TimeUnit;
+import org.apache.sis.util.Configuration;
+
+import static org.apache.sis.util.ArgumentChecks.ensurePositive;
+
+
+/**
+ * Logging levels for measurements of execution time. Different logging levels - {@link #SLOW},
+ * {@link #SLOWER} and {@link #SLOWEST} - are provided in order to log only the events taking
+ * more than some time duration. For example the console could log only the slowest events,
+ * while a file could log all events considered slow.
+ * <p>
+ * Every levels defined in this class have a {@linkplain #intValue() value} between the
+ * {@link Level#FINE} and {@link Level#CONFIG} values. Consequently performance logging are
+ * disabled by default, and enabling them imply enabling configuration logging too. This is
+ * done that way because the configuration typically have a significant impact on performance.
+ *
+ * {@section Enabling performance logging}
+ * Performance logging can be enabled in various ways. Among others:
+ * <p>
+ * <ul>
+ * <li>The {@code $JAVA_HOME/lib/logging.properties} file can be edited in order to log
+ * messages at the {@code FINE} level, at least for the packages of interest.</li>
+ * <li>The {@link Logging#forceMonolineConsoleOutput(Level)} convenience method
+ * can be invoked.</li>
+ * </ul>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-3.16)
+ * @version 0.3
+ * @module
+ */
+public final class PerformanceLevel extends Level {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 6055684381688936293L;
+
+ /*
+ * IMPLEMENTATION NOTE: The level values used in the constants below are also used
+ * in the 'switch' statements of the 'setMinDuration(...)' method. If those values
+ * are modified, don't forget to update also the switch statements!!
+ */
+
+ /**
+ * The level for logging all time measurements, regardless of their duration.
+ * The {@linkplain #intValue() value} of this level is 600.
+ */
+ public static final PerformanceLevel PERFORMANCE = new PerformanceLevel("PERFORMANCE", 600, 0);
+
+ /**
+ * The level for logging relatively slow events. By default, only events having an execution
+ * time equals or greater than 0.1 second are logged at this level. However this threshold
+ * can be changed by a call to <code>SLOW.{@linkplain #setMinDuration(long, TimeUnit)}</code>.
+ */
+ public static final PerformanceLevel SLOW = new PerformanceLevel("SLOW", 610, 100000000L);
+
+ /**
+ * The level for logging only events slower than the ones logged at the {@link #SLOW} level.
+ * By default, only events having an execution time equals or greater than 1 second are
+ * logged at this level. However this threshold can be changed by a call to
+ * <code>SLOWER.{@linkplain #setMinDuration(long, TimeUnit)}</code>.
+ */
+ public static final PerformanceLevel SLOWER = new PerformanceLevel("SLOWER", 620, 1000000000L);
+
+ /**
+ * The level for logging only slowest events. By default, only events having an execution
+ * time equals or greater than 5 seconds are logged at this level. However this threshold
+ * can be changed by a call to <code>SLOWEST.{@linkplain #setMinDuration(long, TimeUnit)}</code>.
+ */
+ public static final PerformanceLevel SLOWEST = new PerformanceLevel("SLOWEST", 630, 5000000000L);
+
+ /**
+ * The minimal duration (in nanoseconds) for logging the record.
+ */
+ private volatile long minDuration;
+
+ /**
+ * Constructs a new logging level for monitoring performance.
+ *
+ * @param name The logging level name.
+ * @param value The level value.
+ * @param duration The minimal duration (in nanoseconds) for logging a record.
+ */
+ private PerformanceLevel(final String name, final int value, final long duration) {
+ super(name, value);
+ minDuration = duration;
+ }
+
+ /**
+ * Returns the level to use for logging an event of the given duration.
+ *
+ * @param duration The event duration.
+ * @param unit The unit of the given duration value.
+ * @return The level to use for logging an event of the given duration.
+ */
+ public static PerformanceLevel forDuration(long duration, final TimeUnit unit) {
+ duration = unit.toNanos(duration);
+ if (duration >= SLOWER.minDuration) {
+ return (duration >= SLOWEST.minDuration) ? SLOWEST : SLOWER;
+ } else {
+ return (duration >= SLOW.minDuration) ? SLOW : PERFORMANCE;
+ }
+ }
+
+ /**
+ * Returns the minimal duration for logging an event at this level.
+ *
+ * @param unit The unit in which to express the minimal duration.
+ * @return The minimal duration in the given unit.
+ */
+ public long getMinDuration(final TimeUnit unit) {
+ return unit.convert(minDuration, TimeUnit.NANOSECONDS);
+ }
+
+ /**
+ * Sets the minimal duration for logging an event at this level. Invoking this method
+ * may have an indirect impact of other performance levels:
+ * <p>
+ * <ul>
+ * <li>If the given duration is longer than the duration of slower levels, then the later
+ * are also set to the given duration.</li>
+ * <li>If the given duration is shorter than the duration of faster levels, then the later
+ * are also set to the given duration.</li>
+ * </ul>
+ *
+ * {@note The duration of the <code>PERFORMANCE</code> level can not be modified: it is
+ * always zero. However invoking this method on the <code>PERFORMANCE</code> field will
+ * ensure that every <code>SLOW*</code> levels will have at least the given duration.}
+ *
+ * @param duration The minimal duration.
+ * @param unit The unit of the given duration value.
+ * @throws IllegalArgumentException If the given duration is negative.
+ */
+ @Configuration
+ @SuppressWarnings("fallthrough")
+ public void setMinDuration(long duration, final TimeUnit unit) throws IllegalArgumentException {
+ ensurePositive("duration", duration);
+ duration = unit.toNanos(duration);
+ final int value = intValue();
+ synchronized (PerformanceLevel.class) {
+ // Check the value of slower levels.
+ switch (value) {
+ default: throw new AssertionError(this);
+ case 600: if (duration > SLOW .minDuration) SLOW .minDuration = duration;
+ case 610: if (duration > SLOWER .minDuration) SLOWER .minDuration = duration;
+ case 620: if (duration > SLOWEST.minDuration) SLOWEST.minDuration = duration;
+ case 630: // Do nothing since there is no level slower than 'SLOWEST'.
+ }
+ // Check the value of faster levels.
+ switch (value) {
+ default: throw new AssertionError(this);
+ case 630: if (duration < SLOWER .minDuration) SLOWER .minDuration = duration;
+ case 620: if (duration < SLOW .minDuration) SLOW .minDuration = duration;
+ case 610: minDuration = duration;
+ case 600: // Do nothing, since we don't allow modification of PERFORMANCE level.
+ }
+ }
+ }
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java
------------------------------------------------------------------------------
svn:mime-type = text/plain