| /* |
| * 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 static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.util.Calendar; |
| import java.util.List; |
| |
| import org.apache.logging.log4j.Level; |
| import org.apache.logging.log4j.MarkerManager; |
| import org.apache.logging.log4j.core.LogEvent; |
| import org.apache.logging.log4j.core.Logger; |
| import org.apache.logging.log4j.core.LoggerContext; |
| import org.apache.logging.log4j.core.config.Configuration; |
| import org.apache.logging.log4j.core.config.NullConfiguration; |
| import org.apache.logging.log4j.core.impl.ContextDataFactory; |
| import org.apache.logging.log4j.core.impl.Log4jLogEvent; |
| import org.apache.logging.log4j.core.impl.ThrowableFormatOptions; |
| import org.apache.logging.log4j.core.time.internal.DummyNanoClock; |
| import org.apache.logging.log4j.core.time.SystemNanoClock; |
| import org.apache.logging.log4j.message.SimpleMessage; |
| import org.apache.logging.log4j.util.StringMap; |
| import org.apache.logging.log4j.util.Strings; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * |
| */ |
| public class PatternParserTest { |
| |
| static String OUTPUT_FILE = "output/PatternParser"; |
| static String WITNESS_FILE = "witness/PatternParser"; |
| LoggerContext ctx = LoggerContext.getContext(); |
| Logger root = ctx.getRootLogger(); |
| |
| private static String msgPattern = "%m%n"; |
| private final String mdcMsgPattern1 = "%m : %X%n"; |
| private final String mdcMsgPattern2 = "%m : %X{key1}%n"; |
| private final String mdcMsgPattern3 = "%m : %X{key2}%n"; |
| private final String mdcMsgPattern4 = "%m : %X{key3}%n"; |
| private final String mdcMsgPattern5 = "%m : %X{key1},%X{key2},%X{key3}%n"; |
| private final String deeplyNestedPattern = "%notEmpty{ %maxLen{%X{var}}{3} }"; |
| |
| private static String badPattern = "[%d{yyyyMMdd HH:mm:ss,SSS] %-5p [%c{10}] - %m%n"; |
| private static String customPattern = "[%d{yyyyMMdd HH:mm:ss,SSS}] %-5p [%-25.25c{1}:%-4L] - %m%n"; |
| private static String patternTruncateFromEnd = "%d; %-5p %5.-5c %m%n"; |
| private static String patternTruncateFromBeginning = "%d; %-5p %5.5c %m%n"; |
| private static String nestedPatternHighlight = |
| "%highlight{%d{dd MMM yyyy HH:mm:ss,SSS}{GMT+0} [%t] %-5level: %msg%n%throwable}"; |
| |
| private static final String KEY = "Converter"; |
| private PatternParser parser; |
| |
| @Before |
| public void setup() { |
| parser = new PatternParser(KEY); |
| } |
| |
| private void validateConverter(final List<PatternFormatter> formatter, final int index, final String name) { |
| final PatternConverter pc = formatter.get(index).getConverter(); |
| assertEquals("Incorrect converter " + pc.getName() + " at index " + index + " expected " + name, |
| pc.getName(), name); |
| } |
| |
| /** |
| * Test the default pattern |
| */ |
| @Test |
| public void defaultPattern() { |
| final List<PatternFormatter> formatters = parser.parse(msgPattern); |
| assertNotNull(formatters); |
| assertTrue(formatters.size() == 2); |
| validateConverter(formatters, 0, "Message"); |
| validateConverter(formatters, 1, "Line Sep"); |
| } |
| |
| /** |
| * Test the custom pattern |
| */ |
| @Test |
| public void testCustomPattern() { |
| final List<PatternFormatter> formatters = parser.parse(customPattern); |
| assertNotNull(formatters); |
| final StringMap mdc = ContextDataFactory.createContextData(); |
| mdc.putValue("loginId", "Fred"); |
| final Throwable t = new Throwable(); |
| final StackTraceElement[] elements = t.getStackTrace(); |
| final Log4jLogEvent event = Log4jLogEvent.newBuilder() // |
| .setLoggerName("org.apache.logging.log4j.PatternParserTest") // |
| .setMarker(MarkerManager.getMarker("TEST")) // |
| .setLoggerFqcn(Logger.class.getName()) // |
| .setLevel(Level.INFO) // |
| .setMessage(new SimpleMessage("Hello, world")) // |
| .setContextData(mdc) // |
| .setThreadName("Thread1") // |
| .setSource(elements[0]) |
| .setTimeMillis(System.currentTimeMillis()).build(); |
| final StringBuilder buf = new StringBuilder(); |
| for (final PatternFormatter formatter : formatters) { |
| formatter.format(event, buf); |
| } |
| final String str = buf.toString(); |
| final String expected = "INFO [PatternParserTest :104 ] - Hello, world" + Strings.LINE_SEPARATOR; |
| assertTrue("Expected to end with: " + expected + ". Actual: " + str, str.endsWith(expected)); |
| } |
| |
| @Test |
| public void testPatternTruncateFromBeginning() { |
| final List<PatternFormatter> formatters = parser.parse(patternTruncateFromBeginning); |
| assertNotNull(formatters); |
| final LogEvent event = Log4jLogEvent.newBuilder() // |
| .setLoggerName("org.apache.logging.log4j.PatternParserTest") // |
| .setLoggerFqcn(Logger.class.getName()) // |
| .setLevel(Level.INFO) // |
| .setMessage(new SimpleMessage("Hello, world")) // |
| .setThreadName("Thread1") // |
| .setTimeMillis(System.currentTimeMillis()) // |
| .build(); |
| final StringBuilder buf = new StringBuilder(); |
| for (final PatternFormatter formatter : formatters) { |
| formatter.format(event, buf); |
| } |
| final String str = buf.toString(); |
| final String expected = "INFO rTest Hello, world" + Strings.LINE_SEPARATOR; |
| assertTrue("Expected to end with: " + expected + ". Actual: " + str, str.endsWith(expected)); |
| } |
| |
| @Test |
| public void testPatternTruncateFromEnd() { |
| final List<PatternFormatter> formatters = parser.parse(patternTruncateFromEnd); |
| assertNotNull(formatters); |
| final LogEvent event = Log4jLogEvent.newBuilder() // |
| .setLoggerName("org.apache.logging.log4j.PatternParserTest") // |
| .setLoggerFqcn(Logger.class.getName()) // |
| .setLevel(Level.INFO) // |
| .setMessage(new SimpleMessage("Hello, world")) // |
| .setThreadName("Thread1") // |
| .setTimeMillis(System.currentTimeMillis()) // |
| .build(); |
| final StringBuilder buf = new StringBuilder(); |
| for (final PatternFormatter formatter : formatters) { |
| formatter.format(event, buf); |
| } |
| final String str = buf.toString(); |
| final String expected = "INFO org.a Hello, world" + Strings.LINE_SEPARATOR; |
| assertTrue("Expected to end with: " + expected + ". Actual: " + str, str.endsWith(expected)); |
| } |
| |
| @Test |
| public void testBadPattern() { |
| final Calendar cal = Calendar.getInstance(); |
| cal.set(2001, Calendar.FEBRUARY, 3, 4, 5, 6); |
| cal.set(Calendar.MILLISECOND, 789); |
| final long timestamp = cal.getTimeInMillis(); |
| |
| final List<PatternFormatter> formatters = parser.parse(badPattern); |
| assertNotNull(formatters); |
| final Throwable t = new Throwable(); |
| final StackTraceElement[] elements = t.getStackTrace(); |
| final LogEvent event = Log4jLogEvent.newBuilder() // |
| .setLoggerName("a.b.c") // |
| .setLoggerFqcn(Logger.class.getName()) // |
| .setLevel(Level.INFO) // |
| .setMessage(new SimpleMessage("Hello, world")) // |
| .setThreadName("Thread1") // |
| .setSource(elements[0]) // |
| .setTimeMillis(timestamp) // |
| .build(); |
| final StringBuilder buf = new StringBuilder(); |
| for (final PatternFormatter formatter : formatters) { |
| formatter.format(event, buf); |
| } |
| final String str = buf.toString(); |
| |
| // eats all characters until the closing '}' character |
| final String expected = "[2001-02-03 04:05:06,789] - Hello, world"; |
| assertTrue("Expected to start with: " + expected + ". Actual: " + str, str.startsWith(expected)); |
| } |
| |
| @Test |
| public void testNestedPatternHighlight() { |
| testNestedPatternHighlight(Level.TRACE, "\u001B[30m"); |
| testNestedPatternHighlight(Level.DEBUG, "\u001B[36m"); |
| testNestedPatternHighlight(Level.INFO, "\u001B[32m"); |
| testNestedPatternHighlight(Level.WARN, "\u001B[33m"); |
| testNestedPatternHighlight(Level.ERROR, "\u001B[1;31m"); |
| testNestedPatternHighlight(Level.FATAL, "\u001B[1;31m"); |
| } |
| |
| private void testNestedPatternHighlight(final Level level, final String expectedStart) { |
| final List<PatternFormatter> formatters = parser.parse(nestedPatternHighlight); |
| assertNotNull(formatters); |
| final Throwable t = new Throwable(); |
| t.getStackTrace(); |
| final LogEvent event = Log4jLogEvent.newBuilder() // |
| .setLoggerName("org.apache.logging.log4j.PatternParserTest") // |
| .setMarker(MarkerManager.getMarker("TEST")) // |
| .setLoggerFqcn(Logger.class.getName()) // |
| .setLevel(level) // |
| .setMessage(new SimpleMessage("Hello, world")) // |
| .setThreadName("Thread1") // |
| .setSource(/*stackTraceElement[0]*/ null) // |
| .setTimeMillis(System.currentTimeMillis()) // |
| .build(); |
| final StringBuilder buf = new StringBuilder(); |
| for (final PatternFormatter formatter : formatters) { |
| formatter.format(event, buf); |
| } |
| final String str = buf.toString(); |
| final String expectedEnd = String.format("] %-5s: Hello, world%s\u001B[m", level, Strings.LINE_SEPARATOR); |
| assertTrue("Expected to start with: " + expectedStart + ". Actual: " + str, str.startsWith(expectedStart)); |
| assertTrue("Expected to end with: \"" + expectedEnd + "\". Actual: \"" + str, str.endsWith(expectedEnd)); |
| } |
| |
| @Test |
| public void testNanoPatternShort() { |
| testFirstConverter("%N", NanoTimePatternConverter.class); |
| } |
| |
| @Test |
| public void testNanoPatternLong() { |
| testFirstConverter("%nano", NanoTimePatternConverter.class); |
| } |
| |
| @Test |
| public void testThreadNamePattern() { |
| testThreadNamePattern("%thread"); |
| } |
| |
| @Test |
| public void testThreadNameFullPattern() { |
| testThreadNamePattern("%threadName"); |
| } |
| |
| @Test |
| public void testThreadIdFullPattern() { |
| testThreadIdPattern("%threadId"); |
| } |
| |
| @Test |
| public void testThreadIdShortPattern1() { |
| testThreadIdPattern("%tid"); |
| } |
| |
| @Test |
| public void testThreadIdShortPattern2() { |
| testThreadIdPattern("%T"); |
| } |
| |
| @Test |
| public void testThreadPriorityShortPattern() { |
| testThreadPriorityPattern("%tp"); |
| } |
| |
| @Test |
| public void testThreadPriorityFullPattern() { |
| testThreadPriorityPattern("%threadPriority"); |
| } |
| |
| @Test |
| public void testLoggerFqcnPattern() { |
| testFirstConverter("%fqcn", LoggerFqcnPatternConverter.class); |
| } |
| |
| @Test |
| public void testEndOfBatchPattern() { |
| testFirstConverter("%endOfBatch", EndOfBatchPatternConverter.class); |
| } |
| |
| private void testThreadIdPattern(final String pattern) { |
| testFirstConverter(pattern, ThreadIdPatternConverter.class); |
| } |
| |
| private void testThreadNamePattern(final String pattern) { |
| testFirstConverter(pattern, ThreadNamePatternConverter.class); |
| } |
| |
| private void testThreadPriorityPattern(final String pattern) { |
| testFirstConverter(pattern, ThreadPriorityPatternConverter.class); |
| } |
| |
| private void testFirstConverter(final String pattern, final Class<?> checkClass) { |
| final List<PatternFormatter> formatters = parser.parse(pattern); |
| assertNotNull(formatters); |
| final String msg = formatters.toString(); |
| assertEquals(msg, 1, formatters.size()); |
| assertTrue(msg, checkClass.isInstance(formatters.get(0).getConverter())); |
| } |
| |
| @Test |
| public void testThreadNameShortPattern() { |
| testThreadNamePattern("%t"); |
| } |
| |
| @Test |
| public void testNanoPatternShortChangesConfigurationNanoClock() { |
| final Configuration config = new NullConfiguration(); |
| assertTrue(config.getNanoClock() instanceof DummyNanoClock); |
| |
| final PatternParser pp = new PatternParser(config, KEY, null); |
| assertTrue(config.getNanoClock() instanceof DummyNanoClock); |
| |
| pp.parse("%m"); |
| assertTrue(config.getNanoClock() instanceof DummyNanoClock); |
| |
| pp.parse("%nano"); // this changes the config clock |
| assertTrue(config.getNanoClock() instanceof SystemNanoClock); |
| } |
| |
| @Test |
| public void testNanoPatternLongChangesNanoClockFactoryMode() { |
| final Configuration config = new NullConfiguration(); |
| assertTrue(config.getNanoClock() instanceof DummyNanoClock); |
| |
| final PatternParser pp = new PatternParser(config, KEY, null); |
| assertTrue(config.getNanoClock() instanceof DummyNanoClock); |
| |
| pp.parse("%m"); |
| assertTrue(config.getNanoClock() instanceof DummyNanoClock); |
| |
| pp.parse("%N"); |
| assertTrue(config.getNanoClock() instanceof SystemNanoClock); |
| } |
| |
| @Test |
| public void testDeeplyNestedPattern() { |
| final List<PatternFormatter> formatters = parser.parse(deeplyNestedPattern); |
| assertNotNull(formatters); |
| assertEquals(1, formatters.size()); |
| |
| final StringMap mdc = ContextDataFactory.createContextData(); |
| mdc.putValue("var", "1234"); |
| final Log4jLogEvent event = Log4jLogEvent.newBuilder() // |
| .setContextData(mdc).build(); |
| final StringBuilder buf = new StringBuilder(); |
| formatters.get(0).format(event, buf); |
| final String expected = " 123 "; |
| assertEquals(expected, buf.toString()); |
| } |
| |
| @Test |
| public void testMissingClosingBracket() { |
| testFirstConverter("%d{", DatePatternConverter.class); |
| } |
| |
| @Test |
| public void testClosingBracketButWrongPlace() { |
| final List<PatternFormatter> formatters = parser.parse("}%d{"); |
| assertNotNull(formatters); |
| assertEquals(2, formatters.size()); |
| |
| validateConverter(formatters, 0, "Literal"); |
| validateConverter(formatters, 1, "Date"); |
| } |
| |
| @Test |
| public void testExceptionWithFilters() { |
| final List<PatternFormatter> formatters = parser |
| .parse("%d{DEFAULT} - %msg - %xEx{full}{filters(org.junit,org.eclipse)}%n"); |
| assertNotNull(formatters); |
| assertEquals(6, formatters.size()); |
| final PatternFormatter patternFormatter = formatters.get(4); |
| final LogEventPatternConverter converter = patternFormatter.getConverter(); |
| assertEquals(ExtendedThrowablePatternConverter.class, converter.getClass()); |
| final ExtendedThrowablePatternConverter exConverter = (ExtendedThrowablePatternConverter) converter; |
| final ThrowableFormatOptions options = exConverter.getOptions(); |
| assertTrue(options.getIgnorePackages().contains("org.junit")); |
| assertTrue(options.getIgnorePackages().contains("org.eclipse")); |
| assertEquals(System.lineSeparator(), options.getSeparator()); |
| } |
| |
| @Test |
| public void testExceptionWithFiltersAndSeparator() { |
| final List<PatternFormatter> formatters = parser |
| .parse("%d{DEFAULT} - %msg - %xEx{full}{filters(org.junit,org.eclipse)}{separator(|)}%n"); |
| assertNotNull(formatters); |
| assertEquals(6, formatters.size()); |
| final PatternFormatter patternFormatter = formatters.get(4); |
| final LogEventPatternConverter converter = patternFormatter.getConverter(); |
| assertEquals(ExtendedThrowablePatternConverter.class, converter.getClass()); |
| final ExtendedThrowablePatternConverter exConverter = (ExtendedThrowablePatternConverter) converter; |
| final ThrowableFormatOptions options = exConverter.getOptions(); |
| final List<String> ignorePackages = options.getIgnorePackages(); |
| assertNotNull(ignorePackages); |
| final String ignorePackagesString = ignorePackages.toString(); |
| assertTrue(ignorePackagesString, ignorePackages.contains("org.junit")); |
| assertTrue(ignorePackagesString, ignorePackages.contains("org.eclipse")); |
| assertEquals("|", options.getSeparator()); |
| } |
| |
| } |