Move to using Minecrell's terminal logger directly, for better ANSI control and JLine support.
Signed-off-by: cpw <cpw+github@weeksfamily.ca>
This commit is contained in:
parent
13501a8b05
commit
c6afb2be3f
11 changed files with 38 additions and 982 deletions
10
build.gradle
10
build.gradle
|
@ -181,15 +181,16 @@ project(':forge') {
|
||||||
nativesDirectory: extractNatives.output
|
nativesDirectory: extractNatives.output
|
||||||
]
|
]
|
||||||
properties = [
|
properties = [
|
||||||
'org.lwjgl.util.Debug': 'true',
|
// 'org.lwjgl.util.Debug': 'true',
|
||||||
'org.lwjgl.util.DebugLoader': 'true',
|
// 'org.lwjgl.util.DebugLoader': 'true',
|
||||||
'org.lwjgl.system.SharedLibraryExtractDirectory': 'lwjgl_dll',
|
'org.lwjgl.system.SharedLibraryExtractDirectory': 'lwjgl_dll',
|
||||||
'mc.version': MC_VERSION,
|
'mc.version': MC_VERSION,
|
||||||
'mcp.version': MCP_VERSION,
|
'mcp.version': MCP_VERSION,
|
||||||
'forge.version': project.version.substring(MC_VERSION.length() + 1),
|
'forge.version': project.version.substring(MC_VERSION.length() + 1),
|
||||||
'forge.spec': SPEC_VERSION,
|
'forge.spec': SPEC_VERSION,
|
||||||
'forge.group': project.group,
|
'forge.group': project.group,
|
||||||
'fmllauncher.version': SPEC_VERSION
|
'fmllauncher.version': SPEC_VERSION,
|
||||||
|
'terminal.ansi': 'true'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
forge_server = {
|
forge_server = {
|
||||||
|
@ -276,12 +277,13 @@ project(':forge') {
|
||||||
installer 'net.minecraftforge:coremods:0.2.+'
|
installer 'net.minecraftforge:coremods:0.2.+'
|
||||||
installer 'com.electronwill.night-config:core:3.4.2'
|
installer 'com.electronwill.night-config:core:3.4.2'
|
||||||
installer 'com.electronwill.night-config:toml:3.4.2'
|
installer 'com.electronwill.night-config:toml:3.4.2'
|
||||||
installer 'org.jline:jline:3.5.1'
|
installer 'org.jline:jline:3.9.0'
|
||||||
installer 'org.apache.maven:maven-artifact:3.6.0'
|
installer 'org.apache.maven:maven-artifact:3.6.0'
|
||||||
installer 'net.jodah:typetools:0.6.0'
|
installer 'net.jodah:typetools:0.6.0'
|
||||||
installer 'java3d:vecmath:1.5.2'
|
installer 'java3d:vecmath:1.5.2'
|
||||||
installer 'org.apache.logging.log4j:log4j-api:2.11.1'
|
installer 'org.apache.logging.log4j:log4j-api:2.11.1'
|
||||||
installer 'org.apache.logging.log4j:log4j-core:2.11.1'
|
installer 'org.apache.logging.log4j:log4j-core:2.11.1'
|
||||||
|
installer 'net.minecrell:terminalconsoleappender:1.1.+'
|
||||||
fmllauncherImplementation 'com.google.guava:guava:21.0'
|
fmllauncherImplementation 'com.google.guava:guava:21.0'
|
||||||
fmllauncherImplementation 'com.google.code.gson:gson:2.8.0'
|
fmllauncherImplementation 'com.google.code.gson:gson:2.8.0'
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:5.0.0"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:5.0.0"
|
||||||
|
|
|
@ -67,6 +67,8 @@ public class LaunchTesting
|
||||||
"--assetsDir", assets,
|
"--assetsDir", assets,
|
||||||
"--userProperties", "{}"
|
"--userProperties", "{}"
|
||||||
}, String.class);
|
}, String.class);
|
||||||
|
} else {
|
||||||
|
launchArgs = ObjectArrays.concat(launchArgs, args, String.class);
|
||||||
}
|
}
|
||||||
Launcher.main(launchArgs);
|
Launcher.main(launchArgs);
|
||||||
Thread.sleep(10000);
|
Thread.sleep(10000);
|
||||||
|
|
|
@ -19,9 +19,8 @@
|
||||||
|
|
||||||
package net.minecraftforge.server.console;
|
package net.minecraftforge.server.console;
|
||||||
|
|
||||||
import net.minecraftforge.server.terminalconsole.TerminalConsoleAppender;
|
|
||||||
|
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
import net.minecraft.server.dedicated.DedicatedServer;
|
||||||
|
import net.minecrell.terminalconsole.TerminalConsoleAppender;
|
||||||
import org.jline.reader.EndOfFileException;
|
import org.jline.reader.EndOfFileException;
|
||||||
import org.jline.reader.LineReader;
|
import org.jline.reader.LineReader;
|
||||||
import org.jline.reader.LineReaderBuilder;
|
import org.jline.reader.LineReaderBuilder;
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
/*
|
|
||||||
* TerminalConsoleAppender
|
|
||||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.minecraftforge.server.terminalconsole;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.Level;
|
|
||||||
import org.apache.logging.log4j.core.LogEvent;
|
|
||||||
import org.apache.logging.log4j.core.config.Configuration;
|
|
||||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
|
||||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
|
||||||
import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
|
||||||
import org.apache.logging.log4j.core.pattern.HighlightConverter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternConverter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternParser;
|
|
||||||
import org.apache.logging.log4j.util.PerformanceSensitive;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simplified version of {@link HighlightConverter} that uses
|
|
||||||
* {@link TerminalConsoleAppender} to detect if Ansi escape codes can be used
|
|
||||||
* to highlight errors and warnings in the console.
|
|
||||||
*
|
|
||||||
* <p>If configured, it will mark all logged errors with a red color and all
|
|
||||||
* warnings with a yellow color. It can be only used together with
|
|
||||||
* {@link TerminalConsoleAppender}.</p>
|
|
||||||
*
|
|
||||||
* <p>{@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} may be used
|
|
||||||
* to force the use of ANSI colors even in unsupported environments.</p>
|
|
||||||
*
|
|
||||||
* <p><b>Example usage:</b> {@code %highlightError{%level: %message}}</p>
|
|
||||||
*/
|
|
||||||
@Plugin(name = "highlightError", category = PatternConverter.CATEGORY)
|
|
||||||
@ConverterKeys({ "highlightError" })
|
|
||||||
@PerformanceSensitive("allocation")
|
|
||||||
public class HighlightErrorConverter extends LogEventPatternConverter
|
|
||||||
{
|
|
||||||
private static final String ANSI_RESET = "\u001B[39;0m";
|
|
||||||
private static final String ANSI_ERROR = "\u001B[31;1m";
|
|
||||||
private static final String ANSI_WARN = "\u001B[33;1m";
|
|
||||||
|
|
||||||
private final List<PatternFormatter> formatters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct the converter.
|
|
||||||
*
|
|
||||||
* @param formatters The pattern formatters to generate the text to highlight
|
|
||||||
*/
|
|
||||||
protected HighlightErrorConverter(List<PatternFormatter> formatters)
|
|
||||||
{
|
|
||||||
super("highlightError", null);
|
|
||||||
this.formatters = formatters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void format(LogEvent event, StringBuilder toAppendTo)
|
|
||||||
{
|
|
||||||
if (TerminalConsoleAppender.isAnsiSupported())
|
|
||||||
{
|
|
||||||
Level level = event.getLevel();
|
|
||||||
if (level.isMoreSpecificThan(Level.ERROR))
|
|
||||||
{
|
|
||||||
format(ANSI_ERROR, event, toAppendTo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (level.isMoreSpecificThan(Level.WARN))
|
|
||||||
{
|
|
||||||
format(ANSI_WARN, event, toAppendTo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection ForLoopReplaceableByForEach
|
|
||||||
for (int i = 0, size = formatters.size(); i < size; i++)
|
|
||||||
{
|
|
||||||
formatters.get(i).format(event, toAppendTo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void format(String style, LogEvent event, StringBuilder toAppendTo)
|
|
||||||
{
|
|
||||||
int start = toAppendTo.length();
|
|
||||||
toAppendTo.append(style);
|
|
||||||
int end = toAppendTo.length();
|
|
||||||
|
|
||||||
//noinspection ForLoopReplaceableByForEach
|
|
||||||
for (int i = 0, size = formatters.size(); i < size; i++)
|
|
||||||
{
|
|
||||||
formatters.get(i).format(event, toAppendTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toAppendTo.length() == end)
|
|
||||||
{
|
|
||||||
// No content so we don't need to append the ANSI escape code
|
|
||||||
toAppendTo.setLength(start);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Append reset code after the line
|
|
||||||
toAppendTo.append(ANSI_RESET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handlesThrowable()
|
|
||||||
{
|
|
||||||
for (final PatternFormatter formatter : formatters)
|
|
||||||
{
|
|
||||||
if (formatter.handlesThrowable())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a new instance of the {@link HighlightErrorConverter} with the
|
|
||||||
* specified options.
|
|
||||||
*
|
|
||||||
* @param config The current configuration
|
|
||||||
* @param options The pattern options
|
|
||||||
* @return The new instance
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static HighlightErrorConverter newInstance(Configuration config, String[] options)
|
|
||||||
{
|
|
||||||
if (options.length != 1)
|
|
||||||
{
|
|
||||||
LOGGER.error("Incorrect number of options on highlightError. Expected 1 received " + options.length);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (options[0] == null)
|
|
||||||
{
|
|
||||||
LOGGER.error("No pattern supplied on highlightError");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PatternParser parser = PatternLayout.createPatternParser(config);
|
|
||||||
List<PatternFormatter> formatters = parser.parse(options[0]);
|
|
||||||
return new HighlightErrorConverter(formatters);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,222 +0,0 @@
|
||||||
/*
|
|
||||||
* TerminalConsoleAppender
|
|
||||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.minecraftforge.server.terminalconsole;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.core.LogEvent;
|
|
||||||
import org.apache.logging.log4j.core.config.Configuration;
|
|
||||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
|
||||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
|
||||||
import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
|
||||||
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternConverter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternParser;
|
|
||||||
import org.apache.logging.log4j.util.PerformanceSensitive;
|
|
||||||
import org.apache.logging.log4j.util.PropertiesUtil;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces Minecraft formatting codes in the result of a pattern with
|
|
||||||
* appropriate ANSI escape codes. The implementation will only replace valid
|
|
||||||
* color codes using the section sign (§).
|
|
||||||
*
|
|
||||||
* <p>The {@link MinecraftFormattingConverter} can be only used together with
|
|
||||||
* {@link TerminalConsoleAppender} to detect if the current console supports
|
|
||||||
* color output. When running in an unsupported environment, it will
|
|
||||||
* automatically strip all formatting codes instead.</p>
|
|
||||||
*
|
|
||||||
* <p>{@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} may be used
|
|
||||||
* to force the use of ANSI colors even in unsupported environments. As an
|
|
||||||
* alternative, {@link #KEEP_FORMATTING_PROPERTY} may be used to keep the
|
|
||||||
* raw Minecraft formatting codes.</p>
|
|
||||||
*
|
|
||||||
* <p><b>Example usage:</b> {@code %minecraftFormatting{%message}}<br>
|
|
||||||
* It can be configured to always strip formatting codes from the message:
|
|
||||||
* {@code %minecraftFormatting{%message}{strip}}</p>
|
|
||||||
*
|
|
||||||
* @see <a href="http://minecraft.gamepedia.com/Formatting_codes">
|
|
||||||
* Formatting Codes</a>
|
|
||||||
*/
|
|
||||||
@Plugin(name = "minecraftFormatting", category = PatternConverter.CATEGORY)
|
|
||||||
@ConverterKeys({ "minecraftFormatting" })
|
|
||||||
@PerformanceSensitive("allocation")
|
|
||||||
public class MinecraftFormattingConverter extends LogEventPatternConverter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* System property that allows disabling the replacement of Minecraft
|
|
||||||
* formatting codes entirely, keeping them in the console output. For
|
|
||||||
* some applications they might be easier and more accurate for parsing
|
|
||||||
* in applications like certain control panels.
|
|
||||||
*
|
|
||||||
* <p>If this system property is not set, or set to any value except
|
|
||||||
* {@code true}, all Minecraft formatting codes will be replaced
|
|
||||||
* or stripped from the console output.</p>
|
|
||||||
*/
|
|
||||||
public static final String KEEP_FORMATTING_PROPERTY = TerminalConsoleAppender.PROPERTY_PREFIX + ".keepMinecraftFormatting";
|
|
||||||
|
|
||||||
private static final boolean KEEP_FORMATTING = PropertiesUtil.getProperties().getBooleanProperty(KEEP_FORMATTING_PROPERTY);
|
|
||||||
|
|
||||||
private static final String ANSI_RESET = "\u001B[39;0m";
|
|
||||||
|
|
||||||
private static final char COLOR_CHAR = '\u00A7'; // §
|
|
||||||
private static final String LOOKUP = "0123456789abcdefklmnor";
|
|
||||||
|
|
||||||
private static final String[] ansiCodes = new String[] {
|
|
||||||
"\u001B[0;30;22m", // Black §0
|
|
||||||
"\u001B[0;34;22m", // Dark Blue §1
|
|
||||||
"\u001B[0;32;22m", // Dark Green §2
|
|
||||||
"\u001B[0;36;22m", // Dark Aqua §3
|
|
||||||
"\u001B[0;31;22m", // Dark Red §4
|
|
||||||
"\u001B[0;35;22m", // Dark Purple §5
|
|
||||||
"\u001B[0;33;22m", // Gold §6
|
|
||||||
"\u001B[0;37;22m", // Gray §7
|
|
||||||
"\u001B[0;30;1m", // Dark Gray §8
|
|
||||||
"\u001B[0;34;1m", // Blue §9
|
|
||||||
"\u001B[0;32;1m", // Green §a
|
|
||||||
"\u001B[0;36;1m", // Aqua §b
|
|
||||||
"\u001B[0;31;1m", // Red §c
|
|
||||||
"\u001B[0;35;1m", // Light Purple §d
|
|
||||||
"\u001B[0;33;1m", // Yellow §e
|
|
||||||
"\u001B[0;37;1m", // White §f
|
|
||||||
"\u001B[5m", // Obfuscated §k
|
|
||||||
"\u001B[21m", // Bold §l
|
|
||||||
"\u001B[9m", // Strikethrough §m
|
|
||||||
"\u001B[4m", // Underline §n
|
|
||||||
"\u001B[3m", // Italic §o
|
|
||||||
ANSI_RESET, // Reset §r
|
|
||||||
};
|
|
||||||
|
|
||||||
private final boolean ansi;
|
|
||||||
private final List<PatternFormatter> formatters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct the converter.
|
|
||||||
*
|
|
||||||
* @param formatters The pattern formatters to generate the text to manipulate
|
|
||||||
* @param strip If true, the converter will strip all formatting codes
|
|
||||||
*/
|
|
||||||
protected MinecraftFormattingConverter(List<PatternFormatter> formatters, boolean strip)
|
|
||||||
{
|
|
||||||
super("minecraftFormatting", null);
|
|
||||||
this.formatters = formatters;
|
|
||||||
this.ansi = !strip;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void format(LogEvent event, StringBuilder toAppendTo)
|
|
||||||
{
|
|
||||||
int start = toAppendTo.length();
|
|
||||||
//noinspection ForLoopReplaceableByForEach
|
|
||||||
for (int i = 0, size = formatters.size(); i < size; i++)
|
|
||||||
{
|
|
||||||
formatters.get(i).format(event, toAppendTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (KEEP_FORMATTING || toAppendTo.length() == start)
|
|
||||||
{
|
|
||||||
// Skip replacement if disabled or if the content is empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String content = toAppendTo.substring(start);
|
|
||||||
format(content, toAppendTo, start, ansi && TerminalConsoleAppender.isAnsiSupported());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void format(String s, StringBuilder result, int start, boolean ansi)
|
|
||||||
{
|
|
||||||
int next = s.indexOf(COLOR_CHAR);
|
|
||||||
int last = s.length() - 1;
|
|
||||||
if (next == -1 || next == last)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.setLength(start + next);
|
|
||||||
|
|
||||||
int pos = next;
|
|
||||||
int format;
|
|
||||||
do {
|
|
||||||
if (pos != next)
|
|
||||||
{
|
|
||||||
result.append(s, pos, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
format = LOOKUP.indexOf(s.charAt(next + 1));
|
|
||||||
if (format != -1)
|
|
||||||
{
|
|
||||||
if (ansi)
|
|
||||||
{
|
|
||||||
result.append(ansiCodes[format]);
|
|
||||||
}
|
|
||||||
pos = next += 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
next++;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = s.indexOf(COLOR_CHAR, next);
|
|
||||||
} while (next != -1 && next < last);
|
|
||||||
|
|
||||||
result.append(s, pos, s.length());
|
|
||||||
if (ansi)
|
|
||||||
{
|
|
||||||
result.append(ANSI_RESET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a new instance of the {@link MinecraftFormattingConverter} with the
|
|
||||||
* specified options.
|
|
||||||
*
|
|
||||||
* @param config The current configuration
|
|
||||||
* @param options The pattern options
|
|
||||||
* @return The new instance
|
|
||||||
*
|
|
||||||
* @see MinecraftFormattingConverter
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static MinecraftFormattingConverter newInstance(Configuration config, String[] options)
|
|
||||||
{
|
|
||||||
if (options.length < 1 || options.length > 2)
|
|
||||||
{
|
|
||||||
LOGGER.error("Incorrect number of options on minecraftFormatting. Expected at least 1, max 2 received " + options.length);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (options[0] == null)
|
|
||||||
{
|
|
||||||
LOGGER.error("No pattern supplied on minecraftFormatting");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PatternParser parser = PatternLayout.createPatternParser(config);
|
|
||||||
List<PatternFormatter> formatters = parser.parse(options[0]);
|
|
||||||
boolean strip = options.length > 1 && "strip".equals(options[1]);
|
|
||||||
return new MinecraftFormattingConverter(formatters, strip);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,374 +0,0 @@
|
||||||
/*
|
|
||||||
* TerminalConsoleAppender
|
|
||||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.minecraftforge.server.terminalconsole;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.core.Appender;
|
|
||||||
import org.apache.logging.log4j.core.Core;
|
|
||||||
import org.apache.logging.log4j.core.Filter;
|
|
||||||
import org.apache.logging.log4j.core.Layout;
|
|
||||||
import org.apache.logging.log4j.core.LogEvent;
|
|
||||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
|
||||||
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.config.plugins.validation.constraints.Required;
|
|
||||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
|
||||||
import org.apache.logging.log4j.util.PropertiesUtil;
|
|
||||||
import org.jline.reader.LineReader;
|
|
||||||
import org.jline.terminal.Terminal;
|
|
||||||
import org.jline.terminal.TerminalBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link Appender} that uses the JLine 3.x {@link Terminal} to print messages
|
|
||||||
* to the console.
|
|
||||||
*
|
|
||||||
* <p>The JLine {@link Terminal} extends the regular console output with support
|
|
||||||
* for Ansi escape codes on Windows. Additionally, it's {@link LineReader}
|
|
||||||
* interface can be used to implement enhanced console input, with an
|
|
||||||
* persistent input line, as well as command history and command completion.</p>
|
|
||||||
*
|
|
||||||
* <p>The {@code TerminalConsole} appender replaces the default {@code Console}
|
|
||||||
* appender in your log4j configuration. By default, log4j will automatically
|
|
||||||
* close the standard output when the original {@code Console} appender is
|
|
||||||
* removed. Consequently, it is necessary to keep an unused {@code Console}
|
|
||||||
* appender.</p>
|
|
||||||
*
|
|
||||||
* <p><b>Example usage:</b></p>
|
|
||||||
* <pre>{@code <TerminalConsole>
|
|
||||||
* <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
|
||||||
* </TerminalConsole>
|
|
||||||
*
|
|
||||||
* <Console name="SysOut" target="SYSTEM_OUT"/>}</pre>
|
|
||||||
*
|
|
||||||
* <p>To use the enhanced console input it is necessary to set the
|
|
||||||
* {@link LineReader} using {@link #setReader(LineReader)}. The appender will
|
|
||||||
* then automatically redraw the current prompt. When creating the
|
|
||||||
* {@link LineReader} it's important to use the {@link Terminal}
|
|
||||||
* returned by {@link #getTerminal()}. Additionally, the reader should
|
|
||||||
* be removed from the appender as soon as it's no longer accepting
|
|
||||||
* input (for example when the user interrupted input using CTRL + C.</p>
|
|
||||||
*
|
|
||||||
* <p>By default, the JLine {@link Terminal} is enabled when the application
|
|
||||||
* is started with an attached terminal session. Usually, this is only the
|
|
||||||
* case if the application is started from the command line, not if it gets
|
|
||||||
* started by another application.</p>
|
|
||||||
*
|
|
||||||
* <p>In some cases, it might be possible to support a subset of the features
|
|
||||||
* in these unsupported environments (e.g. only ANSI color codes). In these
|
|
||||||
* cases, the system properties may be used to override the default behaviour:
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link TerminalConsoleAppender#JLINE_OVERRIDE_PROPERTY} - To enable the extended JLine
|
|
||||||
* input. By default this will also enable the ANSI escape codes.</li>
|
|
||||||
* <li>{@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} - To enable the output of ANSI
|
|
||||||
* escape codes. May be used to force the use of ANSI escape codes
|
|
||||||
* if JLine is disabled or to disable them if it is enabled.</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
@Plugin(name = TerminalConsoleAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
|
|
||||||
public class TerminalConsoleAppender extends AbstractAppender
|
|
||||||
{
|
|
||||||
public static final String PLUGIN_NAME = "TerminalConsole";
|
|
||||||
/**
|
|
||||||
* The prefix used for all system properties in TerminalConsoleAppender.
|
|
||||||
*/
|
|
||||||
public static final String PROPERTY_PREFIX = "terminal";
|
|
||||||
/**
|
|
||||||
* System property that allows overriding the default detection of the
|
|
||||||
* console to force enable or force disable the use of JLine. In some
|
|
||||||
* environments the automatic detection might not work properly.
|
|
||||||
* <p>
|
|
||||||
* <p>If this system property is not set, or set to an invalid value
|
|
||||||
* (neither {@code true} nor {@code false}) then we will attempt
|
|
||||||
* to detect the best option automatically.</p>
|
|
||||||
*/
|
|
||||||
public static final String JLINE_OVERRIDE_PROPERTY = PROPERTY_PREFIX + ".jline";
|
|
||||||
/**
|
|
||||||
* System property that allows overriding the use of ANSI escape codes
|
|
||||||
* for console formatting even though running in an unsupported
|
|
||||||
* environment. By default, ANSI color codes are only enabled if JLine
|
|
||||||
* is enabled. Some systems might be able to handle ANSI escape codes
|
|
||||||
* but are not capable of JLine's extended input mechanism.
|
|
||||||
* <p>
|
|
||||||
* <p>If this system property is not set, or set to an invalid value
|
|
||||||
* (neither {@code true} nor {@code false}) then we will attempt
|
|
||||||
* to detect the best option automatically.</p>
|
|
||||||
*/
|
|
||||||
public static final String ANSI_OVERRIDE_PROPERTY = PROPERTY_PREFIX + ".ansi";
|
|
||||||
public static final Boolean ANSI_OVERRIDE = getOptionalBooleanProperty(ANSI_OVERRIDE_PROPERTY);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We grab the standard output {@link PrintStream} early, otherwise we
|
|
||||||
* might cause infinite loops later if the application redirects
|
|
||||||
* {@link System#out} to Log4J.
|
|
||||||
*/
|
|
||||||
private static final PrintStream stdout = System.out;
|
|
||||||
|
|
||||||
private static boolean initialized;
|
|
||||||
@Nullable
|
|
||||||
private static Terminal terminal;
|
|
||||||
@Nullable
|
|
||||||
private static LineReader reader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link Terminal} that is used to print messages to the
|
|
||||||
* console. Returns {@code null} in unsupported environments, unless
|
|
||||||
* overridden using the {@link TerminalConsoleAppender#JLINE_OVERRIDE_PROPERTY} system
|
|
||||||
* property.
|
|
||||||
*
|
|
||||||
* @return The terminal, or null if not supported
|
|
||||||
* @see TerminalConsoleAppender
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Terminal getTerminal()
|
|
||||||
{
|
|
||||||
return terminal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently configured {@link LineReader} that is used to
|
|
||||||
* read input from the console. May be null if no {@link LineReader}
|
|
||||||
* was configured by the environment.
|
|
||||||
*
|
|
||||||
* @return The current line reader, or null if none
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static LineReader getReader()
|
|
||||||
{
|
|
||||||
return reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link LineReader} that is used to read input from the console.
|
|
||||||
* Setting the {@link LineReader} will allow the appender to automatically
|
|
||||||
* redraw the input line when a new log message is added.
|
|
||||||
*
|
|
||||||
* <p><b>Note:</b> The specified {@link LineReader} must be created with
|
|
||||||
* the terminal returned by {@link #getTerminal()}.</p>
|
|
||||||
*
|
|
||||||
* @param newReader The new line reader
|
|
||||||
*/
|
|
||||||
public static void setReader(@Nullable LineReader newReader)
|
|
||||||
{
|
|
||||||
if (newReader != null && newReader.getTerminal() != terminal)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Reader was not created with TerminalConsoleAppender.getTerminal()");
|
|
||||||
}
|
|
||||||
|
|
||||||
reader = newReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether ANSI escapes codes should be written to the console
|
|
||||||
* output.
|
|
||||||
*
|
|
||||||
* <p>The return value is {@code true} by default if the JLine terminal
|
|
||||||
* is enabled and {@code false} otherwise. It may be overridden using
|
|
||||||
* the {@link TerminalConsoleAppender#ANSI_OVERRIDE_PROPERTY} system property.</p>
|
|
||||||
*
|
|
||||||
* @return true if ANSI escapes codes should be written to the console
|
|
||||||
*/
|
|
||||||
public static boolean isAnsiSupported()
|
|
||||||
{
|
|
||||||
return ANSI_OVERRIDE != null ? ANSI_OVERRIDE : terminal != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new {@link TerminalConsoleAppender}.
|
|
||||||
*
|
|
||||||
* @param name The name of the appender
|
|
||||||
* @param filter The filter, can be {@code null}
|
|
||||||
* @param layout The layout to use
|
|
||||||
* @param ignoreExceptions If {@code true} exceptions encountered when
|
|
||||||
* appending events are logged, otherwise they are propagated to the
|
|
||||||
* caller
|
|
||||||
*/
|
|
||||||
protected TerminalConsoleAppender(String name, @Nullable Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions)
|
|
||||||
{
|
|
||||||
super(name, filter, layout, ignoreExceptions);
|
|
||||||
initializeTerminal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void initializeTerminal()
|
|
||||||
{
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
// A system property can be used to override our automatic detection
|
|
||||||
Boolean jlineOverride = getOptionalBooleanProperty(JLINE_OVERRIDE_PROPERTY);
|
|
||||||
|
|
||||||
// By default, we disable JLine if there is no terminal attached
|
|
||||||
// (e.g. if the program output is redirected to a file or if it's
|
|
||||||
// started by some kind of control panel)
|
|
||||||
|
|
||||||
// The same applies to IDEs, they usually provide only a very basic
|
|
||||||
// console implementation without support for ANSI escape codes
|
|
||||||
// (used for colors) or characters like \r.
|
|
||||||
|
|
||||||
// There are two exceptions:
|
|
||||||
// 1. IntelliJ IDEA supports colors and control characters
|
|
||||||
// (We try to detect it using an additional JAR it adds to the classpath)
|
|
||||||
// 2. The system property forces the use of JLine.
|
|
||||||
boolean dumb = jlineOverride == Boolean.TRUE || System.getProperty("java.class.path").contains("idea_rt.jar");
|
|
||||||
|
|
||||||
if (jlineOverride != Boolean.FALSE)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
terminal = TerminalBuilder.builder().dumb(dumb).build();
|
|
||||||
}
|
|
||||||
catch (IllegalStateException e)
|
|
||||||
{
|
|
||||||
// Unless disabled using one of the exceptions above,
|
|
||||||
// JLine throws an exception before creating a dumb terminal
|
|
||||||
// Dumb terminals are used if there is no real terminal attached
|
|
||||||
// to the application.
|
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled())
|
|
||||||
{
|
|
||||||
// Log with stacktrace
|
|
||||||
LOGGER.warn("Disabling terminal, you're running in an unsupported environment.", e);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.warn("Disabling terminal, you're running in an unsupported environment.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Failed to initialize terminal. Falling back to standard output", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static Boolean getOptionalBooleanProperty(String name)
|
|
||||||
{
|
|
||||||
String value = PropertiesUtil.getProperties().getStringProperty(name);
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.equalsIgnoreCase("true"))
|
|
||||||
{
|
|
||||||
return Boolean.TRUE;
|
|
||||||
}
|
|
||||||
else if (value.equalsIgnoreCase("false"))
|
|
||||||
{
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.warn("Invalid value for boolean input property '{}': {}", name, value);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void append(LogEvent event)
|
|
||||||
{
|
|
||||||
if (terminal != null)
|
|
||||||
{
|
|
||||||
if (reader != null)
|
|
||||||
{
|
|
||||||
// Draw the prompt line again if a reader is available
|
|
||||||
reader.callWidget(LineReader.CLEAR);
|
|
||||||
terminal.writer().print(getLayout().toSerializable(event));
|
|
||||||
reader.callWidget(LineReader.REDRAW_LINE);
|
|
||||||
reader.callWidget(LineReader.REDISPLAY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
terminal.writer().print(getLayout().toSerializable(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
terminal.writer().flush();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stdout.print(getLayout().toSerializable(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the JLine {@link Terminal} (if available) and restores the original
|
|
||||||
* terminal settings.
|
|
||||||
*
|
|
||||||
* @throws IOException If an I/O error occurs
|
|
||||||
*/
|
|
||||||
public static void close() throws IOException
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
initialized = false;
|
|
||||||
if (terminal != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
terminal.close();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
terminal = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link TerminalConsoleAppender}.
|
|
||||||
*
|
|
||||||
* @param name The name of the appender
|
|
||||||
* @param filter The filter, can be {@code null}
|
|
||||||
* @param layout The layout, can be {@code null}
|
|
||||||
* @param ignoreExceptions If {@code true} exceptions encountered when
|
|
||||||
* appending events are logged, otherwise they are propagated to the
|
|
||||||
* caller
|
|
||||||
* @return The new appender
|
|
||||||
*/
|
|
||||||
@PluginFactory
|
|
||||||
public static TerminalConsoleAppender createAppender(
|
|
||||||
@Required(message = "No name provided for TerminalConsoleAppender") @PluginAttribute("name") String name,
|
|
||||||
@PluginElement("Filter") @Nullable Filter filter,
|
|
||||||
@PluginElement("Layout") @Nullable Layout<? extends Serializable> layout,
|
|
||||||
@PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) boolean ignoreExceptions)
|
|
||||||
{
|
|
||||||
if (layout == null)
|
|
||||||
{
|
|
||||||
layout = PatternLayout.createDefaultLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TerminalConsoleAppender(name, filter, layout, ignoreExceptions);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
@MethodsReturnNonnullByDefault
|
|
||||||
package net.minecraftforge.server.terminalconsole;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
|
|
||||||
import mcp.MethodsReturnNonnullByDefault;
|
|
|
@ -1,171 +0,0 @@
|
||||||
/*
|
|
||||||
* TerminalConsoleAppender
|
|
||||||
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.minecraftforge.server.terminalconsole.util;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.core.LogEvent;
|
|
||||||
import org.apache.logging.log4j.core.config.Configuration;
|
|
||||||
import org.apache.logging.log4j.core.config.Node;
|
|
||||||
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.PluginConfiguration;
|
|
||||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
|
||||||
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
|
||||||
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
|
|
||||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
|
||||||
import org.apache.logging.log4j.core.layout.PatternMatch;
|
|
||||||
import org.apache.logging.log4j.core.layout.PatternSelector;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
|
||||||
import org.apache.logging.log4j.core.pattern.PatternParser;
|
|
||||||
import org.apache.logging.log4j.util.PerformanceSensitive;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link PatternSelector} that selects patterns based on the logger name.
|
|
||||||
* Can be used to log messages from different loggers using different patterns.
|
|
||||||
*
|
|
||||||
* <p>Multiple logger names may be separated using comma in the
|
|
||||||
* {@link PatternMatch#getKey() PatternMatch "key"}. The pattern will be applied
|
|
||||||
* if the logger name matches at least one of them.</p>
|
|
||||||
*
|
|
||||||
* <p><b>Example usage:</b></p>
|
|
||||||
* <pre>{@code <PatternLayout>
|
|
||||||
* <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss} %level] [%logger]: %msg%n">
|
|
||||||
* <!-- Log root (empty logger name), "Main", and net.minecrell.* without logger prefix -->
|
|
||||||
* <PatternMatch key=",Main,net.minecrell." pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
|
||||||
* <PatternMatch key="com.example.Logger" pattern="EXAMPLE: %msg%n"/>
|
|
||||||
* </LoggerNamePatternSelector>
|
|
||||||
* </PatternLayout>}</pre>
|
|
||||||
*/
|
|
||||||
@Plugin(name = "LoggerNamePatternSelector", category = Node.CATEGORY, elementType = PatternSelector.ELEMENT_TYPE)
|
|
||||||
@PerformanceSensitive("allocation")
|
|
||||||
public class LoggerNamePatternSelector implements PatternSelector
|
|
||||||
{
|
|
||||||
private static class LoggerNameSelector
|
|
||||||
{
|
|
||||||
private final String name;
|
|
||||||
private final boolean isPackage;
|
|
||||||
private final PatternFormatter[] formatters;
|
|
||||||
|
|
||||||
LoggerNameSelector(String name, PatternFormatter[] formatters)
|
|
||||||
{
|
|
||||||
this.name = name;
|
|
||||||
this.isPackage = name.endsWith(".");
|
|
||||||
this.formatters = formatters;
|
|
||||||
}
|
|
||||||
|
|
||||||
PatternFormatter[] get()
|
|
||||||
{
|
|
||||||
return this.formatters;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean test(String s)
|
|
||||||
{
|
|
||||||
return this.isPackage ? s.startsWith(this.name) : s.equals(this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private final PatternFormatter[] defaultFormatters;
|
|
||||||
private final List<LoggerNameSelector> formatters = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new {@link LoggerNamePatternSelector}.
|
|
||||||
*
|
|
||||||
* @param defaultPattern The default pattern to use if no logger name matches
|
|
||||||
* @param properties The pattern match rules to use
|
|
||||||
* @param alwaysWriteExceptions Write exceptions even if pattern does not
|
|
||||||
* include exception conversion
|
|
||||||
* @param disableAnsi If true, disable all ANSI escape codes
|
|
||||||
* @param noConsoleNoAnsi If true and {@link System#console()} is null,
|
|
||||||
* disable ANSI escape codes
|
|
||||||
* @param config The configuration
|
|
||||||
*/
|
|
||||||
protected LoggerNamePatternSelector(String defaultPattern, PatternMatch[] properties,
|
|
||||||
boolean alwaysWriteExceptions, boolean disableAnsi, boolean noConsoleNoAnsi, Configuration config)
|
|
||||||
{
|
|
||||||
PatternParser parser = PatternLayout.createPatternParser(config);
|
|
||||||
this.defaultFormatters = toArray(parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi));
|
|
||||||
for (PatternMatch property : properties)
|
|
||||||
{
|
|
||||||
PatternFormatter[] formatters = toArray(parser.parse(property.getPattern(), alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi));
|
|
||||||
for (String name : property.getKey().split(","))
|
|
||||||
{
|
|
||||||
this.formatters.add(new LoggerNameSelector(name, formatters));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PatternFormatter[] toArray(List<PatternFormatter> formatters)
|
|
||||||
{
|
|
||||||
return formatters.toArray(new PatternFormatter[formatters.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PatternFormatter[] getFormatters(LogEvent event)
|
|
||||||
{
|
|
||||||
final String loggerName = event.getLoggerName();
|
|
||||||
if (loggerName != null)
|
|
||||||
{
|
|
||||||
//noinspection ForLoopReplaceableByForEach
|
|
||||||
for (int i = 0; i < this.formatters.size(); i++)
|
|
||||||
{
|
|
||||||
LoggerNameSelector selector = this.formatters.get(i);
|
|
||||||
if (selector.test(loggerName))
|
|
||||||
{
|
|
||||||
return selector.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.defaultFormatters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link LoggerNamePatternSelector}.
|
|
||||||
*
|
|
||||||
* @param defaultPattern The default pattern to use if no logger name matches
|
|
||||||
* @param properties The pattern match rules to use
|
|
||||||
* @param alwaysWriteExceptions Write exceptions even if pattern does not
|
|
||||||
* include exception conversion
|
|
||||||
* @param disableAnsi If true, disable all ANSI escape codes
|
|
||||||
* @param noConsoleNoAnsi If true and {@link System#console()} is null,
|
|
||||||
* disable ANSI escape codes
|
|
||||||
* @param config The configuration
|
|
||||||
* @return The new pattern selector
|
|
||||||
*/
|
|
||||||
@PluginFactory
|
|
||||||
public static LoggerNamePatternSelector createSelector(
|
|
||||||
@Required(message = "Default pattern is required") @PluginAttribute(value = "defaultPattern") String defaultPattern,
|
|
||||||
@PluginElement("PatternMatch") PatternMatch[] properties,
|
|
||||||
@PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) boolean alwaysWriteExceptions,
|
|
||||||
@PluginAttribute("disableAnsi") boolean disableAnsi,
|
|
||||||
@PluginAttribute("noConsoleNoAnsi") boolean noConsoleNoAnsi,
|
|
||||||
@PluginConfiguration Configuration config)
|
|
||||||
{
|
|
||||||
return new LoggerNamePatternSelector(defaultPattern, properties, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
@MethodsReturnNonnullByDefault
|
|
||||||
package net.minecraftforge.server.terminalconsole.util;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
|
|
||||||
import mcp.MethodsReturnNonnullByDefault;
|
|
|
@ -1,42 +1,33 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Configuration status="warn" packages="net.minecraftforge.server.terminalconsole.util">
|
<Configuration status="warn" packages="net.minecrell.terminalconsole,net.minecrell.terminalconsole.util,com.mojang.util">
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<Console name="SysOut" target="SYSTEM_OUT">
|
<TerminalConsole name="Console">
|
||||||
<PatternLayout>
|
<PatternLayout>
|
||||||
<LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss.SSS}] [%t/%level] [%c{2.}/%markerSimpleName]: %msg%n">
|
<LoggerNamePatternSelector defaultPattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [%c{2.}/%markerSimpleName]: %minecraftFormatting{%msg}%n%xEx}" disableAnsi="${sys:forge.logging.noansi:-true}">
|
||||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
||||||
<PatternMatch key="net.minecraft." pattern="[%d{HH:mm:ss.SSS}] [%t/%level] [minecraft/%logger{1}]: %msg%n"/>
|
<PatternMatch key="net.minecraft." pattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg}%n%xEx}"/>
|
||||||
<PatternMatch key="com.mojang." pattern="[%d{HH:mm:ss.SSS}] [%t/%level] [mojang/%logger{1}]: %msg%n"/>
|
<PatternMatch key="com.mojang." pattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}%n%xEx}"/>
|
||||||
</LoggerNamePatternSelector>
|
</LoggerNamePatternSelector>
|
||||||
</PatternLayout>
|
</PatternLayout>
|
||||||
</Console>
|
</TerminalConsole>
|
||||||
<Console name="SysOutColour" target="SYSTEM_OUT">
|
|
||||||
<PatternLayout>
|
|
||||||
<LoggerNamePatternSelector defaultPattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [%c{2.}/%markerSimpleName]: %msg%n}">
|
|
||||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
|
||||||
<PatternMatch key="net.minecraft." pattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [minecraft/%logger{1}]: %msg%n}"/>
|
|
||||||
<PatternMatch key="com.mojang." pattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [mojang/%logger{1}]: %msg%n}"/>
|
|
||||||
</LoggerNamePatternSelector>
|
|
||||||
</PatternLayout>
|
|
||||||
</Console>
|
|
||||||
<Queue name="ServerGuiConsole" ignoreExceptions="true">
|
<Queue name="ServerGuiConsole" ignoreExceptions="true">
|
||||||
<PatternLayout>
|
<PatternLayout>
|
||||||
<LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss.SSS}] [%t/%level] [%c{2.}/%markerSimpleName]: %msg%n">
|
<LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss.SSS}] [%t/%level] [%c{2.}/%markerSimpleName]: %minecraftFormatting{%msg}{strip}">
|
||||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
||||||
<PatternMatch key="net.minecraft." pattern="[%d{HH:mm:ss.SSS}] [%t/%level] [minecraft/%logger{1}]: %msg%n"/>
|
<PatternMatch key="net.minecraft." pattern="[%d{HH:mm:ss.SSS}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg}{strip}"/>
|
||||||
<PatternMatch key="com.mojang." pattern="[%d{HH:mm:ss.SSS}] [%t/%level] [mojang/%logger{1}]: %msg%n"/>
|
<PatternMatch key="com.mojang." pattern="[%d{HH:mm:ss.SSS}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}{strip}"/>
|
||||||
</LoggerNamePatternSelector>
|
</LoggerNamePatternSelector>
|
||||||
</PatternLayout>
|
</PatternLayout>
|
||||||
</Queue>
|
</Queue>
|
||||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||||
<PatternLayout pattern="[%d{ddMMMyyyy HH:mm:ss.SSS}] [%t/%level] [%logger/%markerSimpleName]: %msg%n"/>
|
<PatternLayout pattern="[%d{ddMMMyyyy HH:mm:ss.SSS}] [%t/%level] [%logger/%markerSimpleName]: %minecraftFormatting{%msg}{strip}%n%xEx"/>
|
||||||
<Policies>
|
<Policies>
|
||||||
<TimeBasedTriggeringPolicy/>
|
<TimeBasedTriggeringPolicy/>
|
||||||
<OnStartupTriggeringPolicy/>
|
<OnStartupTriggeringPolicy/>
|
||||||
</Policies>
|
</Policies>
|
||||||
</RollingRandomAccessFile>
|
</RollingRandomAccessFile>
|
||||||
<RollingRandomAccessFile name="DebugFile" fileName="logs/debug.log" filePattern="logs/debug-%i.log.gz">
|
<RollingRandomAccessFile name="DebugFile" fileName="logs/debug.log" filePattern="logs/debug-%i.log.gz">
|
||||||
<PatternLayout pattern="[%d{ddMMMyyyy HH:mm:ss.SSS}] [%t/%level] [%logger/%markerSimpleName]: %msg%n"/>
|
<PatternLayout pattern="[%d{ddMMMyyyy HH:mm:ss.SSS}] [%t/%level] [%logger/%markerSimpleName]: %minecraftFormatting{%msg}{strip}%n%xEx"/>
|
||||||
<Policies>
|
<Policies>
|
||||||
<OnStartupTriggeringPolicy/>
|
<OnStartupTriggeringPolicy/>
|
||||||
<SizeBasedTriggeringPolicy size="200MB"/>
|
<SizeBasedTriggeringPolicy size="200MB"/>
|
||||||
|
@ -64,8 +55,7 @@
|
||||||
<MarkerFilter marker="LOADING" onMatch="${sys:forge.logging.marker.loading:-ACCEPT}" onMismatch="NEUTRAL"/>
|
<MarkerFilter marker="LOADING" onMatch="${sys:forge.logging.marker.loading:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||||
<MarkerFilter marker="CORE" onMatch="${sys:forge.logging.marker.core:-ACCEPT}" onMismatch="NEUTRAL"/>
|
<MarkerFilter marker="CORE" onMatch="${sys:forge.logging.marker.core:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||||
</filters>
|
</filters>
|
||||||
<AppenderRef ref="SysOut" level="${sys:forge.logging.console.level:-info}"/>
|
<AppenderRef ref="Console" level="${sys:forge.logging.console.level:-info}"/>
|
||||||
<AppenderRef ref="SysOutColour" level="${sys:forge.logging.colourconsole.level:-off}"/>
|
|
||||||
<AppenderRef ref="ServerGuiConsole" level="${sys:forge.logging.console.level:-info}"/>
|
<AppenderRef ref="ServerGuiConsole" level="${sys:forge.logging.console.level:-info}"/>
|
||||||
<AppenderRef ref="File" level="${sys:forge.logging.file.level:-info}"/>
|
<AppenderRef ref="File" level="${sys:forge.logging.file.level:-info}"/>
|
||||||
<AppenderRef ref="DebugFile" level="${sys:forge.logging.debugFile.level:-trace}"/>
|
<AppenderRef ref="DebugFile" level="${sys:forge.logging.debugFile.level:-trace}"/>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Configuration status="warn" packages="net.minecraftforge.server.terminalconsole,com.mojang.util">
|
<Configuration status="warn" packages="net.minecrell.terminalconsole,net.minecrell.terminalconsole.util,com.mojang.util">
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<TerminalConsole name="Console">
|
<TerminalConsole name="Console">
|
||||||
<PatternLayout>
|
<PatternLayout>
|
||||||
<LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %minecraftFormatting{%msg}%n%xEx">
|
<LoggerNamePatternSelector defaultPattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [%c{2.}/%markerSimpleName]: %minecraftFormatting{%msg}%n%xEx}" disableAnsi="${sys:forge.logging.noansi:-true}">
|
||||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
||||||
<PatternMatch key="net.minecraft." pattern="[%d{HH:mm:ss}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg}%n%xEx"/>
|
<PatternMatch key="net.minecraft." pattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg}%n%xEx}"/>
|
||||||
<PatternMatch key="com.mojang." pattern="[%d{HH:mm:ss}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}%n%xEx"/>
|
<PatternMatch key="com.mojang." pattern="%highlight{[%d{HH:mm:ss.SSS}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}%n%xEx}"/>
|
||||||
</LoggerNamePatternSelector>
|
</LoggerNamePatternSelector>
|
||||||
</PatternLayout>
|
</PatternLayout>
|
||||||
</TerminalConsole>
|
</TerminalConsole>
|
||||||
|
@ -41,7 +41,19 @@
|
||||||
<Logger level="${sys:forge.logging.mojang.level:-info}" name="net.minecraft"/>
|
<Logger level="${sys:forge.logging.mojang.level:-info}" name="net.minecraft"/>
|
||||||
<Root level="all">
|
<Root level="all">
|
||||||
<filters>
|
<filters>
|
||||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
|
<MarkerFilter marker="NETWORK_PACKETS" onMatch="${sys:forge.logging.marker.networking:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="CLASSLOADING" onMatch="${sys:forge.logging.marker.classloading:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="LAUNCHPLUGIN" onMatch="${sys:forge.logging.marker.launchplugin:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="AXFORM" onMatch="${sys:forge.logging.marker.axform:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="EVENTBUS" onMatch="${sys:forge.logging.marker.eventbus:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="DISTXFORM" onMatch="${sys:forge.logging.marker.distxform:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="SCAN" onMatch="${sys:forge.logging.marker.scan:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="REGISTRIES" onMatch="${sys:forge.logging.marker.registries:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="REGISTRYDUMP" onMatch="${sys:forge.logging.marker.registrydump:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="SPLASH" onMatch="${sys:forge.logging.marker.splash:-DENY}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="FORGEMOD" onMatch="${sys:forge.logging.marker.forgemod:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="LOADING" onMatch="${sys:forge.logging.marker.loading:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||||
|
<MarkerFilter marker="CORE" onMatch="${sys:forge.logging.marker.core:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||||
</filters>
|
</filters>
|
||||||
<AppenderRef ref="Console" level="${sys:forge.logging.console.level:-info}"/>
|
<AppenderRef ref="Console" level="${sys:forge.logging.console.level:-info}"/>
|
||||||
<AppenderRef ref="ServerGuiConsole" level="${sys:forge.logging.console.level:-info}"/>
|
<AppenderRef ref="ServerGuiConsole" level="${sys:forge.logging.console.level:-info}"/>
|
||||||
|
|
Loading…
Reference in a new issue