From c7f4e1df6a3dbbfb035ff331c4c1ebb0fc998330 Mon Sep 17 00:00:00 2001 From: Hileb <107909747+Ecdcaeb@users.noreply.github.com> Date: Wed, 1 Jan 2025 14:11:57 +0800 Subject: [PATCH] update net.minecrell:terminalconsoleappender update net.minecrell:terminalconsoleappender --- .../HighlightErrorConverter.java | 11 +- .../MinecraftFormattingConverter.java | 60 +++--- .../SimpleTerminalConsole.java | 162 ++++++++++++++ .../server/terminalconsole/TCALookup.java | 40 ++++ .../TerminalConsoleAppender.java | 197 ++++++++---------- .../util/LoggerNamePatternSelector.java | 24 +-- 6 files changed, 343 insertions(+), 151 deletions(-) create mode 100644 src/main/java/net/minecraftforge/server/terminalconsole/SimpleTerminalConsole.java create mode 100644 src/main/java/net/minecraftforge/server/terminalconsole/TCALookup.java diff --git a/src/main/java/net/minecraftforge/server/terminalconsole/HighlightErrorConverter.java b/src/main/java/net/minecraftforge/server/terminalconsole/HighlightErrorConverter.java index c42662394..fd3eacdfc 100644 --- a/src/main/java/net/minecraftforge/server/terminalconsole/HighlightErrorConverter.java +++ b/src/main/java/net/minecraftforge/server/terminalconsole/HighlightErrorConverter.java @@ -38,7 +38,7 @@ /** * A simplified version of {@link HighlightConverter} that uses - * {@link TerminalConsoleAppender} to detect if Ansi escape codes can be used + * {@link TerminalConsoleAppender} to detect if ANSI escape codes can be used * to highlight errors and warnings in the console. * *
If configured, it will mark all logged errors with a red color and all
@@ -55,9 +55,9 @@
@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 static final String ANSI_RESET = "\u001B[m";
+ private static final String ANSI_ERROR = "\u001B[31;1m"; // Bold Red
+ private static final String ANSI_WARN = "\u001B[33;1m"; // Bold Yellow
private final List Usage: Extend this class and implement the abstract
+ * methods for your application. Consider overriding
+ * {@link #buildReader(LineReaderBuilder)} to add further features to the
+ * console (e.g. call {@link LineReaderBuilder#completer(Completer)} with
+ * a custom completer to provide command completion). This is called if the application receives SIGINT while reading input,
+ * e.g. when pressing CTRL+C on most terminal implementations. The default implementation trims leading and trailing whitespace
+ * from the input and skips execution if the command is empty. Override this method to make further changes, (e.g. call
+ * {@link LineReaderBuilder#appName(String)} or
+ * {@link LineReaderBuilder#completer(Completer)}). The default implementation sets some opinionated default options,
+ * which are considered to be appropriate for most applications: Note that this method won't return until one of the following
+ * conditions are met: Example usage:
+ * {@code
+ *
+ *
+ * @param builder The builder to configure
+ * @return The built line reader
+ */
+ protected LineReader buildReader(LineReaderBuilder builder) {
+ LineReader reader = builder.build();
+ reader.setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION);
+ reader.unsetOpt(LineReader.Option.INSERT_TAB);
+ return reader;
+ }
+
+ /**
+ * Start reading commands from the console.
+ *
+ *
+ *
+ */
+ public void start() {
+ try {
+ final @Nullable Terminal terminal = TerminalConsoleAppender.getTerminal();
+ if (terminal != null) {
+ readCommands(terminal);
+ } else {
+ readCommands(System.in);
+ }
+ } catch (IOException e) {
+ LogManager.getLogger("TerminalConsole").error("Failed to read console input", e);
+ }
+ }
+
+ private void readCommands(Terminal terminal) {
+ LineReader reader = buildReader(LineReaderBuilder.builder().terminal(terminal));
+ TerminalConsoleAppender.setReader(reader);
+
+ try {
+ String line;
+ while (isRunning()) {
+ try {
+ line = reader.readLine("> ");
+ } catch (EndOfFileException ignored) {
+ // Continue reading after EOT
+ continue;
+ }
+
+ if (line == null) {
+ break;
+ }
+
+ processInput(line);
+ }
+ } catch (UserInterruptException e) {
+ shutdown();
+ } finally {
+ TerminalConsoleAppender.setReader(null);
+ }
+ }
+
+ private void readCommands(InputStream in) throws IOException {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
+ String line;
+ while (isRunning() && (line = reader.readLine()) != null) {
+ processInput(line);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/minecraftforge/server/terminalconsole/TCALookup.java b/src/main/java/net/minecraftforge/server/terminalconsole/TCALookup.java
new file mode 100644
index 000000000..6cf951557
--- /dev/null
+++ b/src/main/java/net/minecraftforge/server/terminalconsole/TCALookup.java
@@ -0,0 +1,40 @@
+package net.minecraftforge.server.terminalconsole;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.lookup.AbstractLookup;
+import org.apache.logging.log4j.core.lookup.StrLookup;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * A {@link StrLookup} that returns properties specific to
+ * {@link TerminalConsoleAppender}. The following properties are supported:
+ *
+ *
+ *
+ */
+@Plugin(name = "tca", category = StrLookup.CATEGORY)
+public final class TCALookup extends AbstractLookup {
+
+ /**
+ * Lookup key that returns if ANSI colors are unsupported/disabled.
+ */
+ public final static String KEY_DISABLE_ANSI = "disableAnsi";
+
+ @Override
+ @Nullable
+ public String lookup(LogEvent event, String key) {
+ if (KEY_DISABLE_ANSI.equals(key)) {
+ return String.valueOf(!TerminalConsoleAppender.isAnsiSupported());
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/net/minecraftforge/server/terminalconsole/TerminalConsoleAppender.java b/src/main/java/net/minecraftforge/server/terminalconsole/TerminalConsoleAppender.java
index edb854db5..5bf8adc69 100644
--- a/src/main/java/net/minecraftforge/server/terminalconsole/TerminalConsoleAppender.java
+++ b/src/main/java/net/minecraftforge/server/terminalconsole/TerminalConsoleAppender.java
@@ -30,10 +30,8 @@
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.Property;
+import org.apache.logging.log4j.core.config.plugins.*;
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;
@@ -51,25 +49,18 @@
* persistent input line, as well as command history and command completion.
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.
+ * appender in your log4j configuration. * *Example usage:
*{@code+ * } * ** - * - *- * }
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.
+ * returned by {@link #getTerminal()}. * *By default, the JLine {@link Terminal} is enabled when the application * is started with an attached terminal session. Usually, this is only the @@ -82,9 +73,9 @@ *
* *+ * *
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.
@@ -113,7 +107,7 @@ public class TerminalConsoleAppender extends AbstractAppender * 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. - *+ * *
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.
@@ -129,23 +123,19 @@ public class TerminalConsoleAppender extends AbstractAppender private static final PrintStream stdout = System.out; private static boolean initialized; - @Nullable - private static Terminal terminal; - @Nullable - private static LineReader reader; + private static @Nullable Terminal terminal; + private static @Nullable 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 + * overridden using the {@link #JLINE_OVERRIDE_PROPERTY} system * property. * * @return The terminal, or null if not supported * @see TerminalConsoleAppender */ - @Nullable - public static Terminal getTerminal() - { + public synchronized static @Nullable Terminal getTerminal() { return terminal; } @@ -156,9 +146,7 @@ public static Terminal getTerminal() * * @return The current line reader, or null if none */ - @Nullable - public static LineReader getReader() - { + public synchronized static @Nullable LineReader getReader() { return reader; } @@ -172,8 +160,7 @@ public static LineReader getReader() * * @param newReader The new line reader */ - public static void setReader(@Nullable LineReader newReader) - { + public synchronized static void setReader(@Nullable LineReader newReader) { if (newReader != null && newReader.getTerminal() != terminal) { throw new IllegalArgumentException("Reader was not created with TerminalConsoleAppender.getTerminal()"); @@ -188,39 +175,43 @@ public static void setReader(@Nullable LineReader newReader) * *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.
+ * the {@link #ANSI_OVERRIDE_PROPERTY} system property. * * @return true if ANSI escapes codes should be written to the console */ public static boolean isAnsiSupported() { + if (!initialized) + initializeTerminal(); 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 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 + * appending events are logged, otherwise they are propagated to the + * caller + * @param properties Optional properties */ - protected TerminalConsoleAppender(String name, @Nullable Filter filter, Layout extends Serializable> layout, boolean ignoreExceptions) - { - super(name, filter, layout, ignoreExceptions); - initializeTerminal(); + protected TerminalConsoleAppender(String name, Filter filter, Layout extends Serializable> layout, + boolean ignoreExceptions, Property[] properties) { + super(name, filter, layout, ignoreExceptions, properties); + if (!initialized) + initializeTerminal(); } - private static void initializeTerminal() + private synchronized static void initializeTerminal() { if (!initialized) { initialized = true; // A system property can be used to override our automatic detection - Boolean jlineOverride = getOptionalBooleanProperty(JLINE_OVERRIDE_PROPERTY); + @Nullable 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 @@ -238,8 +229,8 @@ private static void initializeTerminal() if (jlineOverride != Boolean.FALSE) { - try - { + try { + terminal = TerminalBuilder.builder().dumb(dumb).build(); } catch (IllegalStateException e) @@ -252,68 +243,45 @@ private static void initializeTerminal() if (LOGGER.isDebugEnabled()) { // Log with stacktrace - LOGGER.warn("Disabling terminal, you're running in an unsupported environment.", e); + LOGGER.warn("Advanced terminal features are not available in this environment", e); } else { - LOGGER.warn("Disabling terminal, you're running in an unsupported environment."); + LOGGER.warn("Advanced terminal features are not available in this environment"); } } catch (IOException e) { - LOGGER.error("Failed to initialize terminal. Falling back to standard output", e); + LOGGER.error("Failed to initialize terminal. Falling back to standard console", e); } } } } - @Nullable - private static Boolean getOptionalBooleanProperty(String name) + @Override + public void append(LogEvent event) { - 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; - } + print(getLayout().toSerializable(event).toString()); } - @Override - public void append(LogEvent event) + private synchronized static void print(String text) { 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); + reader.printAbove(text); } else { - terminal.writer().print(getLayout().toSerializable(event)); + terminal.writer().print(text); + terminal.writer().flush(); } - - terminal.writer().flush(); } else { - stdout.print(getLayout().toSerializable(event)); + stdout.print(text); } } @@ -323,19 +291,15 @@ public void append(LogEvent event) * * @throws IOException If an I/O error occurs */ - public static void close() throws IOException + public synchronized static void close() throws IOException { - if (initialized) - { + if (initialized) { initialized = false; - if (terminal != null) - { - try - { + reader = null; + if (terminal != null) { + try { terminal.close(); - } - finally - { + } finally { terminal = null; } } @@ -343,28 +307,49 @@ public static void close() throws IOException } /** - * Creates a new {@link TerminalConsoleAppender}. + * Creates a new {@link Builder} for {@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 + * @param The type to build + * @return The new builder */ - @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) + @PluginBuilderFactory + public static > B newBuilder() { - if (layout == null) + return new Builder().asBuilder(); + } + + /** + * Builds {@link TerminalConsoleAppender} instances. + * + * @param The type to build + */ + public static class Builder> extends AbstractAppender.Builder + implements org.apache.logging.log4j.core.util.Builder