mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-02 04:56:50 +01:00
Add ConsoleAppender from log4j for diff visibility
This commit is contained in:
parent
5b6f271037
commit
f3865064f8
1 changed files with 245 additions and 0 deletions
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* 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.appender;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.core.Filter;
|
||||||
|
import org.apache.logging.log4j.core.Layout;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||||
|
import org.apache.logging.log4j.core.helpers.Booleans;
|
||||||
|
import org.apache.logging.log4j.core.helpers.Loader;
|
||||||
|
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||||
|
import org.apache.logging.log4j.util.PropertiesUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConsoleAppender appends log events to <code>System.out</code> or
|
||||||
|
* <code>System.err</code> using a layout specified by the user. The
|
||||||
|
* default target is <code>System.out</code>.
|
||||||
|
* @doubt accessing System.out or .err as a byte stream instead of a writer
|
||||||
|
* bypasses the JVM's knowledge of the proper encoding. (RG) Encoding
|
||||||
|
* is handled within the Layout. Typically, a Layout will generate a String
|
||||||
|
* and then call getBytes which may use a configured encoding or the system
|
||||||
|
* default. OTOH, a Writer cannot print byte streams.
|
||||||
|
*/
|
||||||
|
@Plugin(name = "Console", category = "Core", elementType = "appender", printObject = true)
|
||||||
|
public final class ConsoleAppender extends AbstractOutputStreamAppender {
|
||||||
|
|
||||||
|
private static final String JANSI_CLASS = "org.fusesource.jansi.WindowsAnsiOutputStream";
|
||||||
|
private static ConsoleManagerFactory factory = new ConsoleManagerFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration of console destinations.
|
||||||
|
*/
|
||||||
|
public enum Target {
|
||||||
|
/** Standard output. */
|
||||||
|
SYSTEM_OUT,
|
||||||
|
/** Standard error output. */
|
||||||
|
SYSTEM_ERR
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
|
||||||
|
final OutputStreamManager manager,
|
||||||
|
final boolean ignoreExceptions) {
|
||||||
|
super(name, layout, filter, ignoreExceptions, true, manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Console Appender.
|
||||||
|
* @param layout The layout to use (required).
|
||||||
|
* @param filter The Filter or null.
|
||||||
|
* @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT".
|
||||||
|
* @param follow If true will follow changes to the underlying output stream.
|
||||||
|
* @param name The name of the Appender (required).
|
||||||
|
* @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
|
||||||
|
* they are propagated to the caller.
|
||||||
|
* @return The ConsoleAppender.
|
||||||
|
*/
|
||||||
|
@PluginFactory
|
||||||
|
public static ConsoleAppender createAppender(
|
||||||
|
@PluginElement("Layout") Layout<? extends Serializable> layout,
|
||||||
|
@PluginElement("Filters") final Filter filter,
|
||||||
|
@PluginAttribute("target") final String t,
|
||||||
|
@PluginAttribute("name") final String name,
|
||||||
|
@PluginAttribute("follow") final String follow,
|
||||||
|
@PluginAttribute("ignoreExceptions") final String ignore) {
|
||||||
|
if (name == null) {
|
||||||
|
LOGGER.error("No name provided for ConsoleAppender");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (layout == null) {
|
||||||
|
layout = PatternLayout.createLayout(null, null, null, null, null);
|
||||||
|
}
|
||||||
|
final boolean isFollow = Boolean.parseBoolean(follow);
|
||||||
|
final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
|
||||||
|
final Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t);
|
||||||
|
return new ConsoleAppender(name, layout, filter, getManager(isFollow, target, layout), ignoreExceptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OutputStreamManager getManager(final boolean follow, final Target target, final Layout<? extends Serializable> layout) {
|
||||||
|
final String type = target.name();
|
||||||
|
final OutputStream os = getOutputStream(follow, target);
|
||||||
|
return OutputStreamManager.getManager(target.name() + "." + follow, new FactoryData(os, type, layout), factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OutputStream getOutputStream(final boolean follow, final Target target) {
|
||||||
|
final String enc = Charset.defaultCharset().name();
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
printStream = target == Target.SYSTEM_OUT ?
|
||||||
|
follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out :
|
||||||
|
follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err;
|
||||||
|
} catch (final UnsupportedEncodingException ex) { // should never happen
|
||||||
|
throw new IllegalStateException("Unsupported default encoding " + enc, ex);
|
||||||
|
}
|
||||||
|
final PropertiesUtil propsUtil = PropertiesUtil.getProperties();
|
||||||
|
if (!propsUtil.getStringProperty("os.name").startsWith("Windows") ||
|
||||||
|
propsUtil.getBooleanProperty("log4j.skipJansi")) {
|
||||||
|
return printStream;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final ClassLoader loader = Loader.getClassLoader();
|
||||||
|
// We type the parameter as a wildcard to avoid a hard reference to Jansi.
|
||||||
|
final Class<?> clazz = loader.loadClass(JANSI_CLASS);
|
||||||
|
final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
|
||||||
|
return (OutputStream) constructor.newInstance(printStream);
|
||||||
|
} catch (final ClassNotFoundException cnfe) {
|
||||||
|
LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS);
|
||||||
|
} catch (final NoSuchMethodException nsme) {
|
||||||
|
LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS);
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
LOGGER.warn("Unable to instantiate {}", JANSI_CLASS);
|
||||||
|
}
|
||||||
|
return printStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of OutputStream that redirects to the current System.err.
|
||||||
|
*/
|
||||||
|
private static class SystemErrStream extends OutputStream {
|
||||||
|
public SystemErrStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// do not close sys err!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
System.err.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final byte[] b) throws IOException {
|
||||||
|
System.err.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final byte[] b, final int off, final int len)
|
||||||
|
throws IOException {
|
||||||
|
System.err.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final int b) {
|
||||||
|
System.err.write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of OutputStream that redirects to the current System.out.
|
||||||
|
*/
|
||||||
|
private static class SystemOutStream extends OutputStream {
|
||||||
|
public SystemOutStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// do not close sys out!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
System.out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final byte[] b) throws IOException {
|
||||||
|
System.out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final byte[] b, final int off, final int len)
|
||||||
|
throws IOException {
|
||||||
|
System.out.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final int b) throws IOException {
|
||||||
|
System.out.write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data to pass to factory method.
|
||||||
|
*/
|
||||||
|
private static class FactoryData {
|
||||||
|
private final OutputStream os;
|
||||||
|
private final String type;
|
||||||
|
private final Layout<? extends Serializable> layout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param os The OutputStream.
|
||||||
|
* @param type The name of the target.
|
||||||
|
* @param layout A Serializable layout
|
||||||
|
*/
|
||||||
|
public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
|
||||||
|
this.os = os;
|
||||||
|
this.type = type;
|
||||||
|
this.layout = layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory to create the Appender.
|
||||||
|
*/
|
||||||
|
private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an OutputStreamManager.
|
||||||
|
* @param name The name of the entity to manage.
|
||||||
|
* @param data The data required to create the entity.
|
||||||
|
* @return The OutputStreamManager
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public OutputStreamManager createManager(final String name, final FactoryData data) {
|
||||||
|
return new OutputStreamManager(data.os, data.type, data.layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue