From 807969d25cfdc1aa6db350112519817478e39878 Mon Sep 17 00:00:00 2001 From: James Perkins Date: Thu, 17 Aug 2017 15:22:28 -0700 Subject: [PATCH] [LOGMGR-150] Add a way to capture stdout and stderr early and write error messages to stderr. --- .../DefaultConfigurationLocator.java | 2 +- .../java/org/jboss/logmanager/LogManager.java | 7 +- .../logmanager/PropertyConfigurator.java | 4 +- .../logmanager/StandardOutputStreams.java | 81 +++++++++++++++++++ .../config/AbstractPropertyConfiguration.java | 7 +- .../errormanager/OnlyOnceErrorManager.java | 4 +- .../logmanager/handlers/ConsoleHandler.java | 11 ++- 7 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/jboss/logmanager/StandardOutputStreams.java diff --git a/src/main/java/org/jboss/logmanager/DefaultConfigurationLocator.java b/src/main/java/org/jboss/logmanager/DefaultConfigurationLocator.java index d8feebec..b5ce9e23 100644 --- a/src/main/java/org/jboss/logmanager/DefaultConfigurationLocator.java +++ b/src/main/java/org/jboss/logmanager/DefaultConfigurationLocator.java @@ -35,7 +35,7 @@ public InputStream findConfiguration() throws IOException { if (propLoc != null) try { return new URL(propLoc).openStream(); } catch (IOException e) { - System.err.printf("Unable to read the logging configuration from '%s' (%s)%n", propLoc, e); + StandardOutputStreams.printError("Unable to read the logging configuration from '%s' (%s)%n", propLoc, e); } final ClassLoader tccl = Thread.currentThread().getContextClassLoader(); if (tccl != null) try { diff --git a/src/main/java/org/jboss/logmanager/LogManager.java b/src/main/java/org/jboss/logmanager/LogManager.java index d75e99bf..fbe8dcc6 100644 --- a/src/main/java/org/jboss/logmanager/LogManager.java +++ b/src/main/java/org/jboss/logmanager/LogManager.java @@ -50,6 +50,11 @@ public final class LogManager extends java.util.logging.LogManager { static final boolean PER_THREAD_LOG_FILTER; static { + try { + // Ensure the StandardOutputStreams are initialized early to capture the current System.out and System.err. + Class.forName(StandardOutputStreams.class.getName()); + } catch (ClassNotFoundException ignore) { + } if (System.getSecurityManager() == null) { PER_THREAD_LOG_FILTER = Boolean.getBoolean(PER_THREAD_LOG_FILTER_KEY); } else { @@ -514,7 +519,7 @@ public void readConfiguration(InputStream inputStream) throws IOException, Secur configurator.configure(inputStream); LogContext.getSystemLogContext().getLogger("").attach(Configurator.ATTACHMENT_KEY, configurator); } catch (Throwable t) { - t.printStackTrace(); + StandardOutputStreams.printError(t, "Failed to read or configure the org.jboss.logmanager.LogManager"); } } finally { try { diff --git a/src/main/java/org/jboss/logmanager/PropertyConfigurator.java b/src/main/java/org/jboss/logmanager/PropertyConfigurator.java index cce0023f..9042ff9f 100644 --- a/src/main/java/org/jboss/logmanager/PropertyConfigurator.java +++ b/src/main/java/org/jboss/logmanager/PropertyConfigurator.java @@ -837,7 +837,7 @@ private static void writeSanitized(final Appendable out, final String string, fi * @param msg the message to print */ static void printError(final String msg) { - System.err.println(msg); + StandardOutputStreams.printError(msg); } /** @@ -847,7 +847,7 @@ static void printError(final String msg) { * @param args the format arguments */ static void printError(final String format, final Object... args) { - System.err.printf(format, args); + StandardOutputStreams.printError(format, args); } diff --git a/src/main/java/org/jboss/logmanager/StandardOutputStreams.java b/src/main/java/org/jboss/logmanager/StandardOutputStreams.java new file mode 100644 index 00000000..722a622f --- /dev/null +++ b/src/main/java/org/jboss/logmanager/StandardOutputStreams.java @@ -0,0 +1,81 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2017 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed 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.jboss.logmanager; + +import java.io.PrintStream; + +/** + * Caches {@link System#out stdout} and {@link System#err stderr} early. + * + * @author James R. Perkins + */ +@SuppressWarnings("WeakerAccess") +public class StandardOutputStreams { + public static final PrintStream stdout = System.out; + public static final PrintStream stderr = System.err; + + /** + * Prints an error messages to {@link #stderr stderr}. + * + * @param msg the message to print + */ + public static void printError(final String msg) { + stderr.println(msg); + } + + /** + * Prints an error messages to {@link #stderr stderr}. + * + * @param format the {@link java.util.Formatter format} + * @param args the arguments for the format + */ + public static void printError(final String format, final Object... args) { + stderr.printf(format, args); + } + + /** + * Prints an error messages to {@link #stderr stderr}. + * + * @param cause the cause of the error, if not {@code null} the {@link Throwable#printStackTrace(PrintStream)} + * writes to {@link #stderr stderr} + * @param msg the message to print + */ + public static void printError(final Throwable cause, final String msg) { + stderr.println(msg); + if (cause != null) { + cause.printStackTrace(stderr); + } + } + + /** + * Prints an error messages to {@link #stderr stderr}. + * + * @param cause the cause of the error, if not {@code null} the {@link Throwable#printStackTrace(PrintStream)} + * writes to {@link #stderr stderr} + * @param format the {@link java.util.Formatter format} + * @param args the arguments for the format + */ + public static void printError(final Throwable cause, final String format, final Object... args) { + stderr.printf(format, args); + if (cause != null) { + cause.printStackTrace(stderr); + } + } +} diff --git a/src/main/java/org/jboss/logmanager/config/AbstractPropertyConfiguration.java b/src/main/java/org/jboss/logmanager/config/AbstractPropertyConfiguration.java index 13cd6651..0a14c3bb 100644 --- a/src/main/java/org/jboss/logmanager/config/AbstractPropertyConfiguration.java +++ b/src/main/java/org/jboss/logmanager/config/AbstractPropertyConfiguration.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.regex.Pattern; +import org.jboss.logmanager.StandardOutputStreams; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoader; @@ -215,8 +216,7 @@ public void applyPostCreate(final ObjectProducer param) { try { setter.invoke(instance, param.getObject()); } catch (Throwable e) { - // todo log it properly... - e.printStackTrace(); + StandardOutputStreams.printError(e, "Failed to invoke setter %s with value %s%n.", setter.getName(), param.getObject()); } } } @@ -403,8 +403,7 @@ public void applyPostCreate(final Method param) { try { param.invoke(instance); } catch (Throwable e) { - // todo log it properly... - e.printStackTrace(); + StandardOutputStreams.printError(e, "Failed to invoke method %s%n.", param.getName()); } } diff --git a/src/main/java/org/jboss/logmanager/errormanager/OnlyOnceErrorManager.java b/src/main/java/org/jboss/logmanager/errormanager/OnlyOnceErrorManager.java index 00cb1871..c160d9b0 100644 --- a/src/main/java/org/jboss/logmanager/errormanager/OnlyOnceErrorManager.java +++ b/src/main/java/org/jboss/logmanager/errormanager/OnlyOnceErrorManager.java @@ -24,12 +24,14 @@ import java.util.logging.ErrorManager; +import org.jboss.logmanager.StandardOutputStreams; + /** * An error manager which runs only once and writes a complete formatted error to {@code System.err}. Caches * an early {@code System.err} in case it is replaced. */ public final class OnlyOnceErrorManager extends ErrorManager { - private static final PrintStream ps = System.err; + private static final PrintStream ps = StandardOutputStreams.stderr; private final AtomicBoolean called = new AtomicBoolean(); diff --git a/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java b/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java index 05bf8aba..1e365bdc 100644 --- a/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java +++ b/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java @@ -26,14 +26,13 @@ import java.util.logging.Formatter; +import org.jboss.logmanager.StandardOutputStreams; import org.jboss.logmanager.formatters.Formatters; /** * A console handler which writes to {@code System.out} by default. */ public class ConsoleHandler extends OutputStreamHandler { - private static final OutputStream out = System.out; - private static final OutputStream err = System.err; /** * The target stream type. @@ -95,8 +94,8 @@ public ConsoleHandler(final Target target) { public ConsoleHandler(final Target target, final Formatter formatter) { super(formatter); switch (target) { - case SYSTEM_OUT: setOutputStream(wrap(out)); break; - case SYSTEM_ERR: setOutputStream(wrap(err)); break; + case SYSTEM_OUT: setOutputStream(wrap(StandardOutputStreams.stdout)); break; + case SYSTEM_ERR: setOutputStream(wrap(StandardOutputStreams.stderr)); break; case CONSOLE: setWriter(wrap(console)); break; default: throw new IllegalArgumentException(); } @@ -109,8 +108,8 @@ public ConsoleHandler(final Target target, final Formatter formatter) { */ public void setTarget(Target target) { switch (target) { - case SYSTEM_OUT: setOutputStream(wrap(out)); break; - case SYSTEM_ERR: setOutputStream(wrap(err)); break; + case SYSTEM_OUT: setOutputStream(wrap(StandardOutputStreams.stdout)); break; + case SYSTEM_ERR: setOutputStream(wrap(StandardOutputStreams.stderr)); break; case CONSOLE: setWriter(wrap(console)); break; default: throw new IllegalArgumentException(); }