blob: 6070d30bca26bf3d57cd7ac311dae1c0b9415cfb [file] [log] [blame]
/*
* 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();
}
}