This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 69fdbb413c721ea88e843bc3c8695d5845d234ef Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Jan 12 01:08:00 2022 +0100 Remove the `QuietLogRecord` package-private class, replaced by the use of a `java.util.logging.Filter`. It allows us to get the same functionality from other packages (in this commit, netCDF). --- .../apache/sis/util/resources/KeyConstants.java | 2 +- .../apache/sis/internal/netcdf/NamedElement.java | 6 +- .../sis/internal/storage/AbstractResource.java | 19 ++++++ .../apache/sis/storage/event/QuietLogRecord.java | 70 ---------------------- .../apache/sis/storage/event/StoreListeners.java | 59 +++++++++++------- .../org/apache/sis/storage/event/package-info.java | 3 +- 6 files changed, 61 insertions(+), 98 deletions(-) diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java b/core/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java index 8c7d199..4f46302 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/KeyConstants.java @@ -102,7 +102,7 @@ public class KeyConstants { /** * Returns the name of the key at the given index. If there is no name at that given - * index, format the index as a decimal number. Those decimal numbers are parsed by + * index, formats the index as a decimal number. Those decimal numbers are parsed by * our {@link IndexedResourceBundle#handleGetObject(String)} implementation. */ final String getKeyName(final short index) { diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java index 2a0c9cb..a6f1e52 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/NamedElement.java @@ -23,6 +23,7 @@ import org.apache.sis.util.Characters; import org.apache.sis.util.CharSequences; import org.apache.sis.internal.system.Modules; import org.apache.sis.internal.util.Strings; +import org.apache.sis.internal.storage.AbstractResource; import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.util.resources.IndexedResourceBundle; @@ -32,7 +33,7 @@ import org.apache.sis.util.resources.IndexedResourceBundle; * All those objects share in common a {@link #getName()} method. * * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.2 * @since 0.8 * @module */ @@ -113,10 +114,9 @@ public abstract class NamedElement { record.setSourceClassName(caller.getCanonicalName()); record.setSourceMethodName(method); if (exception != null) { - // TODO: avoid reporting the full exception stack trace (maybe leverage QuietLogRecord). record.setThrown(exception); } - listeners.warning(record); + listeners.warning(record, AbstractResource.removeStackTraceInLogs()); } /** diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java index d38b603..8888341 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java @@ -18,6 +18,8 @@ package org.apache.sis.internal.storage; import java.util.Locale; import java.util.Optional; +import java.util.logging.Filter; +import java.util.logging.LogRecord; import org.opengis.util.GenericName; import org.opengis.util.InternationalString; import org.opengis.metadata.Metadata; @@ -215,4 +217,21 @@ public class AbstractResource extends StoreListeners implements Resource { super.addListener(eventType, listener); } } + + /** + * Returns a log filter that removes the stack trace of filtered given log. + * It can be used as argument in a call to {@link StoreListeners#warning(LogRecord, Filter)} + * if the caller was to trim the stack trace in log files or console outputs. + * + * <p>This filter should be used only for filtering {@link LogRecord} created by the caller, because + * it modifies the record. Users would not expect this side effect on records created by them.</p> + * + * @return a filter for trimming stack trace. + */ + public static Filter removeStackTraceInLogs() { + return (record) -> { + record.setThrown(null); + return true; + }; + } } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/QuietLogRecord.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/QuietLogRecord.java deleted file mode 100644 index 06a3496..0000000 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/QuietLogRecord.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.storage.event; - -import java.util.logging.Level; -import java.util.logging.LogRecord; - - -/** - * A log record to be logged without stack trace, unless the user specified it explicitly. - * - * @author Martin Desruisseaux (Geomatys) - * @version 1.0 - * @since 0.3 - * @module - */ -final class QuietLogRecord extends LogRecord { - /** - * For cross-version compatibility. - */ - private static final long serialVersionUID = 5652099235767670922L; - - /** - * {@code true} if the user invoked {@link #setThrown(Throwable)} explicitly. - * In such case, {@link #clearImplicitThrown()} will not reset the throwable to null. - */ - private boolean explicitThrown; - - /** - * Creates a new log record for the given message and exception. - */ - QuietLogRecord(final Level level, final String message, final Exception exception) { - super(level, message); - super.setThrown(exception); - } - - /** - * Sets the throwable to the given value. The given throwable will not be cleared - * when the record will be logged. - */ - @Override - public void setThrown(final Throwable thrown) { - explicitThrown = true; - super.setThrown(thrown); - } - - /** - * Clears the throwable if it has not been explicitly set by the user. - * Otherwise do nothing. - */ - void clearImplicitThrown() { - if (!explicitThrown) { - super.setThrown(null); - } - } -} diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java index 8943dc6..e790510 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java @@ -22,6 +22,7 @@ import java.util.IdentityHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.LogRecord; +import java.util.logging.Filter; import java.lang.reflect.Method; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.Localized; @@ -49,8 +50,8 @@ import org.apache.sis.storage.Resource; * and additional information like {@linkplain LogRecord#getThrown() stack trace}, timestamp, <i>etc.</i>). * This {@code StoreListeners} class provides convenience methods like {@link #warning(String, Exception)}, * which build {@code LogRecord} from an exception or from a string. But all those {@code warning(…)} methods - * ultimately delegate to {@link #warning(LogRecord)}, thus providing a single point that subclasses can override. - * When a warning is emitted, the default behavior is: + * ultimately delegate to {@link #warning(LogRecord, Filter)}, thus providing a single point that subclasses + * can override. When a warning is emitted, the default behavior is: * * <ul> * <li>Notify all listeners that are registered for a given {@link WarningEvent} type in this {@code StoreListeners} @@ -73,7 +74,7 @@ import org.apache.sis.storage.Resource; * from multiple threads. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 1.0 * @module */ @@ -397,16 +398,11 @@ public class StoreListeners implements Localized { * If both are non-null, then the exception message will be concatenated after the given message. * * <h4>Stack trace omission</h4> - * If there is no registered listener for the {@link WarningEvent} type, then the {@link #warning(LogRecord)} - * method will send the record to a logger but <em>without</em> the stack trace. + * If there is no registered listener for the {@link WarningEvent} type, + * then the {@link LogRecord} will be sent to a {@link Logger} but <em>without</em> the stack trace. * This is done that way because stack traces consume lot of space in the logging files, while being considered * implementation details in the context of {@code StoreListeners} (on the assumption that the logging message - * provides sufficient information). If the stack trace is desired, then users can either: - * <ul> - * <li>invoke {@code warning(LogRecord)} directly, or</li> - * <li>override {@code warning(LogRecord)} and invoke {@link LogRecord#setThrown(Throwable)} explicitly, or</li> - * <li>register a listener which will log the record itself.</li> - * </ul> + * provides sufficient information). * * @param level the warning level. * @param message the message to log, or {@code null} if none. @@ -422,7 +418,8 @@ public class StoreListeners implements Localized { if (message == null) { message = exception.toString(); } - record = new QuietLogRecord(level, message, exception); + record = new LogRecord(level, message); + record.setThrown(exception); } else { ArgumentChecks.ensureNonEmpty("message", message); trace = Thread.currentThread().getStackTrace(); // TODO: on JDK9, use StackWalker instead. @@ -437,7 +434,7 @@ public class StoreListeners implements Localized { } catch (ClassNotFoundException | SecurityException e) { Logging.ignorableException(StoreUtilities.LOGGER, StoreListeners.class, "warning", e); } - warning(record); + warning(record, AbstractResource.removeStackTraceInLogs()); } /** @@ -464,28 +461,47 @@ public class StoreListeners implements Localized { } /** + * Reports a warning described by the given log record. Invoking this method is + * equivalent to invoking {@link #warning(LogRecord, Filter)} with a null filter. + * + * @param description warning details provided as a log record. + */ + public void warning(final LogRecord description) { + warning(description, null); + } + + /** * Reports a warning described by the given log record. The default implementation forwards * the given record to one of the following destinations, in preference order: * - * <ul> + * <ol> * <li><code>{@linkplain StoreListener#eventOccured StoreListener.eventOccured}(new * {@linkplain WarningEvent}(source, record))</code> on all listeners registered for this kind of event.</li> - * <li>Only if above step found no listener, then <code>{@linkplain Logging#getLogger(String) - * Logging.getLogger}(record.loggerName).{@linkplain Logger#log(LogRecord) log}(record)</code> - * where {@code loggerName} is one of the following: + * <li><code>{@linkplain Filter#isLoggable(LogRecord) onUnhandled.isLoggable(description)}</code> + * if above step found no listener and the {@code onUnhandled} filter is non-null.</li> + * <li><code>{@linkplain Logger#getLogger(String) + * Logger.getLogger}(record.loggerName).{@linkplain Logger#log(LogRecord) log}(record)</code> + * if the filter in above step returned {@code true} (or if the filter is null). + * In that case, {@code loggerName} is one of the following: * <ul> * <li><code>record.{@linkplain LogRecord#getLoggerName() getLoggerName()}</code> if that value is non-null.</li> * <li>Otherwise the value of {@link DataStoreProvider#getLogger()} if the provider is found.</li> * <li>Otherwise the source {@link DataStore} package name.</li> * </ul> * </li> - * </ul> + * </ol> * * @param description warning details provided as a log record. + * @param onUnhandled filter invoked if the record has not been handled by a {@link StoreListener}, + * or {@code null} if none. + * + * @since 1.2 */ @SuppressWarnings("unchecked") - public void warning(final LogRecord description) { - if (!fire(new WarningEvent(source, description), WarningEvent.class)) { + public void warning(final LogRecord description, final Filter onUnhandled) { + if (!fire(new WarningEvent(source, description), WarningEvent.class) && + (onUnhandled == null || onUnhandled.isLoggable(description))) + { final String name = description.getLoggerName(); final Logger logger; if (name != null) { @@ -494,9 +510,6 @@ public class StoreListeners implements Localized { logger = getLogger(); description.setLoggerName(logger.getName()); } - if (description instanceof QuietLogRecord) { - ((QuietLogRecord) description).clearImplicitThrown(); - } logger.log(description); } } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java index c9a46b7..f8a7aaa 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java @@ -34,7 +34,8 @@ * to a specific kind of event}. * * @author Johann Sorel (Geomatys) - * @since 1.1 + * @author Martin Desruisseaux (Geomatys) + * @since 1.2 * @version 1.0 * @module */