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 formatters; @@ -142,8 +142,7 @@ public boolean handlesThrowable() * @param options The pattern options * @return The new instance */ - @Nullable - public static HighlightErrorConverter newInstance(Configuration config, String[] options) + public static @Nullable HighlightErrorConverter newInstance(Configuration config, String[] options) { if (options.length != 1) { diff --git a/src/main/java/net/minecraftforge/server/terminalconsole/MinecraftFormattingConverter.java b/src/main/java/net/minecraftforge/server/terminalconsole/MinecraftFormattingConverter.java index aff7184e2..2d85d42d9 100644 --- a/src/main/java/net/minecraftforge/server/terminalconsole/MinecraftFormattingConverter.java +++ b/src/main/java/net/minecraftforge/server/terminalconsole/MinecraftFormattingConverter.java @@ -56,10 +56,16 @@ * * @see * Formatting Codes + * @deprecated Minecraft-specific. Also, legacy formatting codes are deprecated + * and do not natively allow specifying RGB colors. Consider implementing + * native support for the (JSON) chat components of your platform instead. + * For more information, see + * issue #18. */ @Plugin(name = "minecraftFormatting", category = PatternConverter.CATEGORY) @ConverterKeys({ "minecraftFormatting" }) @PerformanceSensitive("allocation") +@Deprecated public class MinecraftFormattingConverter extends LogEventPatternConverter { @@ -77,34 +83,34 @@ public class MinecraftFormattingConverter extends LogEventPatternConverter private static final boolean KEEP_FORMATTING = PropertiesUtil.getProperties().getBooleanProperty(KEEP_FORMATTING_PROPERTY); - private static final String ANSI_RESET = "\u001B[m"; + static final String ANSI_RESET = "\u001B[m"; - private static final char COLOR_CHAR = '\u00A7'; // § + private static final char COLOR_CHAR = '§'; private static final String LOOKUP = "0123456789abcdefklmnor"; private static final String[] ansiCodes = new String[] { - "\u001B[0;30m", // Black §0 - "\u001B[0;34m", // Dark Blue §1 - "\u001B[0;32m", // Dark Green §2 - "\u001B[0;36m", // Dark Aqua §3 - "\u001B[0;31m", // Dark Red §4 - "\u001B[0;35m", // Dark Purple §5 - "\u001B[0;33m", // Gold §6 - "\u001B[0;37m", // 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 + "\u001B[0;30m", // Black §0 + "\u001B[0;34m", // Dark Blue §1 + "\u001B[0;32m", // Dark Green §2 + "\u001B[0;36m", // Dark Aqua §3 + "\u001B[0;31m", // Dark Red §4 + "\u001B[0;35m", // Dark Purple §5 + "\u001B[0;33m", // Gold §6 + "\u001B[0;37m", // 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; @@ -143,7 +149,7 @@ public void format(LogEvent event, StringBuilder toAppendTo) format(content, toAppendTo, start, ansi && TerminalConsoleAppender.isAnsiSupported()); } - private static void format(String s, StringBuilder result, int start, boolean ansi) + static void format(String s, StringBuilder result, int start, boolean ansi) { int next = s.indexOf(COLOR_CHAR); int last = s.length() - 1; @@ -195,8 +201,7 @@ private static void format(String s, StringBuilder result, int start, boolean an * * @see MinecraftFormattingConverter */ - @Nullable - public static MinecraftFormattingConverter newInstance(Configuration config, String[] options) + public static @Nullable MinecraftFormattingConverter newInstance(Configuration config, String[] options) { if (options.length < 1 || options.length > 2) { @@ -214,4 +219,5 @@ public static MinecraftFormattingConverter newInstance(Configuration config, Str boolean strip = options.length > 1 && "strip".equals(options[1]); return new MinecraftFormattingConverter(formatters, strip); } + } diff --git a/src/main/java/net/minecraftforge/server/terminalconsole/SimpleTerminalConsole.java b/src/main/java/net/minecraftforge/server/terminalconsole/SimpleTerminalConsole.java new file mode 100644 index 000000000..e33ccc228 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/terminalconsole/SimpleTerminalConsole.java @@ -0,0 +1,162 @@ +package net.minecraftforge.server.terminalconsole; + +import org.apache.logging.log4j.LogManager; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jline.reader.Completer; +import org.jline.reader.EndOfFileException; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.UserInterruptException; +import org.jline.terminal.Terminal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * A simple, optional base implementation of a basic console input command + * reader using {@link TerminalConsoleAppender}. Once started, it displays + * a command prompt ("{@code > }") and reads input commands from the console. + * + *

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).

+ */ +public abstract class SimpleTerminalConsole { + + /** + * Determines if the application is still running and accepting input. + * + * @return {@code true} to continue reading input + */ + protected abstract boolean isRunning(); + + /** + * Run a command entered in the console. + * + * @param command The command line to run + */ + protected abstract void runCommand(String command); + + /** + * Shutdown the application and perform a clean exit. + * + *

This is called if the application receives SIGINT while reading input, + * e.g. when pressing CTRL+C on most terminal implementations.

+ */ + protected abstract void shutdown(); + + /** + * Process an input line entered through the console. + * + *

The default implementation trims leading and trailing whitespace + * from the input and skips execution if the command is empty.

+ * + * @param input The input line + */ + protected void processInput(String input) { + String command = input.trim(); + if (!command.isEmpty()) { + runCommand(command); + } + } + + /** + * Configures the {@link LineReaderBuilder} and {@link LineReader} with + * additional options. + * + *

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:

+ * + * + * + * @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. + * + *

Note that this method won't return until one of the following + * conditions are met:

+ * + * + */ + 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 @@ *

* * @@ -92,6 +83,9 @@ @Plugin(name = TerminalConsoleAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) public class TerminalConsoleAppender extends AbstractAppender { + /** + * The name of the appender in the configuration. + */ public static final String PLUGIN_NAME = "TerminalConsole"; /** * The prefix used for all system properties in TerminalConsoleAppender. @@ -101,7 +95,7 @@ public class TerminalConsoleAppender extends AbstractAppender * 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. - *

+ * *

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 layout, boolean ignoreExceptions) - { - super(name, filter, layout, ignoreExceptions); - initializeTerminal(); + protected TerminalConsoleAppender(String name, Filter filter, Layout 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 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 + { + + @Override + public TerminalConsoleAppender build() { - layout = PatternLayout.createDefaultLayout(); + return new TerminalConsoleAppender(getName(), getFilter(), getOrCreateLayout(), + isIgnoreExceptions(), getPropertyArray()); + } + } + + private static @Nullable Boolean getOptionalBooleanProperty(String name) + { + String value = PropertiesUtil.getProperties().getStringProperty(name); + if (value == null) { + return null; } - return new TerminalConsoleAppender(name, filter, layout, ignoreExceptions); + 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; + } } + } diff --git a/src/main/java/net/minecraftforge/server/terminalconsole/util/LoggerNamePatternSelector.java b/src/main/java/net/minecraftforge/server/terminalconsole/util/LoggerNamePatternSelector.java index 9d0c6c12e..331b08ac2 100644 --- a/src/main/java/net/minecraftforge/server/terminalconsole/util/LoggerNamePatternSelector.java +++ b/src/main/java/net/minecraftforge/server/terminalconsole/util/LoggerNamePatternSelector.java @@ -19,6 +19,7 @@ package net.minecraftforge.server.terminalconsole.util; +import jakarta.annotation.Nullable; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Node; @@ -38,6 +39,8 @@ import java.util.ArrayList; import java.util.List; +// TODO: Consider moving this into a separate project + /** * A {@link PatternSelector} that selects patterns based on the logger name. * Can be used to log messages from different loggers using different patterns. @@ -61,6 +64,7 @@ public class LoggerNamePatternSelector implements PatternSelector { private static class LoggerNameSelector { + private final String name; private final boolean isPackage; private final PatternFormatter[] formatters; @@ -103,26 +107,22 @@ protected LoggerNamePatternSelector(String defaultPattern, PatternMatch[] proper 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(",")) - { + PatternFormatter[] emptyFormatters = new PatternFormatter[0]; + this.defaultFormatters = parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi) + .toArray(emptyFormatters); + for (PatternMatch property : properties) { + PatternFormatter[] formatters = parser.parse(property.getPattern(), alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi) + .toArray(emptyFormatters); + for (String name : property.getKey().split(",")) { this.formatters.add(new LoggerNameSelector(name, formatters)); } } } - private static PatternFormatter[] toArray(List formatters) - { - return formatters.toArray(new PatternFormatter[formatters.size()]); - } - @Override public PatternFormatter[] getFormatters(LogEvent event) { - final String loggerName = event.getLoggerName(); + final @Nullable String loggerName = event.getLoggerName(); if (loggerName != null) { //noinspection ForLoopReplaceableByForEach