You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/02/06 14:27:12 UTC
[logging-log4j2] branch release-2.x updated: Add more missing code and tests from Log4j 1.2.17.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/release-2.x by this push:
new bbfd378 Add more missing code and tests from Log4j 1.2.17.
bbfd378 is described below
commit bbfd3788242ae4f88622227ade5c210681b5198a
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 6 09:27:07 2022 -0500
Add more missing code and tests from Log4j 1.2.17.
2 tests are ignored for now.
---
.../main/java/org/apache/log4j/PatternLayout.java | 76 ++-
.../org/apache/log4j/helpers/FormattingInfo.java | 39 ++
.../org/apache/log4j/helpers/OptionConverter.java | 3 +-
.../org/apache/log4j/helpers/PatternConverter.java | 111 ++++
.../org/apache/log4j/helpers/PatternParser.java | 570 +++++++++++++++++++++
.../src/test/java/org/apache/log4j/LayoutTest.java | 168 ++++++
.../org/apache/log4j/PropertyConfiguratorTest.java | 2 +-
.../org/apache/log4j/helpers/DateLayoutTest.java | 288 +++++++++++
.../log4j/helpers/OptionConverterTestCase.java | 177 +++++++
.../log4j/helpers/PatternParserTestCase.java | 131 +++++
.../test/java/org/apache/log4j/util/Compare.java | 150 ++++++
.../src/test/java/org/apache/log4j/xml/XLevel.java | 74 +++
src/changes/changes.xml | 9 +
13 files changed, 1794 insertions(+), 4 deletions(-)
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
index e250700..da6f87f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
@@ -16,8 +16,9 @@
*/
package org.apache.log4j;
+import org.apache.log4j.helpers.PatternConverter;
+import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
-import org.apache.logging.log4j.util.Strings;
/**
*
@@ -35,6 +36,17 @@ public class PatternLayout extends Layout {
*/
public final static String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
+ protected final int BUF_SIZE = 256;
+
+ protected final int MAX_CAPACITY = 1024;
+
+ // output buffer appended to when format() is invoked
+ private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
+
+ private String pattern;
+
+ private PatternConverter head;
+
/**
* Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
*
@@ -44,17 +56,77 @@ public class PatternLayout extends Layout {
this(DEFAULT_CONVERSION_PATTERN);
}
+ /**
+ * Constructs a PatternLayout using the supplied conversion pattern.
+ */
public PatternLayout(final String pattern) {
+ this.pattern = pattern;
+ head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse();
+ }
+ /**
+ * Does not do anything as options become effective
+ */
+ public void activateOptions() {
+ // nothing to do.
+ }
+
+ /**
+ * Returns PatternParser used to parse the conversion string. Subclasses may override this to return a subclass of
+ * PatternParser which recognize custom conversion characters.
+ *
+ * @since 0.9.0
+ */
+ protected PatternParser createPatternParser(final String pattern) {
+ return new PatternParser(pattern);
}
+ /**
+ * Produces a formatted string as specified by the conversion pattern.
+ */
@Override
public String format(final LoggingEvent event) {
- return Strings.EMPTY;
+ // Reset working stringbuffer
+ if (sbuf.capacity() > MAX_CAPACITY) {
+ sbuf = new StringBuffer(BUF_SIZE);
+ } else {
+ sbuf.setLength(0);
+ }
+
+ PatternConverter c = head;
+
+ while (c != null) {
+ c.format(sbuf, event);
+ c = c.next;
+ }
+ return sbuf.toString();
+ }
+
+ /**
+ * Returns the value of the <b>ConversionPattern</b> option.
+ */
+ public String getConversionPattern() {
+ return pattern;
}
+ /**
+ * The PatternLayout does not handle the throwable contained within {@link LoggingEvent LoggingEvents}. Thus, it returns
+ * <code>true</code>.
+ *
+ * @since 0.8.4
+ */
@Override
public boolean ignoresThrowable() {
return true;
}
+
+ /**
+ * Set the <b>ConversionPattern</b> option. This is the string which controls formatting and consists of a mix of
+ * literal content and conversion specifiers.
+ */
+ public void setConversionPattern(final String conversionPattern) {
+ pattern = conversionPattern;
+ head = createPatternParser(conversionPattern).parse();
+ }
+
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
new file mode 100644
index 0000000..7b62660
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
@@ -0,0 +1,39 @@
+/*
+ * 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.log4j.helpers;
+
+/**
+ * FormattingInfo instances contain the information obtained when parsing formatting modifiers in conversion modifiers.
+ *
+ * @since 0.8.2
+ */
+public class FormattingInfo {
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ void dump() {
+ LogLog.debug("min=" + min + ", max=" + max + ", leftAlign=" + leftAlign);
+ }
+
+ void reset() {
+ min = -1;
+ max = 0x7FFFFFFF;
+ leftAlign = false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
index e4030f1..ad52737 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -30,6 +30,7 @@ import org.apache.log4j.spi.Configurator;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LoaderUtil;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.Strings;
@@ -52,7 +53,7 @@ public class OptionConverter {
static char DELIM_STOP = '}';
static int DELIM_START_LEN = 2;
static int DELIM_STOP_LEN = 1;
- private static final Logger LOGGER = LogManager.getLogger(OptionConverter.class);
+ private static final Logger LOGGER = StatusLogger.getLogger();
private static final CharMap[] charMap = new CharMap[] {
new CharMap('n', '\n'),
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
new file mode 100644
index 0000000..2b46db7
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+
+ <p>PatternConverter is an abtract class that provides the
+ formatting functionality that derived classes need.
+
+ <p>Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting a logging event in a converter specific manner.
+
+ @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ protected
+ PatternConverter() { }
+
+ protected
+ PatternConverter(FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ Derived pattern converters must override this method in order to
+ convert conversion specifiers in the correct way.
+ */
+ abstract
+ protected
+ String convert(LoggingEvent event);
+
+ /**
+ A template method for formatting in a converter specific way.
+ */
+ public
+ void format(StringBuffer sbuf, LoggingEvent e) {
+ String s = convert(e);
+
+ if(s == null) {
+ if(0 < min)
+ spacePad(sbuf, min);
+ return;
+ }
+
+ int len = s.length();
+
+ if(len > max)
+ sbuf.append(s.substring(len-max));
+ else if(len < min) {
+ if(leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min-len);
+ }
+ else {
+ spacePad(sbuf, min-len);
+ sbuf.append(s);
+ }
+ }
+ else
+ sbuf.append(s);
+ }
+
+ static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces
+ " ", // 16 spaces
+ " " }; // 32 spaces
+
+ /**
+ Fast space padding method.
+ */
+ public
+ void spacePad(StringBuffer sbuf, int length) {
+ while(length >= 32) {
+ sbuf.append(SPACES[5]);
+ length -= 32;
+ }
+
+ for(int i = 4; i >= 0; i--) {
+ if((length & (1<<i)) != 0) {
+ sbuf.append(SPACES[i]);
+ }
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
new file mode 100644
index 0000000..0d3ead6
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
@@ -0,0 +1,570 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LocationInfo;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.Arrays;
+
+// Contributors: Nelson Minar <(n...@monkey.org>
+// Igor E. Poteryaev <ja...@mail.ru>
+// Reinhard Deschler <re...@web.de>
+
+/**
+ Most of the work of the {@link org.apache.log4j.PatternLayout} class
+ is delegated to the PatternParser class.
+
+ <p>It is this class that parses conversion patterns and creates
+ a chained list of {@link OptionConverter OptionConverters}.
+
+ @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+ @author Anders Kristensen
+
+ @since 0.8.2
+*/
+public class PatternParser {
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public
+ PatternParser(String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private
+ void addToList(PatternConverter pc) {
+ if(head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected
+ String extractOption() {
+ if((i < patternLength) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+ if (end > i) {
+ String r = pattern.substring(i + 1, end);
+ i = end+1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ The option is expected to be in decimal and positive. In case of
+ error, zero is returned. */
+ protected
+ int extractPrecisionOption() {
+ String opt = extractOption();
+ int r = 0;
+ if(opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if(r <= 0) {
+ LogLog.error(
+ "Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ }
+ catch (NumberFormatException e) {
+ LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public
+ PatternConverter parse() {
+ char c;
+ i = 0;
+ while(i < patternLength) {
+ c = pattern.charAt(i++);
+ switch(state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if(i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if(c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch(pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(Layout.LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(
+ currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ }
+ else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch(c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if(c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ }
+ else
+ finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.min = formattingInfo.min*10 + (c - '0');
+ else if(c == '.')
+ state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ }
+ else {
+ LogLog.error("Error occured in position "+i
+ +".\n Was expecting digit, instead got char \""+c+"\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.max = formattingInfo.max*10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected
+ void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch(c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CATEGORY converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CLASS_NAME converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ String dOpt = extractOption();
+ if(dOpt != null)
+ dateFormatStr = dOpt;
+
+ if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ }
+ catch (IllegalArgumentException e) {
+ LogLog.error("Could not instantiate SimpleDateFormat with " +
+ dateFormatStr, e);
+ df = (DateFormat) OptionConverter.instantiateByClassName(
+ "org.apache.log4j.helpers.ISO8601DateFormat",
+ DateFormat.class, null);
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo,
+ FILE_LOCATION_CONVERTER);
+ //LogLog.debug("File name converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'l':
+ pc = new LocationPatternConverter(formattingInfo,
+ FULL_LOCATION_CONVERTER);
+ //LogLog.debug("Location converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo,
+ LINE_LOCATION_CONVERTER);
+ //LogLog.debug("LINE NUMBER converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ //LogLog.debug("MESSAGE converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo,
+ METHOD_LOCATION_CONVERTER);
+ //LogLog.debug("METHOD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ //LogLog.debug("LEVEL converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo,
+ RELATIVE_TIME_CONVERTER);
+ //LogLog.debug("RELATIVE time converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ //LogLog.debug("THREAD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'u':
+ if(i < patternLength) {
+ char cNext = pattern.charAt(i);
+ if(cNext >= '0' && cNext <= '9') {
+ pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+ LogLog.debug("USER converter ["+cNext+"].");
+ formattingInfo.dump();
+ currentLiteral.setLength(0);
+ i++;
+ }
+ else
+ LogLog.error("Unexpected char" +cNext+" at position "+i);
+ }
+ break;*/
+ case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ //LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;
+ default:
+ LogLog.error("Unexpected char [" +c+"] at position "+i
+ +" in conversion patterrn.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected
+ void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ switch(type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ case NDC_CONVERTER:
+ return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default: return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(String value) {
+ literal = value;
+ }
+
+ public
+ final
+ void format(StringBuffer sbuf, LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ date.setTime(event.timeStamp);
+ String converted = null;
+ try {
+ converted = df.format(date);
+ }
+ catch (Exception ex) {
+ LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private static class MDCPatternConverter extends PatternConverter {
+ private String key;
+
+ MDCPatternConverter(FormattingInfo formattingInfo, String key) {
+ super(formattingInfo);
+ this.key = key;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ if (key == null) {
+ StringBuffer buf = new StringBuffer("{");
+ Map properties = event.getProperties();
+ if (properties.size() > 0) {
+ Object[] keys = properties.keySet().toArray();
+ Arrays.sort(keys);
+ for (int i = 0; i < keys.length; i++) {
+ buf.append('{');
+ buf.append(keys[i]);
+ buf.append(',');
+ buf.append(properties.get(keys[i]));
+ buf.append('}');
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ } else {
+ Object val = event.getMDC(key);
+ if(val == null) {
+ return null;
+ } else {
+ return val.toString();
+ }
+ }
+ }
+ }
+
+
+ private class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ LocationInfo locationInfo = event.getLocationInformation();
+ switch(type) {
+ case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default: return null;
+ }
+ }
+ }
+
+ private static abstract class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract
+ String getFullyQualifiedName(LoggingEvent event);
+
+ public
+ String convert(LoggingEvent event) {
+ String n = getFullyQualifiedName(event);
+ if(precision <= 0)
+ return n;
+ else {
+ int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len -1 ;
+ for(int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end-1);
+ if(end == -1)
+ return n;
+ }
+ return n.substring(end+1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
+
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
new file mode 100644
index 0000000..991c3e9
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for Layout.
+ *
+ */
+public class LayoutTest extends TestCase {
+
+ /**
+ * Concrete Layout class for tests.
+ */
+ private static final class MockLayout extends Layout {
+ /**
+ * @{inheritDoc}
+ */
+ public void activateOptions() {
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public String format(final LoggingEvent event) {
+ return "Mock";
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public boolean ignoresThrowable() {
+ return true;
+ }
+ }
+
+ /**
+ * Expected content type.
+ */
+ private final String contentType;
+
+ /**
+ * Expected value for ignoresThrowable.
+ */
+ private final boolean ignoresThrowable;
+
+ /**
+ * Expected value for header.
+ */
+ private final String header;
+
+ /**
+ * Expected value for footer.
+ */
+ private final String footer;
+
+ /**
+ * Construct a new instance of LayoutTest.
+ *
+ * @param testName test name.
+ */
+ public LayoutTest(final String testName) {
+ super(testName);
+ contentType = "text/plain";
+ ignoresThrowable = true;
+ header = null;
+ footer = null;
+ }
+
+ /**
+ * Constructor for use by derived tests.
+ *
+ * @param testName name of test.
+ * @param expectedContentType expected value for getContentType().
+ * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+ * @param expectedHeader expected value for getHeader().
+ * @param expectedFooter expected value for getFooter().
+ */
+ protected LayoutTest(final String testName, final String expectedContentType, final boolean expectedIgnoresThrowable, final String expectedHeader,
+ final String expectedFooter) {
+ super(testName);
+ contentType = expectedContentType;
+ ignoresThrowable = expectedIgnoresThrowable;
+ header = expectedHeader;
+ footer = expectedFooter;
+ }
+
+ /**
+ * Creates layout for test.
+ *
+ * @return new instance of Layout.
+ */
+ protected Layout createLayout() {
+ return new MockLayout();
+ }
+
+ /**
+ * Tests format.
+ *
+ * @throws Exception derived tests, particular XMLLayoutTest, may throw exceptions.
+ */
+ public void testFormat() throws Exception {
+ Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+ LoggingEvent event = new LoggingEvent("org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+ String result = createLayout().format(event);
+ assertEquals("Mock", result);
+ }
+
+ /**
+ * Tests getContentType.
+ */
+ public void testGetContentType() {
+ assertEquals(contentType, createLayout().getContentType());
+ }
+
+ /**
+ * Tests getFooter.
+ */
+ public void testGetFooter() {
+ assertEquals(footer, createLayout().getFooter());
+ }
+
+ /**
+ * Tests getHeader.
+ */
+ public void testGetHeader() {
+ assertEquals(header, createLayout().getHeader());
+ }
+
+ /**
+ * Tests ignoresThrowable.
+ */
+ public void testIgnoresThrowable() {
+ assertEquals(ignoresThrowable, createLayout().ignoresThrowable());
+ }
+
+ /**
+ * Tests Layout.LINE_SEP.
+ */
+ public void testLineSep() {
+ assertEquals(System.getProperty("line.separator"), Layout.LINE_SEP);
+ }
+
+ /**
+ * Tests Layout.LINE_SEP.
+ */
+ public void testLineSepLen() {
+ assertEquals(Layout.LINE_SEP.length(), Layout.LINE_SEP_LEN);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
index 26812ec..347bfd0 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
@@ -406,7 +406,7 @@ public class PropertyConfiguratorTest {
assertFalse(file.exists());
}
- void validateNested() {
+ public void validateNested() {
final Logger logger = Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest");
final String appenderName = "ROLLING";
// Appender OK
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
new file mode 100644
index 0000000..0573e78
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
@@ -0,0 +1,288 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.LayoutTest;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.TimeZone;
+import java.util.Calendar;
+
+/**
+ * Tests {@link DateLayout}.
+ */
+public class DateLayoutTest extends LayoutTest {
+
+ /**
+ * Construct a new instance of LayoutTest.
+ *
+ * @param testName test name.
+ */
+ public DateLayoutTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * Constructor for use by derived tests.
+ *
+ * @param testName name of test.
+ * @param expectedContentType expected value for getContentType().
+ * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+ * @param expectedHeader expected value for getHeader().
+ * @param expectedFooter expected value for getFooter().
+ */
+ protected DateLayoutTest(final String testName, final String expectedContentType, final boolean expectedIgnoresThrowable, final String expectedHeader,
+ final String expectedFooter) {
+ super(testName, expectedContentType, expectedIgnoresThrowable, expectedHeader, expectedFooter);
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ protected Layout createLayout() {
+ return new MockLayout();
+ }
+
+ /**
+ * Tests DateLayout.NULL_DATE_FORMAT constant.
+ */
+ public void testNullDateFormat() {
+ assertEquals("NULL", DateLayout.NULL_DATE_FORMAT);
+ }
+
+ /**
+ * Tests DateLayout.RELATIVE constant.
+ */
+ public void testRelativeTimeDateFormat() {
+ assertEquals("RELATIVE", DateLayout.RELATIVE_TIME_DATE_FORMAT);
+ }
+
+ /**
+ * Tests DateLayout.DATE_FORMAT_OPTION constant.
+ *
+ * @deprecated since constant is deprecated
+ */
+ public void testDateFormatOption() {
+ assertEquals("DateFormat", DateLayout.DATE_FORMAT_OPTION);
+ }
+
+ /**
+ * Tests DateLayout.TIMEZONE_OPTION constant.
+ *
+ * @deprecated since constant is deprecated
+ */
+ public void testTimeZoneOption() {
+ assertEquals("TimeZone", DateLayout.TIMEZONE_OPTION);
+ }
+
+ /**
+ * Tests getOptionStrings().
+ *
+ * @deprecated since getOptionStrings is deprecated.
+ *
+ */
+ public void testGetOptionStrings() {
+ String[] options = ((DateLayout) createLayout()).getOptionStrings();
+ assertEquals(2, options.length);
+ }
+
+ /**
+ * Tests setting DateFormat through setOption method.
+ *
+ * @deprecated since setOption is deprecated.
+ */
+ public void testSetOptionDateFormat() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setOption("dAtefOrmat", "foobar");
+ assertEquals("FOOBAR", layout.getDateFormat());
+ }
+
+ /**
+ * Tests setting TimeZone through setOption method.
+ *
+ * @deprecated since setOption is deprecated.
+ */
+ public void testSetOptionTimeZone() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setOption("tImezOne", "+05:00");
+ assertEquals("+05:00", layout.getTimeZone());
+ }
+
+ /**
+ * Tests setDateFormat.
+ */
+ public void testSetDateFormat() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("ABSOLUTE");
+ assertEquals("ABSOLUTE", layout.getDateFormat());
+ }
+
+ /**
+ * Tests setTimeZone.
+ */
+ public void testSetTimeZone() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setTimeZone("+05:00");
+ assertEquals("+05:00", layout.getTimeZone());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with null.
+ */
+ public void testSetDateFormatNull() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat((String) null, null);
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "NULL".
+ */
+ public void testSetDateFormatNullString() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("NuLL", null);
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "RELATIVE".
+ */
+ public void testSetDateFormatRelative() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("rElatIve", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "ABSOLUTE".
+ */
+ public void testSetDateFormatAbsolute() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("aBsolUte", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "DATETIME".
+ */
+ public void testSetDateFormatDateTime() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("dAte", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "ISO8601".
+ */
+ public void testSetDateFormatISO8601() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("iSo8601", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "HH:mm:ss".
+ */
+ public void testSetDateFormatSimple() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("HH:mm:ss", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests activateOptions.
+ */
+ public void testActivateOptions() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("HH:mm:ss");
+ layout.setTimeZone("+05:00");
+ layout.activateOptions();
+ }
+
+ /**
+ * Tests setDateFormat(DateFormat, TimeZone).
+ */
+ public void testSetDateFormatWithFormat() {
+ DateFormat format = new SimpleDateFormat("HH:mm");
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat(format, TimeZone.getDefault());
+ }
+
+ /**
+ * Tests IS08601DateFormat class.
+ *
+ * @deprecated since ISO8601DateFormat is deprecated
+ */
+ public void testISO8601Format() {
+ DateFormat format = new ISO8601DateFormat();
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(1970, 0, 1, 0, 0, 0);
+ String actual = format.format(calendar.getTime());
+ assertEquals("1970-01-01 00:00:00,000", actual);
+ }
+
+ /**
+ * Tests DateTimeDateFormat class.
+ *
+ * @deprecated since DateTimeDateFormat is deprecated
+ */
+ public void testDateTimeFormat() {
+ DateFormat format = new DateTimeDateFormat();
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(1970, 0, 1, 0, 0, 0);
+ String actual = format.format(calendar.getTime());
+ SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,SSS");
+ String expected = df.format(calendar.getTime());
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Concrete Layout class for tests.
+ */
+ private static final class MockLayout extends DateLayout {
+ /**
+ * Create new instance of MockLayout.
+ */
+ public MockLayout() {
+ //
+ // checks that protected fields are properly initialized
+ assertNotNull(pos);
+ assertNotNull(date);
+ assertNull(dateFormat);
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public String format(final LoggingEvent event) {
+ return "Mock";
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public void activateOptions() {
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public boolean ignoresThrowable() {
+ return true;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
new file mode 100644
index 0000000..83ded20
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+// Log4j uses the JUnit framework for internal unit testing. JUnit
+// is available from "http://www.junit.org".
+
+package org.apache.log4j.helpers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PropertyConfiguratorTest;
+import org.apache.log4j.xml.XLevel;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test variable substitution code.
+ *
+ * @since 1.0
+ */
+@Ignore("WIP")
+public class OptionConverterTestCase extends TestCase {
+
+ Properties props;
+
+ public OptionConverterTestCase(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ props = new Properties();
+ props.put("TOTO", "wonderful");
+ props.put("key1", "value1");
+ props.put("key2", "value2");
+ // Log4J will NPE without this:
+ props.put("line.separator", System.getProperty("line.separator"));
+ // Log4J will throw an Error without this:
+ props.put("java.home", System.getProperty("java.home"));
+ System.setProperties(props);
+
+ }
+
+ public void tearDown() {
+ props = null;
+ LogManager.resetConfiguration();
+ }
+
+ public void varSubstTest1() {
+ String r;
+
+ r = OptionConverter.substVars("hello world.", null);
+ assertEquals("hello world.", r);
+
+ r = OptionConverter.substVars("hello ${TOTO} world.", null);
+
+ assertEquals("hello wonderful world.", r);
+ }
+
+ public void varSubstTest2() {
+ String r;
+
+ r = OptionConverter.substVars("Test2 ${key1} mid ${key2} end.", null);
+ assertEquals("Test2 value1 mid value2 end.", r);
+ }
+
+ public void varSubstTest3() {
+ String r;
+
+ r = OptionConverter.substVars("Test3 ${unset} mid ${key1} end.", null);
+ assertEquals("Test3 mid value1 end.", r);
+ }
+
+ public void varSubstTest4() {
+ String val = "Test4 ${incomplete ";
+ try {
+ OptionConverter.substVars(val, null);
+ } catch (IllegalArgumentException e) {
+ String errorMsg = e.getMessage();
+ // System.out.println('['+errorMsg+']');
+ assertEquals('"' + val + "\" has no closing brace. Opening brace at position 6.", errorMsg);
+ }
+ }
+
+ public void varSubstTest5() {
+ Properties props = new Properties();
+ props.put("p1", "x1");
+ props.put("p2", "${p1}");
+ String res = OptionConverter.substVars("${p2}", props);
+ System.out.println("Result is [" + res + "].");
+ assertEquals(res, "x1");
+ }
+
+ /**
+ * Tests configuring Log4J from an InputStream.
+ *
+ * @since 1.2.17
+ */
+ public void testInputStream() throws IOException {
+ File file = new File("src/test/resources/log4j1-1.2.17/input/filter1.properties");
+ assertTrue(file.exists());
+ try (FileInputStream inputStream = new FileInputStream(file)) {
+ OptionConverter.selectAndConfigure(inputStream, null, LogManager.getLoggerRepository());
+ }
+ new PropertyConfiguratorTest().validateNested();
+ }
+
+ public void toLevelTest1() {
+ String val = "INFO";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, Level.INFO);
+ }
+
+ public void toLevelTest2() {
+ String val = "INFO#org.apache.log4j.xml.XLevel";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, Level.INFO);
+ }
+
+ public void toLevelTest3() {
+ String val = "TRACE#org.apache.log4j.xml.XLevel";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, XLevel.TRACE);
+ }
+
+ public void toLevelTest4() {
+ String val = "TR#org.apache.log4j.xml.XLevel";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, null);
+ }
+
+ public void toLevelTest5() {
+ String val = "INFO#org.apache.log4j.xml.TOTO";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, null);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new OptionConverterTestCase("varSubstTest5"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest1"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest2"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest3"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest4"));
+
+ suite.addTest(new OptionConverterTestCase("testInputStream"));
+
+ suite.addTest(new OptionConverterTestCase("toLevelTest1"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest2"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest3"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest4"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest5"));
+ return suite;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
new file mode 100644
index 0000000..e91cc0f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.MDC;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.util.Compare;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for helpers/PatternParser.java. Tests the various conversion patterns supported by PatternParser. This test
+ * class tests PatternParser via the PatternLayout class which uses it.
+ */
+@Ignore("WIP")
+public class PatternParserTestCase extends TestCase {
+
+ static String OUTPUT_FILE = "target/PatternParser";
+ static String WITNESS_FILE = "target/witness/PatternParser";
+
+ static String msgPattern = "%m%n";
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new PatternParserTestCase("mdcPattern"));
+ return suite;
+ }
+ Logger root;
+
+ Logger logger;
+
+ public PatternParserTestCase(String name) {
+ super(name);
+ }
+
+ /**
+ * Test case for MDC conversion pattern.
+ */
+ public void mdcPattern() throws Exception {
+
+ String mdcMsgPattern1 = "%m : %X%n";
+ String mdcMsgPattern2 = "%m : %X{key1}%n";
+ String mdcMsgPattern3 = "%m : %X{key2}%n";
+ String mdcMsgPattern4 = "%m : %X{key3}%n";
+ String mdcMsgPattern5 = "%m : %X{key1},%X{key2},%X{key3}%n";
+
+ // set up appender
+ PatternLayout layout = new PatternLayout(msgPattern);
+ Appender appender = new FileAppender(layout, OUTPUT_FILE + "_mdc", false);
+
+ // set appender on root and set level to debug
+ root.addAppender(appender);
+ root.setLevel(Level.DEBUG);
+
+ // output starting message
+ root.debug("starting mdc pattern test");
+
+ layout.setConversionPattern(mdcMsgPattern1);
+ root.debug("empty mdc, no key specified in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern2);
+ root.debug("empty mdc, key1 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern3);
+ root.debug("empty mdc, key2 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern4);
+ root.debug("empty mdc, key3 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern5);
+ root.debug("empty mdc, key1, key2, and key3 in pattern");
+
+ MDC.put("key1", "value1");
+ MDC.put("key2", "value2");
+
+ layout.setConversionPattern(mdcMsgPattern1);
+ root.debug("filled mdc, no key specified in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern2);
+ root.debug("filled mdc, key1 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern3);
+ root.debug("filled mdc, key2 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern4);
+ root.debug("filled mdc, key3 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern5);
+ root.debug("filled mdc, key1, key2, and key3 in pattern");
+
+ MDC.remove("key1");
+ MDC.remove("key2");
+
+ layout.setConversionPattern(msgPattern);
+ root.debug("finished mdc pattern test");
+
+ assertTrue(Compare.compare(OUTPUT_FILE + "_mdc", WITNESS_FILE + "_mdc"));
+ }
+
+ public void setUp() {
+ root = Logger.getRootLogger();
+ root.removeAllAppenders();
+ }
+
+ public void tearDown() {
+ root.getLoggerRepository().resetConfiguration();
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
new file mode 100644
index 0000000..b65b9e9
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
@@ -0,0 +1,150 @@
+/*
+ * 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.log4j.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class Compare {
+
+ static final int B1_NULL = -1;
+ static final int B2_NULL = -2;
+
+ public static boolean compare(final Class testClass, final String file1, final String file2) throws IOException {
+ try (final BufferedReader in1 = new BufferedReader(new FileReader(file1));
+ final BufferedReader in2 = new BufferedReader(new InputStreamReader(open(testClass, file2)))) {
+ return compare(testClass, file1, file2, in1, in2);
+ }
+ }
+
+ public static boolean compare(final Class testClass, final String file1, final String file2, final BufferedReader in1, final BufferedReader in2)
+ throws IOException {
+
+ String s1;
+ int lineCounter = 0;
+
+ while ((s1 = in1.readLine()) != null) {
+ lineCounter++;
+
+ final String s2 = in2.readLine();
+
+ if (!s1.equals(s2)) {
+ System.out.println("Files [" + file1 + "] and [" + file2 + "] differ on line " + lineCounter);
+ System.out.println("One reads: [" + s1 + "].");
+ System.out.println("Other reads:[" + s2 + "].");
+ outputFile(testClass, file1);
+ outputFile(testClass, file2);
+
+ return false;
+ }
+ }
+
+ // the second file is longer
+ if (in2.read() != -1) {
+ System.out.println("File [" + file2 + "] longer than file [" + file1 + "].");
+ outputFile(testClass, file1);
+ outputFile(testClass, file2);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ static public boolean compare(final String file1, final String file2) throws FileNotFoundException, IOException {
+ try (final BufferedReader in1 = new BufferedReader(new FileReader(file1)); final BufferedReader in2 = new BufferedReader(new FileReader(file2))) {
+
+ String s1;
+ int lineCounter = 0;
+ while ((s1 = in1.readLine()) != null) {
+ lineCounter++;
+ final String s2 = in2.readLine();
+ if (!s1.equals(s2)) {
+ System.out.println("Files [" + file1 + "] and [" + file2 + "] differ on line " + lineCounter);
+ System.out.println("One reads: [" + s1 + "].");
+ System.out.println("Other reads:[" + s2 + "].");
+ return false;
+ }
+ }
+
+ // the second file is longer
+ if (in2.read() != -1) {
+ System.out.println("File [" + file2 + "] longer than file [" + file1 + "].");
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ private static final InputStream open(final Class testClass, final String fileName) throws IOException {
+ String resourceName = fileName;
+ if (fileName.startsWith("witness/")) {
+ resourceName = fileName.substring(fileName.lastIndexOf('/') + 1);
+ }
+ InputStream is = testClass.getResourceAsStream(resourceName);
+ if (is == null) {
+ final File file = new File(fileName);
+ if (file.exists()) {
+ is = new FileInputStream(file);
+ } else {
+ throw new FileNotFoundException("Resource " + resourceName + " not found");
+ }
+ }
+ return is;
+ }
+
+ /**
+ *
+ * Prints file on the console.
+ *
+ */
+ private static void outputFile(final Class testClass, final String file) throws IOException {
+ try (final InputStream is = open(testClass, file); final BufferedReader in1 = new BufferedReader(new InputStreamReader(is))) {
+
+ String s1;
+ int lineCounter = 0;
+ System.out.println("--------------------------------");
+ System.out.println("Contents of " + file + ":");
+
+ while ((s1 = in1.readLine()) != null) {
+ lineCounter++;
+ System.out.print(lineCounter);
+
+ if (lineCounter < 10) {
+ System.out.print(" : ");
+ } else if (lineCounter < 100) {
+ System.out.print(" : ");
+ } else if (lineCounter < 1000) {
+ System.out.print(" : ");
+ } else {
+ System.out.print(": ");
+ }
+
+ System.out.println(s1);
+ }
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
new file mode 100644
index 0000000..b0503b7
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.Level;
+
+/**
+ * This class introduces a new level level called TRACE. TRACE has lower level than DEBUG.
+ */
+public class XLevel extends Level {
+ private static final long serialVersionUID = 7288304330257085144L;
+
+ public static final int TRACE_INT = Level.DEBUG_INT - 1;
+ public static final int LETHAL_INT = Level.FATAL_INT + 1;
+
+ private static String TRACE_STR = "TRACE";
+ private static String LETHAL_STR = "LETHAL";
+
+ public static final XLevel TRACE = new XLevel(TRACE_INT, TRACE_STR, 7);
+ public static final XLevel LETHAL = new XLevel(LETHAL_INT, LETHAL_STR, 0);
+
+ public static Level toLevel(final int i) throws IllegalArgumentException {
+ switch (i) {
+ case TRACE_INT:
+ return XLevel.TRACE;
+ case LETHAL_INT:
+ return XLevel.LETHAL;
+ }
+ return Level.toLevel(i);
+ }
+
+ /**
+ * Convert the string passed as argument to a level. If the conversion fails, then this method returns {@link #TRACE}.
+ */
+ public static Level toLevel(final String sArg) {
+ return toLevel(sArg, XLevel.TRACE);
+ }
+
+ public static Level toLevel(final String sArg, final Level defaultValue) {
+
+ if (sArg == null) {
+ return defaultValue;
+ }
+ final String stringVal = sArg.toUpperCase();
+
+ if (stringVal.equals(TRACE_STR)) {
+ return XLevel.TRACE;
+ } else if (stringVal.equals(LETHAL_STR)) {
+ return XLevel.LETHAL;
+ }
+
+ return Level.toLevel(sArg, defaultValue);
+ }
+
+ protected XLevel(final int level, final String strLevel, final int syslogEquiv) {
+ super(level, strLevel, syslogEquiv);
+ }
+
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2a78d4b..c59773a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -184,6 +184,15 @@
<action dev="ggregory" type="fix" due-to="Gary Gregory">
Log4j 1.2 bridge missing UtilLoggingLevel.
</action>
+ <action dev="ggregory" type="fix" due-to="Gary Gregory">
+ Log4j 1.2 bridge missing FormattingInfo.
+ </action>
+ <action dev="ggregory" type="fix" due-to="Gary Gregory">
+ Log4j 1.2 bridge missing PatternConverter.
+ </action>
+ <action dev="ggregory" type="fix" due-to="Gary Gregory">
+ Log4j 1.2 bridge missing PatternParser.
+ </action>
<action dev="ggregory" type="fix">
JndiManager reverts to 2.17.0 behavior: Read the system property for each call.
</action>