Repository: logging-log4j2 Updated Branches: refs/heads/master 0699478ba -> ea7b42f85
[LOG4J2-1685] Single property to disable all color output. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/ea7b42f8 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/ea7b42f8 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/ea7b42f8 Branch: refs/heads/master Commit: ea7b42f858c93e922016ac615c9779849b62ce05 Parents: 0699478 Author: Raman Gupta <[email protected]> Authored: Sat Nov 12 17:25:33 2016 -0800 Committer: Gary Gregory <[email protected]> Committed: Sat Nov 12 17:25:33 2016 -0800 ---------------------------------------------------------------------- .../core/appender/rolling/PatternProcessor.java | 2 +- .../core/layout/MarkerPatternSelector.java | 34 ++++-- .../log4j/core/layout/PatternLayout.java | 104 ++++++++++++++++--- .../log4j/core/layout/Rfc5424Layout.java | 4 +- .../core/layout/ScriptPatternSelector.java | 31 ++++-- .../log4j/core/pattern/HighlightConverter.java | 15 ++- .../log4j/core/pattern/PatternParser.java | 65 +++++++++--- .../log4j/core/pattern/StyleConverter.java | 8 +- .../log4j/core/pattern/DisableAnsiTest.java | 59 +++++++++++ .../core/pattern/HighlightConverterTest.java | 8 +- .../log4j/core/pattern/PatternParserTest2.java | 2 +- .../resources/log4j2-console-disableAnsi.xml | 34 ++++++ src/changes/changes.xml | 3 + src/site/xdoc/manual/layouts.xml.vm | 5 + 14 files changed, 321 insertions(+), 53 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/PatternProcessor.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/PatternProcessor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/PatternProcessor.java index ca13769..6cfec53 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/PatternProcessor.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/PatternProcessor.java @@ -78,7 +78,7 @@ public class PatternProcessor { final PatternParser parser = createPatternParser(); final List<PatternConverter> converters = new ArrayList<>(); final List<FormattingInfo> fields = new ArrayList<>(); - parser.parse(pattern, converters, fields, false, false); + parser.parse(pattern, converters, fields, false, false, false); final FormattingInfo[] infoArray = new FormattingInfo[fields.size()]; patternFields = fields.toArray(infoArray); final ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()]; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java index dc9a93f..2aa86d1 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java @@ -56,10 +56,13 @@ public class MarkerPatternSelector implements PatternSelector { @PluginBuilderAttribute(value = "alwaysWriteExceptions") private boolean alwaysWriteExceptions = true; - @PluginBuilderAttribute(value = "noConsoleNoAnsi") - private boolean noConsoleNoAnsi; + @PluginBuilderAttribute(value = "disableAnsi") + private boolean disableAnsi; - @PluginConfiguration + @PluginBuilderAttribute(value = "noConsoleNoAnsi") + private boolean noConsoleNoAnsi; + + @PluginConfiguration private Configuration configuration; @Override @@ -71,8 +74,8 @@ public class MarkerPatternSelector implements PatternSelector { LOGGER.warn("No marker patterns were provided with PatternMatch"); return null; } - return new MarkerPatternSelector(properties, defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi, - configuration); + return new MarkerPatternSelector(properties, defaultPattern, alwaysWriteExceptions, disableAnsi, + noConsoleNoAnsi, configuration); } public Builder withProperties(final PatternMatch[] properties) { @@ -90,6 +93,11 @@ public class MarkerPatternSelector implements PatternSelector { return this; } + public Builder withDisableAnsi(final boolean disableAnsi) { + this.disableAnsi = disableAnsi; + return this; + } + public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { this.noConsoleNoAnsi = noConsoleNoAnsi; return this; @@ -113,13 +121,24 @@ public class MarkerPatternSelector implements PatternSelector { private static Logger LOGGER = StatusLogger.getLogger(); + /** + * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version. + */ + @Deprecated public MarkerPatternSelector(final PatternMatch[] properties, final String defaultPattern, final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi, final Configuration config) { + this(properties, defaultPattern, alwaysWriteExceptions, false, noConsoleNoAnsi, config); + } + + private MarkerPatternSelector(final PatternMatch[] properties, final String defaultPattern, + final boolean alwaysWriteExceptions, final boolean disableAnsi, + final boolean noConsoleNoAnsi, final Configuration config) { final PatternParser parser = PatternLayout.createPatternParser(config); for (final PatternMatch property : properties) { try { - final List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, noConsoleNoAnsi); + final List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, + disableAnsi, noConsoleNoAnsi); formatterMap.put(property.getKey(), list.toArray(new PatternFormatter[list.size()])); patternMap.put(property.getKey(), property.getPattern()); } catch (final RuntimeException ex) { @@ -127,7 +146,8 @@ public class MarkerPatternSelector implements PatternSelector { } } try { - final List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi); + final List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, + noConsoleNoAnsi); defaultFormatters = list.toArray(new PatternFormatter[list.size()]); this.defaultPattern = defaultPattern; } catch (final RuntimeException ex) { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java index 183c29d..25027bf 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java @@ -93,6 +93,8 @@ public final class PatternLayout extends AbstractStringLayout { * @param charset The character set. * @param alwaysWriteExceptions Whether or not exceptions should always be handled in this pattern (if {@code true}, * exceptions will be written even if the pattern does not specify so). + * @param disableAnsi + * If {@code "true"}, do not output ANSI escape codes * @param noConsoleNoAnsi * If {@code "true"} (default) and {@link System#console()} is null, do not output ANSI escape codes * @param headerPattern header conversion pattern. @@ -100,16 +102,39 @@ public final class PatternLayout extends AbstractStringLayout { */ private PatternLayout(final Configuration config, final RegexReplacement replace, final String eventPattern, final PatternSelector patternSelector, final Charset charset, final boolean alwaysWriteExceptions, - final boolean noConsoleNoAnsi, final String headerPattern, final String footerPattern) { + final boolean disableAnsi, final boolean noConsoleNoAnsi, final String headerPattern, + final String footerPattern) { super(config, charset, - createSerializer(config, replace, headerPattern, null, patternSelector, alwaysWriteExceptions, - noConsoleNoAnsi), - createSerializer(config, replace, footerPattern, null, patternSelector, alwaysWriteExceptions, - noConsoleNoAnsi)); + newSerializerBuilder() + .withConfiguration(config) + .withReplace(replace) + .withPatternSelector(patternSelector) + .withAlwaysWriteExceptions(alwaysWriteExceptions) + .withDisableAnsi(disableAnsi) + .withNoConsoleNoAnsi(noConsoleNoAnsi) + .withPattern(headerPattern) + .build(), + newSerializerBuilder() + .withConfiguration(config) + .withReplace(replace) + .withPatternSelector(patternSelector) + .withAlwaysWriteExceptions(alwaysWriteExceptions) + .withDisableAnsi(disableAnsi) + .withNoConsoleNoAnsi(noConsoleNoAnsi) + .withPattern(footerPattern) + .build()); this.conversionPattern = eventPattern; this.patternSelector = patternSelector; - this.eventSerializer = createSerializer(config, replace, eventPattern, DEFAULT_CONVERSION_PATTERN, - patternSelector, alwaysWriteExceptions, noConsoleNoAnsi); + this.eventSerializer = newSerializerBuilder() + .withConfiguration(config) + .withReplace(replace) + .withPatternSelector(patternSelector) + .withAlwaysWriteExceptions(alwaysWriteExceptions) + .withDisableAnsi(disableAnsi) + .withNoConsoleNoAnsi(noConsoleNoAnsi) + .withPattern(eventPattern) + .withDefaultPattern(DEFAULT_CONVERSION_PATTERN) + .build(); } public static SerializerBuilder newSerializerBuilder() { @@ -252,8 +277,10 @@ public final class PatternLayout extends AbstractStringLayout { * @param footerPattern * The footer to place at the bottom of the document, once. * @return The PatternLayout. + * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version. */ @PluginFactory + @Deprecated public static PatternLayout createLayout( @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern, @PluginElement("PatternSelector") final PatternSelector patternSelector, @@ -335,8 +362,9 @@ public final class PatternLayout extends AbstractStringLayout { private String defaultPattern; private PatternSelector patternSelector; private boolean alwaysWriteExceptions; + private boolean disableAnsi; private boolean noConsoleNoAnsi; - + @Override public Serializer build() { if (Strings.isEmpty(pattern) && Strings.isEmpty(defaultPattern)) { @@ -346,7 +374,7 @@ public final class PatternLayout extends AbstractStringLayout { try { final PatternParser parser = createPatternParser(configuration); final List<PatternFormatter> list = parser.parse(pattern == null ? defaultPattern : pattern, - alwaysWriteExceptions, noConsoleNoAnsi); + alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi); final PatternFormatter[] formatters = list.toArray(new PatternFormatter[0]); return new PatternSerializer(formatters, replace); } catch (final RuntimeException ex) { @@ -386,11 +414,16 @@ public final class PatternLayout extends AbstractStringLayout { return this; } + public SerializerBuilder withDisableAnsi(final boolean disableAnsi) { + this.disableAnsi = disableAnsi; + return this; + } + public SerializerBuilder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { this.noConsoleNoAnsi = noConsoleNoAnsi; return this; } - + } private static class PatternSelectorSerializer implements Serializer, Serializer2 { @@ -502,6 +535,9 @@ public final class PatternLayout extends AbstractStringLayout { private boolean alwaysWriteExceptions = true; @PluginBuilderAttribute + private boolean disableAnsi; + + @PluginBuilderAttribute private boolean noConsoleNoAnsi; @PluginBuilderAttribute @@ -513,28 +549,47 @@ public final class PatternLayout extends AbstractStringLayout { private Builder() { } - // TODO: move javadocs from PluginFactory to here + /** + * @param pattern + * The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN. + */ public Builder withPattern(final String pattern) { this.pattern = pattern; return this; } + /** + * @param patternSelector + * Allows different patterns to be used based on some selection criteria. + */ public Builder withPatternSelector(final PatternSelector patternSelector) { this.patternSelector = patternSelector; return this; } + /** + * @param configuration + * The Configuration. Some Converters require access to the Interpolator. + */ public Builder withConfiguration(final Configuration configuration) { this.configuration = configuration; return this; } + /** + * @param regexReplacement + * A Regex replacement + */ public Builder withRegexReplacement(final RegexReplacement regexReplacement) { this.regexReplacement = regexReplacement; return this; } + /** + * @param charset + * The character set. The platform default is used if not specified. + */ public Builder withCharset(final Charset charset) { // LOG4J2-783 if null, use platform default by default if (charset != null) { @@ -543,21 +598,46 @@ public final class PatternLayout extends AbstractStringLayout { return this; } + /** + * @param alwaysWriteExceptions + * If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens. + */ public Builder withAlwaysWriteExceptions(final boolean alwaysWriteExceptions) { this.alwaysWriteExceptions = alwaysWriteExceptions; return this; } + /** + * @param disableAnsi + * If {@code "true"} (default is false), do not output ANSI escape codes + */ + public Builder withDisableAnsi(final boolean disableAnsi) { + this.disableAnsi = disableAnsi; + return this; + } + + /** + * @param noConsoleNoAnsi + * If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes + */ public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { this.noConsoleNoAnsi = noConsoleNoAnsi; return this; } + /** + * @param header + * The footer to place at the top of the document, once. + */ public Builder withHeader(final String header) { this.header = header; return this; } + /** + * @param footer + * The footer to place at the bottom of the document, once. + */ public Builder withFooter(final String footer) { this.footer = footer; return this; @@ -570,7 +650,7 @@ public final class PatternLayout extends AbstractStringLayout { configuration = new DefaultConfiguration(); } return new PatternLayout(configuration, regexReplacement, pattern, patternSelector, charset, - alwaysWriteExceptions, noConsoleNoAnsi, header, footer); + alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi, header, footer); } } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java index 1ce81bd..062ebac 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java @@ -128,7 +128,7 @@ public final class Rfc5424Layout extends AbstractStringLayout { final String exceptionPattern, final boolean useTLSMessageFormat, final LoggerFields[] loggerFields) { super(charset); final PatternParser exceptionParser = createPatternParser(config, ThrowablePatternConverter.class); - exceptionFormatters = exceptionPattern == null ? null : exceptionParser.parse(exceptionPattern, false, false); + exceptionFormatters = exceptionPattern == null ? null : exceptionParser.parse(exceptionPattern, false, false, false); this.facility = facility; this.defaultId = id == null ? DEFAULT_ID : id; this.enterpriseNumber = ein; @@ -207,7 +207,7 @@ public final class Rfc5424Layout extends AbstractStringLayout { final PatternParser fieldParser = createPatternParser(config, null); for (final Map.Entry<String, String> entry : fields.entrySet()) { - final List<PatternFormatter> formatters = fieldParser.parse(entry.getValue(), false, false); + final List<PatternFormatter> formatters = fieldParser.parse(entry.getValue(), false, false, false); sdParams.put(entry.getKey(), formatters); } final FieldFormatter fieldFormatter = new FieldFormatter(sdParams, http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/ScriptPatternSelector.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/ScriptPatternSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/ScriptPatternSelector.java index e0b7d5e..bf0d808 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/ScriptPatternSelector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/ScriptPatternSelector.java @@ -45,7 +45,7 @@ import org.apache.logging.log4j.status.StatusLogger; public class ScriptPatternSelector implements PatternSelector { /** - * Custom ScriptPatternSelector builder. Use the {@link ScriptPatternSelector.#newBuilder() builder factory method} to create this. + * Custom ScriptPatternSelector builder. Use the {@link #newBuilder() builder factory method} to create this. */ public static class Builder implements org.apache.logging.log4j.core.util.Builder<ScriptPatternSelector> { @@ -61,10 +61,13 @@ public class ScriptPatternSelector implements PatternSelector { @PluginBuilderAttribute("alwaysWriteExceptions") private boolean alwaysWriteExceptions = true; - @PluginBuilderAttribute("noConsoleNoAnsi") - private boolean noConsoleNoAnsi; + @PluginBuilderAttribute("disableAnsi") + private boolean disableAnsi; - @PluginConfiguration + @PluginBuilderAttribute("noConsoleNoAnsi") + private boolean noConsoleNoAnsi; + + @PluginConfiguration private Configuration configuration; private Builder() { @@ -90,7 +93,8 @@ public class ScriptPatternSelector implements PatternSelector { LOGGER.warn("No marker patterns were provided"); return null; } - return new ScriptPatternSelector(script, properties, defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi, configuration); + return new ScriptPatternSelector(script, properties, defaultPattern, alwaysWriteExceptions, disableAnsi, + noConsoleNoAnsi, configuration); } public Builder withScript(final AbstractScript script) { @@ -113,6 +117,11 @@ public class ScriptPatternSelector implements PatternSelector { return this; } + public Builder withDisableAnsi(final boolean disableAnsi) { + this.disableAnsi = disableAnsi; + return this; + } + public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { this.noConsoleNoAnsi = noConsoleNoAnsi; return this; @@ -137,9 +146,13 @@ public class ScriptPatternSelector implements PatternSelector { private final Configuration configuration; + /** + * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version. + */ + @Deprecated public ScriptPatternSelector(final AbstractScript script, final PatternMatch[] properties, final String defaultPattern, - final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi, - final Configuration config) { + final boolean alwaysWriteExceptions, final boolean disableAnsi, + final boolean noConsoleNoAnsi, final Configuration config) { this.script = script; this.configuration = config; if (!(script instanceof ScriptRef)) { @@ -148,7 +161,7 @@ public class ScriptPatternSelector implements PatternSelector { final PatternParser parser = PatternLayout.createPatternParser(config); for (final PatternMatch property : properties) { try { - final List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, noConsoleNoAnsi); + final List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi); formatterMap.put(property.getKey(), list.toArray(new PatternFormatter[list.size()])); patternMap.put(property.getKey(), property.getPattern()); } catch (final RuntimeException ex) { @@ -156,7 +169,7 @@ public class ScriptPatternSelector implements PatternSelector { } } try { - final List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi); + final List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi); defaultFormatters = list.toArray(new PatternFormatter[list.size()]); this.defaultPattern = defaultPattern; } catch (final RuntimeException ex) { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java index eb46a7a..5ab92bd 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java @@ -66,6 +66,14 @@ import org.apache.logging.log4j.util.Strings; * You can use whitespace around the comma and equal sign. The names in values MUST come from the * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally. * </p> + * + * <p> + * To disable ANSI output unconditionally, specify an additional option <code>disableAnsi=true</code>, or to + * disable ANSI output if no console is detected, specify option <code>noConsoleNoAnsi=true</code> e.g.. + * </p> + * <pre> + * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{STYLE=DEFAULT, noConsoleNoAnsi=true} + * </pre> */ @Plugin(name = "highlight", category = PatternConverter.CATEGORY) @ConverterKeys({ "highlight" }) @@ -137,7 +145,9 @@ public final class HighlightConverter extends LogEventPatternConverter implement return DEFAULT_STYLES; } // Feels like a hack. Should String[] options change to a Map<String,String>? - final String string = options[1].replaceAll(PatternParser.NO_CONSOLE_NO_ANSI + "=(true|false)", Strings.EMPTY); + final String string = options[1] + .replaceAll(PatternParser.DISABLE_ANSI + "=(true|false)", Strings.EMPTY) + .replaceAll(PatternParser.NO_CONSOLE_NO_ANSI + "=(true|false)", Strings.EMPTY); // final Map<String, String> styles = AnsiEscape.createMap(string, new String[] {STYLE_KEY}); final Map<Level, String> levelStyles = new HashMap<>(DEFAULT_STYLES); @@ -184,8 +194,9 @@ public final class HighlightConverter extends LogEventPatternConverter implement } final PatternParser parser = PatternLayout.createPatternParser(config); final List<PatternFormatter> formatters = parser.parse(options[0]); + final boolean disableAnsi = Arrays.toString(options).contains(PatternParser.DISABLE_ANSI + "=true"); final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true"); - final boolean hideAnsi = noConsoleNoAnsi && System.console() == null; + final boolean hideAnsi = disableAnsi || (noConsoleNoAnsi && System.console() == null); return new HighlightConverter(formatters, createLevelStyleMap(options), hideAnsi); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java index 4adb90a..6881550 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java @@ -41,6 +41,7 @@ import org.apache.logging.log4j.util.Strings; * PatternConverters}. */ public final class PatternParser { + static final String DISABLE_ANSI = "disableAnsi"; static final String NO_CONSOLE_NO_ANSI = "noConsoleNoAnsi"; /** @@ -159,16 +160,21 @@ public final class PatternParser { } public List<PatternFormatter> parse(final String pattern) { - return parse(pattern, false, false); + return parse(pattern, false, false, false); } public List<PatternFormatter> parse(final String pattern, final boolean alwaysWriteExceptions, - final boolean noConsoleNoAnsi) { + final boolean noConsoleNoAnsi) { + return parse(pattern, alwaysWriteExceptions, false, noConsoleNoAnsi); + } + + public List<PatternFormatter> parse(final String pattern, final boolean alwaysWriteExceptions, + boolean disableAnsi, final boolean noConsoleNoAnsi) { final List<PatternFormatter> list = new ArrayList<>(); final List<PatternConverter> converters = new ArrayList<>(); final List<FormattingInfo> fields = new ArrayList<>(); - parse(pattern, converters, fields, noConsoleNoAnsi, true); + parse(pattern, converters, fields, disableAnsi, noConsoleNoAnsi, true); final Iterator<FormattingInfo> fieldIter = fields.iterator(); boolean handlesThrowable = false; @@ -305,13 +311,35 @@ public final class PatternParser { * @param formattingInfos * list to receive field specifiers corresponding to pattern converters. * @param noConsoleNoAnsi - * TODO + * do not do not output ANSI escape codes if {@link System#console()} * @param convertBackslashes if {@code true}, backslash characters are treated as escape characters and character * sequences like "\" followed by "t" (backslash+t) are converted to special characters like '\t' (tab). */ public void parse(final String pattern, final List<PatternConverter> patternConverters, - final List<FormattingInfo> formattingInfos, final boolean noConsoleNoAnsi, - final boolean convertBackslashes) { + final List<FormattingInfo> formattingInfos, final boolean noConsoleNoAnsi, + final boolean convertBackslashes) { + parse(pattern, patternConverters, formattingInfos, false, noConsoleNoAnsi, convertBackslashes); + } + + /** + * Parse a format specifier. + * + * @param pattern + * pattern to parse. + * @param patternConverters + * list to receive pattern converters. + * @param formattingInfos + * list to receive field specifiers corresponding to pattern converters. + * @param disableAnsi + * do not output ANSI escape codes + * @param noConsoleNoAnsi + * do not do not output ANSI escape codes if {@link System#console()} + * @param convertBackslashes if {@code true}, backslash characters are treated as escape characters and character + * sequences like "\" followed by "t" (backslash+t) are converted to special characters like '\t' (tab). + */ + public void parse(final String pattern, final List<PatternConverter> patternConverters, + final List<FormattingInfo> formattingInfos, final boolean disableAnsi, + final boolean noConsoleNoAnsi, final boolean convertBackslashes) { Objects.requireNonNull(pattern, "pattern"); final StringBuilder currentLiteral = new StringBuilder(BUF_SIZE); @@ -384,7 +412,7 @@ public final class PatternParser { state = ParserState.MIN_STATE; } else { i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules, - patternConverters, formattingInfos, noConsoleNoAnsi, convertBackslashes); + patternConverters, formattingInfos, disableAnsi, noConsoleNoAnsi, convertBackslashes); // Next pattern is assumed to be a literal. state = ParserState.LITERAL_STATE; @@ -406,7 +434,7 @@ public final class PatternParser { state = ParserState.DOT_STATE; } else { i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules, - patternConverters, formattingInfos, noConsoleNoAnsi, convertBackslashes); + patternConverters, formattingInfos, disableAnsi, noConsoleNoAnsi, convertBackslashes); state = ParserState.LITERAL_STATE; formattingInfo = FormattingInfo.getDefault(); currentLiteral.setLength(0); @@ -447,7 +475,7 @@ public final class PatternParser { formattingInfo.getMaxLength() * DECIMAL + c - '0', formattingInfo.isLeftTruncate()); } else { i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules, - patternConverters, formattingInfos, noConsoleNoAnsi, convertBackslashes); + patternConverters, formattingInfos, disableAnsi, noConsoleNoAnsi, convertBackslashes); state = ParserState.LITERAL_STATE; formattingInfo = FormattingInfo.getDefault(); currentLiteral.setLength(0); @@ -476,11 +504,15 @@ public final class PatternParser { * map of stock pattern converters keyed by format specifier. * @param options * converter options. - * @param noConsoleNoAnsi TODO + * @param disableAnsi + * do not output ANSI escape codes + * @param noConsoleNoAnsi + * do not do not output ANSI escape codes if {@link System#console()} * @return converter or null. */ private PatternConverter createConverter(final String converterId, final StringBuilder currentLiteral, - final Map<String, Class<PatternConverter>> rules, final List<String> options, final boolean noConsoleNoAnsi) { + final Map<String, Class<PatternConverter>> rules, final List<String> options, final boolean disableAnsi, + final boolean noConsoleNoAnsi) { String converterName = converterId; Class<PatternConverter> converterClass = null; @@ -499,6 +531,7 @@ public final class PatternParser { } if (AnsiConverter.class.isAssignableFrom(converterClass)) { + options.add(DISABLE_ANSI + '=' + disableAnsi); options.add(NO_CONSOLE_NO_ANSI + '=' + noConsoleNoAnsi); } // Work around the regression bug in Class.getDeclaredMethods() in Oracle Java in version > 1.6.0_17: @@ -580,8 +613,10 @@ public final class PatternParser { * list to receive parsed pattern converter. * @param formattingInfos * list to receive corresponding field specifier. + * @param disableAnsi + * do not output ANSI escape codes * @param noConsoleNoAnsi - * TODO + * do not do not output ANSI escape codes if {@link System#console()} * @param convertBackslashes if {@code true}, backslash characters are treated as escape characters and character * sequences like "\" followed by "t" (backslash+t) are converted to special characters like '\t' (tab). * @return position after format specifier sequence. @@ -589,7 +624,8 @@ public final class PatternParser { private int finalizeConverter(final char c, final String pattern, final int start, final StringBuilder currentLiteral, final FormattingInfo formattingInfo, final Map<String, Class<PatternConverter>> rules, final List<PatternConverter> patternConverters, - final List<FormattingInfo> formattingInfos, final boolean noConsoleNoAnsi, final boolean convertBackslashes) { + final List<FormattingInfo> formattingInfos, final boolean disableAnsi, final boolean noConsoleNoAnsi, + final boolean convertBackslashes) { int i = start; final StringBuilder convBuf = new StringBuilder(); i = extractConverter(c, pattern, i, convBuf, currentLiteral); @@ -599,7 +635,8 @@ public final class PatternParser { final List<String> options = new ArrayList<>(); i = extractOptions(pattern, i, options); - final PatternConverter pc = createConverter(converterId, currentLiteral, rules, options, noConsoleNoAnsi); + final PatternConverter pc = createConverter(converterId, currentLiteral, rules, options, disableAnsi, + noConsoleNoAnsi); if (pc == null) { StringBuilder msg; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java index 3c100e3..821005a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java @@ -28,6 +28,11 @@ import org.apache.logging.log4j.util.PerformanceSensitive; /** * Style pattern converter. Adds ANSI color styling to the result of the enclosed pattern. + * + * <p> + * To disable ANSI output unconditionally, specify an additional option <code>disableAnsi=true</code>, or to + * disable ANSI output if no console is detected, specify option <code>noConsoleNoAnsi=true</code>. + * </p> */ @Plugin(name = "style", category = PatternConverter.CATEGORY) @ConverterKeys({ "style" }) @@ -86,8 +91,9 @@ public final class StyleConverter extends LogEventPatternConverter implements An final PatternParser parser = PatternLayout.createPatternParser(config); final List<PatternFormatter> formatters = parser.parse(options[0]); final String style = AnsiEscape.createSequence(options[1].split(Patterns.COMMA_SEPARATOR)); + final boolean disableAnsi = Arrays.toString(options).contains(PatternParser.DISABLE_ANSI + "=true"); final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true"); - final boolean hideAnsi = noConsoleNoAnsi && System.console() == null; + final boolean hideAnsi = disableAnsi || (noConsoleNoAnsi && System.console() == null); return new StyleConverter(formatters, style, hideAnsi); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DisableAnsiTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DisableAnsiTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DisableAnsiTest.java new file mode 100644 index 0000000..7fea6fe --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DisableAnsiTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.pattern; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.apache.logging.log4j.util.Strings; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.*; + +public class DisableAnsiTest { + + private static final String EXPECTED = + "ERROR LoggerTest o.a.l.l.c.p.DisableAnsiTest org.apache.logging.log4j.core.pattern.DisableAnsiTest" + + Strings.LINE_SEPARATOR; + + @Rule + public LoggerContextRule init = new LoggerContextRule("log4j2-console-disableAnsi.xml"); + + private Logger logger; + private ListAppender app; + + @Before + public void setUp() throws Exception { + this.logger = this.init.getLogger("LoggerTest"); + this.app = this.init.getListAppender("List").clear(); + } + + @Test + public void testReplacement() { + logger.error(this.getClass().getName()); + + final List<String> msgs = app.getMessages(); + assertNotNull(msgs); + assertEquals("Incorrect number of messages. Should be 1 is " + msgs.size(), 1, msgs.size()); + assertTrue("Replacement failed - expected ending " + EXPECTED + ", actual " + msgs.get(0), msgs.get(0).endsWith(EXPECTED)); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java index 6051f85..37b803b 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java @@ -30,7 +30,7 @@ public class HighlightConverterTest { @Test public void testAnsiEmpty() { - final String[] options = {"", PatternParser.NO_CONSOLE_NO_ANSI + "=false"}; + final String[] options = {"", PatternParser.NO_CONSOLE_NO_ANSI + "=false, " + PatternParser.DISABLE_ANSI + "=false"}; final HighlightConverter converter = HighlightConverter.newInstance(null, options); final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( @@ -42,7 +42,7 @@ public class HighlightConverterTest { @Test public void testAnsiNonEmpty() { - final String[] options = {"%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=false"}; + final String[] options = {"%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=false, " + PatternParser.DISABLE_ANSI + "=false"}; final HighlightConverter converter = HighlightConverter.newInstance(null, options); final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( @@ -54,7 +54,7 @@ public class HighlightConverterTest { @Test public void testNoAnsiEmpty() { - final String[] options = {"", PatternParser.NO_CONSOLE_NO_ANSI + "=true"}; + final String[] options = {"", PatternParser.DISABLE_ANSI + "=true"}; final HighlightConverter converter = HighlightConverter.newInstance(null, options); final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( @@ -66,7 +66,7 @@ public class HighlightConverterTest { @Test public void testNoAnsiNonEmpty() { - final String[] options = {"%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=true"}; + final String[] options = {"%-5level: %msg", PatternParser.DISABLE_ANSI + "=true"}; final HighlightConverter converter = HighlightConverter.newInstance(null, options); final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java index ded0675..45c0a8b 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java @@ -64,7 +64,7 @@ public class PatternParserTest2 { final PatternParser parser0 = new PatternParser(null, "Converter", null); final List<PatternConverter> converters = new ArrayList<>(); final List<FormattingInfo> fields = new ArrayList<>(); - parser0.parse(pattern, converters, fields, false, convert); + parser0.parse(pattern, converters, fields, false, false, convert); final FormattingInfo[] infoArray = new FormattingInfo[fields.size()]; final FormattingInfo[] patternFields = fields.toArray(infoArray); final ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()]; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/log4j-core/src/test/resources/log4j2-console-disableAnsi.xml ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/resources/log4j2-console-disableAnsi.xml b/log4j-core/src/test/resources/log4j2-console-disableAnsi.xml new file mode 100644 index 0000000..b0e44ed --- /dev/null +++ b/log4j-core/src/test/resources/log4j2-console-disableAnsi.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +<Configuration status="OFF" name="DisableAnsiTest"> + <Appenders> + <List name="List"> + <PatternLayout disableAnsi="true"> + <Pattern>%d %highlight{%p} %style{%logger}{bright,cyan} %C{1.} %msg%n</Pattern> + </PatternLayout> + </List> + </Appenders> + + <Loggers> + <Root level="trace"> + <AppenderRef ref="List"/> + </Root> + </Loggers> + +</Configuration> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0fc7d75..a86116f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -27,6 +27,9 @@ <action issue="LOG4J2-1689" dev="mikes" type="fix"> Add ThreadContextMap3 interface supporting method removeAll(Iterable<String>). </action> + <action issue="LOG4J2-1685" dev="mikes" type="fix" due-to="Raman Gupta"> + Option 'disableAnsi' in PatternLayout to unconditionally disable ANSI escape codes. + </action> <action issue="LOG4J2-1679" dev="rpopma" type="fix"> (GC) Avoid allocating temporary objects in StructuredDataFilter. </action> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ea7b42f8/src/site/xdoc/manual/layouts.xml.vm ---------------------------------------------------------------------- diff --git a/src/site/xdoc/manual/layouts.xml.vm b/src/site/xdoc/manual/layouts.xml.vm index aa151ab..73517cb 100644 --- a/src/site/xdoc/manual/layouts.xml.vm +++ b/src/site/xdoc/manual/layouts.xml.vm @@ -485,6 +485,11 @@ WARN [main]: Message 2</pre> <td>The optional footer string to include at the bottom of each log file.</td> </tr> <tr> + <td>disableAnsi</td> + <td>boolean</td> + <td>If <code>true</code> (default is false), do not output ANSI escape codes.</td> + </tr> + <tr> <td>noConsoleNoAnsi</td> <td>boolean</td> <td>If <code>true</code> (default is false) and <code>System.console()</code> is null, do not output ANSI escape codes.</td>
