View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.spi;
19  
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.Locale;
26  
27  import org.apache.log4j.rule.InFixToPostFix;
28  
29  
30  /***
31   * A singleton helper utility which accepts a field name
32   * and a LoggingEvent and returns the value of that field.
33   *
34   * This class defines a grammar used in creation of an expression-based Rule.
35   *
36   * The only available method is
37   * Object getField(String fieldName, LoggingEvent event).
38   *
39   * Here is a description of the mapping of field names in the grammar
40   * to fields on the logging event.  While the getField method returns an Object,
41   * the individual types returned per field are described here:
42   *
43   * Field Name    Field value (String representation        Return type
44   * LOGGER        category name (logger)                    String
45   * LEVEL         level                                     Level
46   * CLASS         locationInformation's class name          String
47   * FILE          locationInformation's file name           String
48   * LINE          locationInformation's line number         String
49   * METHOD        locationInformation's method name         String
50   * MSG           message                                   Object
51   * NDC           NDC                                       String
52   * EXCEPTION     throwable string representation           ThrowableInformation
53   * TIMESTAMP     timestamp                                 Long
54   * THREAD        thread                                    String
55   * PROP.keyName  entry in the Property hashtable           String
56   *               mapped to the key [keyName]
57  
58   * NOTE:  the values for the 'keyName' portion of the MDC and PROP mappings must
59   * be an exact match to the key in the hashTable (case sensitive).
60   *
61   * If the passed-in field is null or doesn't match an entry
62   * in the above-described mapping, an exception is thrown.
63   *
64   * @author Scott Deboy (sdeboy@apache.org)
65   * @author Paul Smith (psmith@apache.org)
66   *
67   */
68  public final class LoggingEventFieldResolver {
69      /***
70       * Keyword list.
71       */
72    public static final List KEYWORD_LIST = new ArrayList();
73      /***
74       * LOGGER string literal.
75       */
76    public static final String LOGGER_FIELD = "LOGGER";
77      /***
78       * LEVEL string literal.
79       */
80    public static final String LEVEL_FIELD = "LEVEL";
81      /***
82       * CLASS string literal.
83       */
84    public static final String CLASS_FIELD = "CLASS";
85      /***
86       * FILE string literal.
87       */
88    public static final String FILE_FIELD = "FILE";
89      /***
90       * LINE string literal.
91       */
92    public static final String LINE_FIELD = "LINE";
93      /***
94       * METHOD string literal.
95       */
96    public static final String METHOD_FIELD = "METHOD";
97      /***
98       * MSG string literal.
99       */
100   public static final String MSG_FIELD = "MSG";
101     /***
102      * NDC string literal.
103      */
104   public static final String NDC_FIELD = "NDC";
105     /***
106      * EXCEPTION string literal.
107      */
108   public static final String EXCEPTION_FIELD = "EXCEPTION";
109     /***
110      * TIMESTAMP string literal.
111      */
112   public static final String TIMESTAMP_FIELD = "TIMESTAMP";
113     /***
114      * THREAD string literal.
115      */
116   public static final String THREAD_FIELD = "THREAD";
117     /***
118      * PROP. string literal.
119      */
120   public static final String PROP_FIELD = "PROP.";
121     /***
122      * empty string literal.
123      */
124   public static final String EMPTY_STRING = "";
125     /***
126      * LOGGER string literal.
127      */
128   private static final LoggingEventFieldResolver RESOLVER =
129     new LoggingEventFieldResolver();
130 
131     /***
132      * Create new instance.
133      */
134   private LoggingEventFieldResolver() {
135     super();
136     KEYWORD_LIST.add(LOGGER_FIELD);
137     KEYWORD_LIST.add(LEVEL_FIELD);
138     KEYWORD_LIST.add(CLASS_FIELD);
139     KEYWORD_LIST.add(FILE_FIELD);
140     KEYWORD_LIST.add(LINE_FIELD);
141     KEYWORD_LIST.add(METHOD_FIELD);
142     KEYWORD_LIST.add(MSG_FIELD);
143     KEYWORD_LIST.add(NDC_FIELD);
144     KEYWORD_LIST.add(EXCEPTION_FIELD);
145     KEYWORD_LIST.add(TIMESTAMP_FIELD);
146     KEYWORD_LIST.add(THREAD_FIELD);
147     KEYWORD_LIST.add(PROP_FIELD);
148   }
149 
150     /***
151      * Apply fields.
152      * @param replaceText replacement text.
153      * @param event logging event.
154      * @return evaluted expression
155      */
156   public String applyFields(final String replaceText,
157                             final LoggingEvent event) {
158       if (replaceText == null) {
159         return null;
160       }
161       InFixToPostFix.CustomTokenizer tokenizer = new InFixToPostFix.CustomTokenizer(replaceText);
162       StringBuffer result = new StringBuffer();
163       boolean found = false;
164 
165       while (tokenizer.hasMoreTokens()) {
166           String token = tokenizer.nextToken();
167           if (isField(token) || token.toUpperCase(Locale.US).startsWith(PROP_FIELD)) {
168               result.append(getValue(token, event).toString());
169               found = true;
170           } else {
171               result.append(token);
172           }
173       }
174       if (found) {
175         return result.toString();
176       }
177       return null;
178   }
179 
180     /***
181      * Get singleton instance.
182      * @return singleton instance
183      */
184   public static LoggingEventFieldResolver getInstance() {
185     return RESOLVER;
186   }
187 
188     /***
189      * Determines if specified string is a recognized field.
190      * @param fieldName field name
191      * @return true if recognized field.
192      */
193   public boolean isField(final String fieldName) {
194     if (fieldName != null) {
195         return (KEYWORD_LIST.contains(
196                 fieldName.toUpperCase(Locale.US))
197                 || fieldName.toUpperCase().startsWith(PROP_FIELD));
198     }
199     return false;
200   }
201 
202     /***
203      * Get value of field.
204      * @param fieldName field
205      * @param event event
206      * @return value of field
207      */
208   public Object getValue(final String fieldName,
209                          final LoggingEvent event) {
210     String upperField = fieldName.toUpperCase(Locale.US);
211     if (LOGGER_FIELD.equals(upperField)) {
212       return event.getLoggerName();
213     } else if (LEVEL_FIELD.equals(upperField)) {
214       return event.getLevel();
215     } else if (MSG_FIELD.equals(upperField)) {
216       return event.getMessage();
217     } else if (NDC_FIELD.equals(upperField)) {
218       String ndcValue = event.getNDC();
219       return ((ndcValue == null) ? EMPTY_STRING : ndcValue);
220     } else if (EXCEPTION_FIELD.equals(upperField)) {
221         String[] throwableRep = event.getThrowableStrRep();
222         if (throwableRep == null) {
223             return EMPTY_STRING;
224         } else {
225             return getExceptionMessage(throwableRep);
226         }
227     } else if (TIMESTAMP_FIELD.equals(upperField)) {
228       return new Long(event.timeStamp);
229     } else if (THREAD_FIELD.equals(upperField)) {
230       return event.getThreadName();
231     } else if (upperField.startsWith(PROP_FIELD)) {
232       //note: need to use actual fieldname since case matters
233       Object propValue = event.getMDC(fieldName.substring(5));
234       if (propValue == null) {
235           //case-specific match didn't work, try case insensitive match
236           String lowerPropKey = fieldName.substring(5).toLowerCase();
237           Set entrySet = event.getProperties().entrySet();
238           for (Iterator iter = entrySet.iterator();iter.hasNext();) {
239               Map.Entry thisEntry = (Map.Entry) iter.next();
240               if (thisEntry.getKey().toString().toLowerCase().equals(lowerPropKey)) {
241                   propValue = thisEntry.getValue();
242               }
243           }
244       }
245       return ((propValue == null) ? EMPTY_STRING : propValue.toString());
246     } else {
247         LocationInfo info = event.getLocationInformation();
248         if (CLASS_FIELD.equals(upperField)) {
249             return ((info == null) ? EMPTY_STRING : info.getClassName());
250         } else if (FILE_FIELD.equals(upperField)) {
251             return ((info == null) ? EMPTY_STRING : info.getFileName());
252         } else if (LINE_FIELD.equals(upperField)) {
253             return ((info == null) ? EMPTY_STRING : info.getLineNumber());
254         } else if (METHOD_FIELD.equals(upperField)) {
255             return ((info == null) ? EMPTY_STRING : info.getMethodName());
256         }
257     }
258 
259     //there wasn't a match, so throw a runtime exception
260     throw new IllegalArgumentException("Unsupported field name: " + fieldName);
261   }
262 
263     /***
264      * Get message from throwable representation.
265      * @param exception exception
266      * @return message
267      */
268     private static String getExceptionMessage(final String[] exception) {
269         StringBuffer buff = new StringBuffer();
270         for (int i = 0; i < exception.length; i++) {
271             buff.append(exception[i]);
272         }
273         return buff.toString();
274     }
275 }