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 2013/06/14 00:19:37 UTC
svn commit: r1492886 - in /sis/branches/JDK7/core/sis-utility/src:
main/java/org/apache/sis/util/logging/MonolineFormatter.java
test/java/org/apache/sis/test/suite/UtilityTestSuite.java
test/java/org/apache/sis/util/logging/MonolineFormatterTest.java
Author: desruisseaux
Date: Thu Jun 13 22:19:36 2013
New Revision: 1492886
URL: http://svn.apache.org/r1492886
Log:
Simplified install() and added test case.
Added:
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java (with props)
Modified:
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java?rev=1492886&r1=1492885&r2=1492886&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java [UTF-8] Thu Jun 13 22:19:36 2013
@@ -45,12 +45,12 @@ import org.apache.sis.util.Debug;
* {@code MonolineFormatter} may look like:
*
* <blockquote><table style="color:#FFFFFF; background:black" class="compact">
- * <tr><td style="background:blue"><code>CONFIG</code></td>
- * <td><code><b>[MyApplication]</b> Read configuration from “my-application/setup.xml”.</code></td>
- * <tr><td style="background:green"><code>INFO</code></td>
- * <td><code><b>[DirectEpsgFactory]</b> Connected to the EPSG database version 6.9 on JavaDB 10.8.</code></td>
- * <tr><td style="background:goldenrod"><code>WARNING</code></td>
- * <td><code><b>[DefaultTemporalExtent]</b> This operation requires the “sis-temporal” module.</code></td>
+ * <tr><td><code>00:01</code></td><td style="background:blue"><code>CONFIG</code></td>
+ * <td><code><b>[MyApplication]</b> Read configuration from “my-application/setup.xml”.</code></td></tr>
+ * <tr><td><code>00:03</code></td><td style="background:green"><code>INFO</code></td>
+ * <td><code><b>[DirectEpsgFactory]</b> Connected to the EPSG database version 6.9 on JavaDB 10.8.</code></td></tr>
+ * <tr><td><code>00:12</code></td><td style="background:goldenrod"><code>WARNING</code></td>
+ * <td><code><b>[DefaultTemporalExtent]</b> This operation requires the “sis-temporal” module.</code></td></tr>
* </table></blockquote>
*
* By default, {@code MonolineFormatter} shows only the level and the message. One or two additional
@@ -200,8 +200,10 @@ public class MonolineFormatter extends F
* The minimum amount of characters to use for writing logging level before the message.
* If the logging level is shorter, remaining characters will be padded with spaces.
* This is used in order to align the messages.
+ *
+ * @see #levelWidth(Level)
*/
- private int levelWidth;
+ private final int levelWidth;
/**
* Time of {@code MonolineFormatter} creation, in milliseconds elapsed since January 1, 1970.
@@ -253,29 +255,11 @@ public class MonolineFormatter extends F
public MonolineFormatter(final Handler handler) {
this.startMillis = System.currentTimeMillis();
/*
- * Sets the "levelWidth" field to the largest label that may be displayed,
- * according current handler setting. In the case where a larger label is
- * to be printed, this class will adjust itself but the visual alignment
- * with previous or next record may be broken.
+ * The length of the widest standard level name that may be displayed according current handler setting.
+ * If a larger label is to be printed, this class will adjust itself but the visual alignment with
+ * previous or next record may be broken.
*/
- final Level level = (handler != null) ? handler.getLevel() : null;
-loop: for (int i=0; ; i++) {
- final Level c;
- switch (i) {
- case 0: c = Level.FINEST; break;
- case 1: c = Level.FINER; break;
- case 2: c = Level.FINE; break;
- case 3: c = Level.CONFIG; break;
- case 4: c = Level.INFO; break;
- case 5: c = Level.WARNING; break;
- case 6: c = Level.SEVERE; break;
- default: break loop;
- }
- if (level == null || c.intValue() >= level.intValue()) {
- final int length = c.getLocalizedName().length();
- if (length > levelWidth) levelWidth = length;
- }
- }
+ levelWidth = levelWidth((handler != null) ? handler.getLevel() : null);
/*
* Configures this formatter according the properties, if any.
*/
@@ -319,6 +303,33 @@ loop: for (int i=0; ; i++) {
}
/**
+ * Returns the length of the widest level name, taking in account only the standard levels
+ * equals or greater then the given threshold.
+ */
+ static int levelWidth(final Level threshold) {
+ int levelWidth = 0;
+loop: for (int i=0; ; i++) {
+ final Level c;
+ switch (i) {
+ case 0: c = Level.SEVERE; break;
+ case 1: c = Level.WARNING; break;
+ case 2: c = Level.INFO; break;
+ case 3: c = Level.CONFIG; break;
+ case 4: c = Level.FINE; break;
+ case 5: c = Level.FINER; break;
+ case 6: c = Level.FINEST; break;
+ default: break loop;
+ }
+ if (threshold != null && c.intValue() < threshold.intValue()) {
+ break loop;
+ }
+ final int length = c.getLocalizedName().length();
+ if (length > levelWidth) levelWidth = length;
+ }
+ return levelWidth;
+ }
+
+ /**
* Returns the string to write on the left side of the first line of every log records, or {@code null} if none.
* This is a string to be shown just before the level.
*
@@ -777,10 +788,23 @@ loop: for (int i=0; ; i++) {
}
/**
- * Installs a {@code MonolineFormatter} for the root logger and its children.
- * If a {@code MonolineFormatter} is already installed, then it will be returned unchanged.
- * If a {@link SimpleFormatter} was installed, then it will be removed in order to avoid sending
- * the same records twice to the console.
+ * Installs a {@code MonolineFormatter} for the root logger, or returns the existing instance if any.
+ * This method performs the following choices:
+ *
+ * <ul>
+ * <li>If a {@link ConsoleHandler} is associated to the root logger, then:
+ * <ul>
+ * <li>If that handler already uses a {@code MonolineFormatter}, then the existing formatter is returned.</li>
+ * <li>Otherwise the {@code ConsoleHandler} formatter is replaced by a new {@code MonolineFormatter} instance,
+ * and that new instance is returned. We perform this replacement in order to avoid sending twice the same
+ * records to the console.</li>
+ * </ul></li>
+ * <li>Otherwise a new {@code ConsoleHandler} using a new {@code MonolineFormatter} is created and added to the
+ * root logger.</li>
+ * </ul>
+ *
+ * {@note The current implementation does not check for duplicated <code>ConsoleHandler</code> instances,
+ * and does not check if any child logger has a <code>ConsoleHandler</code>.}
*
* @return The new or existing {@code MonolineFormatter}. The formatter output can be configured
* using the {@link #setTimeFormat(String)} and {@link #setSourceFormat(String)} methods.
@@ -792,19 +816,36 @@ loop: for (int i=0; ; i++) {
}
/**
- * Installs a {@code MonolineFormatter} for the specified logger and its children.
- * If a {@code MonolineFormatter} is already installed, then it will be returned unchanged.
- * If a {@link SimpleFormatter} was installed, then it will be removed in order to avoid sending
- * the same records twice to the console.
+ * Installs a {@code MonolineFormatter} for the specified logger, or returns the existing instance if any.
+ * This method performs the following steps:
*
- * {@section Specifying a log level}
- * This method can opportunistically set the handler levels. If the given level is non-null,
- * then every {@link Handler}s using the {@code MonolineFormatter} may be set to that level.
- * The given level is only a hint - this method may ignores it if the current configuration
- * already uses a finer level.
+ * <ul>
+ * <li>If a {@link ConsoleHandler} is associated to the given logger, then:
+ * <ul>
+ * <li>If that handler already uses a {@code MonolineFormatter}, then the existing formatter is returned.</li>
+ * <li>Otherwise the {@code ConsoleHandler} formatter is replaced by a new {@code MonolineFormatter} instance,
+ * and that new instance is returned. We perform this replacement in order to avoid sending twice the same
+ * records to the console.</li>
+ * </ul></li>
+ * <li>Otherwise:
+ * <ul>
+ * <li>The {@link Logger#setUseParentHandlers(boolean)} flag is set to {@code false} for avoiding duplicated
+ * loggings if a {@code ConsoleHandler} instance exists in the parent handlers.</li>
+ * <li>Parent handlers that are not {@code ConsoleHandler} instances are added to the given logger in
+ * order to preserve similar behavior as before the call to {@code setUseParentHandlers(false)}.</li>
+ * <li>A new {@code ConsoleHandler} using a new {@code MonolineFormatter} is created and added to the
+ * given logger.</li>
+ * </ul></li>
+ * </ul>
*
- * <p>This method is provided mostly for debugging purpose, as a quick way to increase the
- * logging verbosity.</p>
+ * {@note The current implementation does not check for duplicated <code>ConsoleHandler</code> instances,
+ * and does not check if any child logger has a <code>ConsoleHandler</code>.}
+ *
+ * {@section Specifying a log level}
+ * This method can opportunistically set the handler level. If the given level is non-null,
+ * then the {@link ConsoleHandler} using the {@code MonolineFormatter} will be set to that level.
+ * This is mostly a convenience for temporary increase of logging verbosity for debugging purpose.
+ * This functionality should not be used in production environment, since it overwrite user's level setting.
*
* @param logger The base logger to apply the change on.
* @param level The desired level, or {@code null} if no level should be set.
@@ -816,95 +857,55 @@ loop: for (int i=0; ; i++) {
@Configuration
public static MonolineFormatter install(final Logger logger, final Level level) throws SecurityException {
MonolineFormatter monoline = null;
- boolean foundConsoleHandler = false;
- Handler[] handlers = logger.getHandlers();
- for (int i=0; i<handlers.length; i++) {
- final Handler handler = handlers[i];
- if (handler.getClass() == ConsoleHandler.class) {
- foundConsoleHandler = true;
+ for (final Handler handler : logger.getHandlers()) {
+ if (handler instanceof ConsoleHandler) {
+ /*
+ * Get or replace the formatter of the first ConsoleHandler found, then stop the search.
+ * We do not search for duplicated ConsoleHandler instances. If such duplicated values exist,
+ * we presume that the user know what he is doing and will avoid messing more with his configuration.
+ */
final Formatter formatter = handler.getFormatter();
if (formatter instanceof MonolineFormatter) {
- /*
- * A MonolineFormatter already existed. Sets the level only for the first
- * instance (only one instance should exists anyway) for consistency with
- * the fact that this method returns only one MonolineFormatter for further
- * configuration.
- */
- if (monoline == null) {
- monoline = (MonolineFormatter) formatter;
- setLevel(handler, level);
- }
- } else if (formatter.getClass() == SimpleFormatter.class) {
- /*
- * A ConsoleHandler using the SimpleFormatter has been found. Replaces
- * the SimpleFormatter by MonolineFormatter, creating it if necessary.
- */
- setLevel(handler, level);
- if (monoline == null) {
- monoline = new MonolineFormatter(handler);
- }
+ monoline = (MonolineFormatter) formatter;
+ } else {
+ monoline = new MonolineFormatter(handler);
handler.setFormatter(monoline);
}
+ if (level != null) {
+ handler.setLevel(level);
+ }
+ break;
}
}
/*
- * If the logger uses parent handlers, copy them to the logger that we are initializing,
- * because we will not use parent handlers anymore at the end of this method.
+ * If we didn't found any ConsoleHandler, then we will need to create a new one. This usually happen if
+ * the logger given in argument to this method was not the root logger. For example the user may want to
+ * configure only the "org.apache.sis" logger. But before to create the new ConsoleHandler, we will need
+ * to stop using the parent handlers because we don't want to inherit the original ConsoleHandler which
+ * is likely to exist in the root package. In order to preserve functionalities of other loggers, we copy
+ * a snapshot of all other handlers.
*/
- for (Logger parent=logger; parent.getUseParentHandlers();) {
- parent = parent.getParent();
- if (parent == null) {
- break;
- }
- handlers = parent.getHandlers();
- for (int i=0; i<handlers.length; i++) {
- Handler handler = handlers[i];
- if (handler.getClass() == ConsoleHandler.class) {
- if (!foundConsoleHandler) {
- // We have already set a ConsoleHandler and we don't want a second one.
- continue;
- }
- foundConsoleHandler = true;
- final Formatter formatter = handler.getFormatter();
- if (formatter.getClass() == SimpleFormatter.class) {
- monoline = addHandler(logger, level);
- continue;
+ if (monoline == null) {
+ logger.setUseParentHandlers(false);
+ for (Logger parent=logger; parent.getUseParentHandlers();) {
+ parent = parent.getParent();
+ if (parent == null) {
+ break;
+ }
+ for (final Handler handler : parent.getHandlers()) {
+ if (!(handler instanceof ConsoleHandler)) {
+ logger.addHandler(handler);
}
}
- logger.addHandler(handler);
}
+ final Handler handler = new ConsoleHandler();
+ if (level != null) {
+ handler.setLevel(level); // Shall be before MonolineFormatter creation.
+ }
+ monoline = new MonolineFormatter(handler);
+ handler.setFormatter(monoline);
+ logger.addHandler(handler);
}
- logger.setUseParentHandlers(false);
- if (!foundConsoleHandler) {
- monoline = addHandler(logger, level);
- }
- return monoline;
- }
-
- /**
- * Adds to the specified logger a {@link Handler} using a {@code MonolineFormatter}
- * set at the specified level. The formatter is returned for convenience.
- */
- private static MonolineFormatter addHandler(final Logger logger, final Level level) {
- final Handler handler = new ConsoleHandler();
- final MonolineFormatter monoline = new MonolineFormatter(handler);
- handler.setFormatter(monoline);
- setLevel(handler, level);
- logger.addHandler(handler);
return monoline;
}
-
- /**
- * Sets the level of the given handler. This method tries to find a balance between user's
- * setting and desired level using heuristic rules that may change in any future version.
- */
- private static void setLevel(final Handler handler, final Level level) {
- if (level != null) {
- final int desired = level.intValue();
- final int current = handler.getLevel().intValue();
- if (desired < LEVEL_THRESHOLD.intValue() ? desired < current : desired > current) {
- handler.setLevel(level);
- }
- }
- }
}
Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1492886&r1=1492885&r2=1492886&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Thu Jun 13 22:19:36 2013
@@ -50,6 +50,7 @@ import org.junit.BeforeClass;
org.apache.sis.util.resources.IndexedResourceBundleTest.class,
org.apache.sis.util.logging.PerformanceLevelTest.class,
org.apache.sis.util.logging.WarningListenersTest.class,
+ org.apache.sis.util.logging.MonolineFormatterTest.class,
org.apache.sis.math.MathFunctionsTest.class,
org.apache.sis.math.StatisticsTest.class,
org.apache.sis.math.StatisticsFormatTest.class,
Added: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java?rev=1492886&view=auto
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java (added)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java [UTF-8] Thu Jun 13 22:19:36 2013
@@ -0,0 +1,109 @@
+/*
+ * 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.LogRecord;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests the {@link MonolineFormatter} class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-3.16)
+ * @version 0.3
+ * @module
+ */
+public final strictfp class MonolineFormatterTest extends TestCase {
+ /**
+ * The formatter to be tested.
+ */
+ private final MonolineFormatter formatter = new MonolineFormatter(null);
+
+ /**
+ * Tests {@link MonolineFormatter#levelWidth(Level)}.
+ */
+ @Test
+ public void testlevelWidth() {
+ final String severe = Level.SEVERE.getLocalizedName();
+ assertEquals(severe, severe.length(), MonolineFormatter.levelWidth(Level.SEVERE));
+
+ final String warning = Level.WARNING.getLocalizedName();
+ assertEquals(warning, StrictMath.max(severe.length(), warning.length()),
+ MonolineFormatter.levelWidth(Level.WARNING));
+ }
+
+ /**
+ * Formats the given expected string to a format matching the current locale setting.
+ * The given string shall use tabulation before each line of the message.
+ */
+ private static String localize(final Level level, final String expected) {
+ final String name = level.getName();
+ return expected.replace(name, level.getLocalizedName())
+ .replace("\t", CharSequences.spaces(MonolineFormatter.levelWidth(null) - name.length()));
+ }
+
+ /**
+ * Tests formatting of a multi-line message.
+ */
+ @Test
+ @DependsOnMethod("testlevelWidth")
+ public void testMultilines() {
+ final LogRecord record = new LogRecord(Level.INFO, "First line\n Indented line\nLast line\n");
+ final String formatted = formatter.format(record);
+ assertMultilinesEquals(localize(Level.INFO,
+ "INFO\t First line\n" +
+ " \t Indented line\n" +
+ " \t Last line\n"), formatted);
+ }
+
+ /**
+ * Tests formatting a log record which contains an exception.
+ */
+ @Test
+ @DependsOnMethod("testlevelWidth")
+ public void testException() {
+ final LogRecord record = new LogRecord(Level.WARNING, "An exception occured.");
+ final Exception exception = new Exception();
+ exception.setStackTrace(new StackTraceElement[] {
+ new StackTraceElement("org.apache.sis.NonExistent", "foo", "NonExistent.java", 10),
+ new StackTraceElement("org.junit.WhoKnows", "main", "WhoKnows.java", 20)
+ });
+ record.setThrown(exception);
+ String formatted = formatter.format(record);
+ assertMultilinesEquals(localize(Level.WARNING,
+ "WARNING\t An exception occured.\n" +
+ " \t Caused by: java.lang.Exception\n" +
+ " \t at org.apache.sis.NonExistent.foo(NonExistent.java:10)\n" +
+ " \t at org.junit.WhoKnows.main(WhoKnows.java:20)\n"), formatted);
+ /*
+ * Remove the message and try again.
+ */
+ record.setMessage(null);
+ formatted = formatter.format(record);
+ assertMultilinesEquals(localize(Level.WARNING,
+ "WARNING\t java.lang.Exception\n" +
+ " \t at org.apache.sis.NonExistent.foo(NonExistent.java:10)\n" +
+ " \t at org.junit.WhoKnows.main(WhoKnows.java:20)\n"), formatted);
+ }
+}
Propchange: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8