| /* |
| * 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.logging.log4j.core.config.status; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.PrintStream; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.Collection; |
| import java.util.concurrent.LinkedBlockingQueue; |
| |
| |
| import org.apache.logging.log4j.Level; |
| import org.apache.logging.log4j.core.util.FileUtils; |
| import org.apache.logging.log4j.core.util.NetUtils; |
| import org.apache.logging.log4j.status.StatusConsoleListener; |
| import org.apache.logging.log4j.status.StatusListener; |
| import org.apache.logging.log4j.status.StatusLogger; |
| |
| /** |
| * Configuration for setting up {@link StatusConsoleListener} instances. |
| */ |
| public class StatusConfiguration { |
| |
| @SuppressWarnings("UseOfSystemOutOrSystemErr") |
| private static final PrintStream DEFAULT_STREAM = System.out; |
| private static final Level DEFAULT_STATUS = Level.ERROR; |
| private static final Verbosity DEFAULT_VERBOSITY = Verbosity.QUIET; |
| |
| private final Collection<String> errorMessages = new LinkedBlockingQueue<String>(); |
| private final StatusLogger logger = StatusLogger.getLogger(); |
| |
| private volatile boolean initialized; |
| |
| private PrintStream destination = DEFAULT_STREAM; |
| private Level status = DEFAULT_STATUS; |
| private Verbosity verbosity = DEFAULT_VERBOSITY; |
| private String[] verboseClasses; |
| |
| /** |
| * Specifies how verbose the StatusLogger should be. |
| */ |
| public enum Verbosity { |
| QUIET, VERBOSE; |
| |
| /** |
| * Parses the verbosity property into an enum. |
| * |
| * @param value property value to parse. |
| * @return enum corresponding to value, or QUIET by default. |
| */ |
| public static Verbosity toVerbosity(final String value) { |
| return Boolean.parseBoolean(value) ? VERBOSE : QUIET; |
| } |
| } |
| |
| /** |
| * Logs an error message to the StatusLogger. If the StatusLogger hasn't been set up yet, queues the message to be |
| * logged after initialization. |
| * |
| * @param message error message to log. |
| */ |
| public void error(final String message) { |
| if (!this.initialized) { |
| this.errorMessages.add(message); |
| } else { |
| this.logger.error(message); |
| } |
| } |
| |
| /** |
| * Specifies the destination for StatusLogger events. This can be {@code out} (default) for using |
| * {@link System#out standard out}, {@code err} for using {@link System#err standard error}, or a file URI to |
| * which log events will be written. If the provided URI is invalid, then the default destination of standard |
| * out will be used. |
| * |
| * @param destination where status log messages should be output. |
| * @return {@code this} |
| */ |
| public StatusConfiguration setDestination(final String destination) { |
| try { |
| this.destination = parseStreamName(destination); |
| } catch (final URISyntaxException e) { |
| this.error("Could not parse URI [" + destination + "]. Falling back to default of stdout."); |
| this.destination = DEFAULT_STREAM; |
| } catch (final FileNotFoundException e) { |
| this.error("File could not be found at [" + destination + "]. Falling back to default of stdout."); |
| this.destination = DEFAULT_STREAM; |
| } |
| return this; |
| } |
| |
| private PrintStream parseStreamName(final String name) throws URISyntaxException, FileNotFoundException { |
| if (name == null || name.equalsIgnoreCase("out")) { |
| return DEFAULT_STREAM; |
| } |
| if (name.equalsIgnoreCase("err")) { |
| return System.err; |
| } |
| final URI destUri = NetUtils.toURI(name); |
| final File output = FileUtils.fileFromUri(destUri); |
| if (output == null) { |
| // don't want any NPEs, no sir |
| return DEFAULT_STREAM; |
| } |
| final FileOutputStream fos = new FileOutputStream(output); |
| return new PrintStream(fos, true); |
| } |
| |
| /** |
| * Specifies the logging level by name to use for filtering StatusLogger messages. |
| * |
| * @param status name of logger level to filter below. |
| * @return {@code this} |
| * @see Level |
| */ |
| public StatusConfiguration setStatus(final String status) { |
| this.status = Level.toLevel(status, null); |
| if (this.status == null) { |
| this.error("Invalid status level specified: " + status + ". Defaulting to ERROR."); |
| this.status = Level.ERROR; |
| } |
| return this; |
| } |
| |
| /** |
| * Specifies the logging level to use for filtering StatusLogger messages. |
| * |
| * @param status logger level to filter below. |
| * @return {@code this} |
| */ |
| public StatusConfiguration setStatus(final Level status) { |
| this.status = status; |
| return this; |
| } |
| |
| /** |
| * Specifies the verbosity level to log at. This only applies to classes configured by |
| * {@link #setVerboseClasses(String...) verboseClasses}. |
| * |
| * @param verbosity basic filter for status logger messages. |
| * @return {@code this} |
| */ |
| public StatusConfiguration setVerbosity(final String verbosity) { |
| this.verbosity = Verbosity.toVerbosity(verbosity); |
| return this; |
| } |
| |
| /** |
| * Specifies which class names to filter if the configured verbosity level is QUIET. |
| * |
| * @param verboseClasses names of classes to filter if not using VERBOSE. |
| * @return {@code this} |
| */ |
| public StatusConfiguration setVerboseClasses(final String... verboseClasses) { |
| this.verboseClasses = verboseClasses; |
| return this; |
| } |
| |
| /** |
| * Configures and initializes the StatusLogger using the configured options in this instance. |
| */ |
| public void initialize() { |
| if (!this.initialized) { |
| if (this.status == Level.OFF) { |
| this.initialized = true; |
| } else { |
| final boolean configured = configureExistingStatusConsoleListener(); |
| if (!configured) { |
| registerNewStatusConsoleListener(); |
| } |
| migrateSavedLogMessages(); |
| } |
| } |
| } |
| |
| private boolean configureExistingStatusConsoleListener() { |
| boolean configured = false; |
| for (final StatusListener statusListener : this.logger.getListeners()) { |
| if (statusListener instanceof StatusConsoleListener) { |
| final StatusConsoleListener listener = (StatusConsoleListener) statusListener; |
| listener.setLevel(this.status); |
| this.logger.updateListenerLevel(this.status); |
| if (this.verbosity == Verbosity.QUIET) { |
| listener.setFilters(this.verboseClasses); |
| } |
| configured = true; |
| } |
| } |
| return configured; |
| } |
| |
| |
| private void registerNewStatusConsoleListener() { |
| final StatusConsoleListener listener = new StatusConsoleListener(this.status, this.destination); |
| if (this.verbosity == Verbosity.QUIET) { |
| listener.setFilters(this.verboseClasses); |
| } |
| this.logger.registerListener(listener); |
| } |
| |
| private void migrateSavedLogMessages() { |
| for (final String message : this.errorMessages) { |
| this.logger.error(message); |
| } |
| this.initialized = true; |
| this.errorMessages.clear(); |
| } |
| } |