org.apache.log4j.helpers.PatternParser Maven / Gradle / Ivy
/*
* 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 java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import org.apache.log4j.Layout;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
// Contributors: Nelson Minar <([email protected]>
// Igor E. Poteryaev
// Reinhard Deschler
/**
* Most of the work of the {@link org.apache.log4j.PatternLayout} class is delegated to the PatternParser class.
*
*
* It is this class that parses conversion patterns and creates a chained list of {@link OptionConverter
* OptionConverters}.
*
* @author James P. Cakalic
* @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();
}
}
}