/* * 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.spi; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Locale; import org.apache.log4j.rule.InFixToPostFix; /** * A singleton helper utility which accepts a field name * and a LoggingEvent and returns the value of that field. * * This class defines a grammar used in creation of an expression-based Rule. * * The only available method is * Object getField(String fieldName, LoggingEvent event). * * Here is a description of the mapping of field names in the grammar * to fields on the logging event. While the getField method returns an Object, * the individual types returned per field are described here: * * Field Name Field value (String representation Return type * LOGGER category name (logger) String * LEVEL level Level * CLASS locationInformation's class name String * FILE locationInformation's file name String * LINE locationInformation's line number String * METHOD locationInformation's method name String * MSG message Object * NDC NDC String * EXCEPTION throwable string representation ThrowableInformation * TIMESTAMP timestamp Long * THREAD thread String * PROP.keyName entry in the Property hashtable String * mapped to the key [keyName] * NOTE: the values for the 'keyName' portion of the MDC and PROP mappings must * be an exact match to the key in the hashTable (case sensitive). * * If the passed-in field is null or doesn't match an entry * in the above-described mapping, an exception is thrown. * * @author Scott Deboy (sdeboy@apache.org) * @author Paul Smith (psmith@apache.org) * */ public final class LoggingEventFieldResolver { /** * Keyword list. */ public static final List KEYWORD_LIST = new ArrayList(); /** * LOGGER string literal. */ public static final String LOGGER_FIELD = "LOGGER"; /** * LEVEL string literal. */ public static final String LEVEL_FIELD = "LEVEL"; /** * CLASS string literal. */ public static final String CLASS_FIELD = "CLASS"; /** * FILE string literal. */ public static final String FILE_FIELD = "FILE"; /** * LINE string literal. */ public static final String LINE_FIELD = "LINE"; /** * METHOD string literal. */ public static final String METHOD_FIELD = "METHOD"; /** * MSG string literal. */ public static final String MSG_FIELD = "MSG"; /** * NDC string literal. */ public static final String NDC_FIELD = "NDC"; /** * EXCEPTION string literal. */ public static final String EXCEPTION_FIELD = "EXCEPTION"; /** * TIMESTAMP string literal. */ public static final String TIMESTAMP_FIELD = "TIMESTAMP"; /** * THREAD string literal. */ public static final String THREAD_FIELD = "THREAD"; /** * PROP. string literal. */ public static final String PROP_FIELD = "PROP."; /** * empty string literal. */ public static final String EMPTY_STRING = ""; /** * LOGGER string literal. */ private static final LoggingEventFieldResolver RESOLVER = new LoggingEventFieldResolver(); /** * Create new instance. */ private LoggingEventFieldResolver() { super(); KEYWORD_LIST.add(LOGGER_FIELD); KEYWORD_LIST.add(LEVEL_FIELD); KEYWORD_LIST.add(CLASS_FIELD); KEYWORD_LIST.add(FILE_FIELD); KEYWORD_LIST.add(LINE_FIELD); KEYWORD_LIST.add(METHOD_FIELD); KEYWORD_LIST.add(MSG_FIELD); KEYWORD_LIST.add(NDC_FIELD); KEYWORD_LIST.add(EXCEPTION_FIELD); KEYWORD_LIST.add(TIMESTAMP_FIELD); KEYWORD_LIST.add(THREAD_FIELD); KEYWORD_LIST.add(PROP_FIELD); } /** * Apply fields. * @param replaceText replacement text. * @param event logging event. * @return evaluted expression */ public String applyFields(final String replaceText, final LoggingEvent event) { if (replaceText == null) { return null; } InFixToPostFix.CustomTokenizer tokenizer = new InFixToPostFix.CustomTokenizer(replaceText); StringBuffer result = new StringBuffer(); boolean found = false; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (isField(token) || token.toUpperCase(Locale.US).startsWith(PROP_FIELD)) { result.append(getValue(token, event).toString()); found = true; } else { result.append(token); } } if (found) { return result.toString(); } return null; } /** * Get singleton instance. * @return singleton instance */ public static LoggingEventFieldResolver getInstance() { return RESOLVER; } /** * Determines if specified string is a recognized field. * @param fieldName field name * @return true if recognized field. */ public boolean isField(final String fieldName) { if (fieldName != null) { return (KEYWORD_LIST.contains( fieldName.toUpperCase(Locale.US)) || fieldName.toUpperCase().startsWith(PROP_FIELD)); } return false; } /** * Get value of field. * @param fieldName field * @param event event * @return value of field */ public Object getValue(final String fieldName, final LoggingEvent event) { String upperField = fieldName.toUpperCase(Locale.US); if (LOGGER_FIELD.equals(upperField)) { return event.getLoggerName(); } else if (LEVEL_FIELD.equals(upperField)) { return event.getLevel(); } else if (MSG_FIELD.equals(upperField)) { return event.getMessage(); } else if (NDC_FIELD.equals(upperField)) { String ndcValue = event.getNDC(); return ((ndcValue == null) ? EMPTY_STRING : ndcValue); } else if (EXCEPTION_FIELD.equals(upperField)) { String[] throwableRep = event.getThrowableStrRep(); if (throwableRep == null) { return EMPTY_STRING; } else { return getExceptionMessage(throwableRep); } } else if (TIMESTAMP_FIELD.equals(upperField)) { return new Long(event.timeStamp); } else if (THREAD_FIELD.equals(upperField)) { return event.getThreadName(); } else if (upperField.startsWith(PROP_FIELD)) { //note: need to use actual fieldname since case matters Object propValue = event.getMDC(fieldName.substring(5)); if (propValue == null) { //case-specific match didn't work, try case insensitive match String lowerPropKey = fieldName.substring(5).toLowerCase(); Set entrySet = event.getProperties().entrySet(); for (Iterator iter = entrySet.iterator();iter.hasNext();) { Map.Entry thisEntry = (Map.Entry) iter.next(); if (thisEntry.getKey().toString().equalsIgnoreCase(lowerPropKey)) { propValue = thisEntry.getValue(); } } } return ((propValue == null) ? EMPTY_STRING : propValue.toString()); } else { LocationInfo info = event.getLocationInformation(); if (CLASS_FIELD.equals(upperField)) { return ((info == null) ? EMPTY_STRING : info.getClassName()); } else if (FILE_FIELD.equals(upperField)) { return ((info == null) ? EMPTY_STRING : info.getFileName()); } else if (LINE_FIELD.equals(upperField)) { return ((info == null) ? EMPTY_STRING : info.getLineNumber()); } else if (METHOD_FIELD.equals(upperField)) { return ((info == null) ? EMPTY_STRING : info.getMethodName()); } } //there wasn't a match, so throw a runtime exception throw new IllegalArgumentException("Unsupported field name: " + fieldName); } /** * Get message from throwable representation. * @param exception exception * @return message */ private static String getExceptionMessage(final String[] exception) { StringBuffer buff = new StringBuffer(); for (int i = 0; i < exception.length; i++) { buff.append(exception[i]); } return buff.toString(); } }