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.List;
22  import java.util.StringTokenizer;
23  import java.util.Locale;
24  
25  
26  /***
27   * A singleton helper utility which accepts a field name
28   * and a LoggingEvent and returns the value of that field.
29   *
30   * This class defines a grammar used in creation of an expression-based Rule.
31   *
32   * The only available method is
33   * Object getField(String fieldName, LoggingEvent event).
34   *
35   * Here is a description of the mapping of field names in the grammar
36   * to fields on the logging event.  While the getField method returns an Object,
37   * the individual types returned per field are described here:
38   *
39   * Field Name    Field value (String representation        Return type
40   * LOGGER        category name (logger)                    String
41   * LEVEL         level                                     Level
42   * CLASS         locationInformation's class name          String
43   * FILE          locationInformation's file name           String
44   * LINE          locationInformation's line number         String
45   * METHOD        locationInformation's method name         String
46   * MSG           message                                   Object
47   * NDC           NDC                                       String
48   * EXCEPTION     throwable string representation           ThrowableInformation
49   * TIMESTAMP     timestamp                                 Long
50   * THREAD        thread                                    String
51   * PROP.keyName  entry in the Property hashtable           String
52   *               mapped to the key [keyName]
53  
54   * NOTE:  the values for the 'keyName' portion of the MDC and PROP mappings must
55   * be an exact match to the key in the hashTable (case sensitive).
56   *
57   * If the passed-in field is null or doesn't match an entry
58   * in the above-described mapping, an exception is thrown.
59   *
60   * @author Scott Deboy (sdeboy@apache.org)
61   * @author Paul Smith (psmith@apache.org)
62   *
63   */
64  public final class LoggingEventFieldResolver {
65      /***
66       * Keyword list.
67       */
68    public static final List KEYWORD_LIST = new ArrayList();
69      /***
70       * LOGGER string literal.
71       */
72    public static final String LOGGER_FIELD = "LOGGER";
73      /***
74       * LEVEL string literal.
75       */
76    public static final String LEVEL_FIELD = "LEVEL";
77      /***
78       * CLASS string literal.
79       */
80    public static final String CLASS_FIELD = "CLASS";
81      /***
82       * FILE string literal.
83       */
84    public static final String FILE_FIELD = "FILE";
85      /***
86       * LINE string literal.
87       */
88    public static final String LINE_FIELD = "LINE";
89      /***
90       * METHOD string literal.
91       */
92    public static final String METHOD_FIELD = "METHOD";
93      /***
94       * MSG string literal.
95       */
96    public static final String MSG_FIELD = "MSG";
97      /***
98       * NDC string literal.
99       */
100   public static final String NDC_FIELD = "NDC";
101     /***
102      * EXCEPTION string literal.
103      */
104   public static final String EXCEPTION_FIELD = "EXCEPTION";
105     /***
106      * TIMESTAMP string literal.
107      */
108   public static final String TIMESTAMP_FIELD = "TIMESTAMP";
109     /***
110      * THREAD string literal.
111      */
112   public static final String THREAD_FIELD = "THREAD";
113     /***
114      * PROP. string literal.
115      */
116   public static final String PROP_FIELD = "PROP.";
117     /***
118      * empty string literal.
119      */
120   public static final String EMPTY_STRING = "";
121     /***
122      * LOGGER string literal.
123      */
124   private static final LoggingEventFieldResolver RESOLVER =
125     new LoggingEventFieldResolver();
126 
127     /***
128      * Create new instance.
129      */
130   private LoggingEventFieldResolver() {
131     super();
132     KEYWORD_LIST.add(LOGGER_FIELD);
133     KEYWORD_LIST.add(LEVEL_FIELD);
134     KEYWORD_LIST.add(CLASS_FIELD);
135     KEYWORD_LIST.add(FILE_FIELD);
136     KEYWORD_LIST.add(LINE_FIELD);
137     KEYWORD_LIST.add(METHOD_FIELD);
138     KEYWORD_LIST.add(MSG_FIELD);
139     KEYWORD_LIST.add(NDC_FIELD);
140     KEYWORD_LIST.add(EXCEPTION_FIELD);
141     KEYWORD_LIST.add(TIMESTAMP_FIELD);
142     KEYWORD_LIST.add(THREAD_FIELD);
143     KEYWORD_LIST.add(PROP_FIELD);
144   }
145 
146     /***
147      * Apply fields.
148      * @param replaceText replacement text.
149      * @param event logging event.
150      * @return evaluted expression
151      */
152   public String applyFields(final String replaceText,
153                             final LoggingEvent event) {
154       if (replaceText == null) {
155         return null;
156       }
157       StringTokenizer tokenizer = new StringTokenizer(replaceText);
158       StringBuffer result = new StringBuffer();
159       boolean found = false;
160 
161       while (tokenizer.hasMoreTokens()) {
162           String token = tokenizer.nextToken();
163           if (isField(token) || token.toUpperCase(Locale.US).startsWith(PROP_FIELD)) {
164               result.append(getValue(token, event).toString());
165               found = true;
166           } else {
167               result.append(token);
168           }
169       }
170       if (found) {
171         return result.toString();
172       }
173       return null;
174   }
175 
176     /***
177      * Get singleton instance.
178      * @return singleton instance
179      */
180   public static LoggingEventFieldResolver getInstance() {
181     return RESOLVER;
182   }
183 
184     /***
185      * Determines if specified string is a recognized field.
186      * @param fieldName field name
187      * @return true if recognized field.
188      */
189   public boolean isField(final String fieldName) {
190     if (fieldName != null) {
191         return (KEYWORD_LIST.contains(
192                 fieldName.toUpperCase(Locale.US))
193                 || fieldName.toUpperCase().startsWith(PROP_FIELD));
194     }
195     return false;
196   }
197 
198     /***
199      * Get value of field.
200      * @param fieldName field
201      * @param event event
202      * @return value of field
203      */
204   public Object getValue(final String fieldName,
205                          final LoggingEvent event) {
206     String upperField = fieldName.toUpperCase(Locale.US);
207     if (LOGGER_FIELD.equals(upperField)) {
208       return event.getLoggerName();
209     } else if (LEVEL_FIELD.equals(upperField)) {
210       return event.getLevel();
211     } else if (MSG_FIELD.equals(upperField)) {
212       return event.getMessage();
213     } else if (NDC_FIELD.equals(upperField)) {
214       String ndcValue = event.getNDC();
215       return ((ndcValue == null) ? EMPTY_STRING : ndcValue);
216     } else if (EXCEPTION_FIELD.equals(upperField)) {
217         String[] throwableRep = event.getThrowableStrRep();
218         if (throwableRep == null) {
219             return EMPTY_STRING;
220         } else {
221             return getExceptionMessage(throwableRep);
222         }
223     } else if (TIMESTAMP_FIELD.equals(upperField)) {
224       return new Long(event.timeStamp);
225     } else if (THREAD_FIELD.equals(upperField)) {
226       return event.getThreadName();
227     } else if (upperField.startsWith(PROP_FIELD)) {
228       //note: need to use actual fieldname since case matters
229       Object propValue = event.getMDC(fieldName.substring(5));
230       return ((propValue == null) ? EMPTY_STRING : propValue.toString());
231     } else {
232         LocationInfo info = event.getLocationInformation();
233         if (CLASS_FIELD.equals(upperField)) {
234             return ((info == null) ? EMPTY_STRING : info.getClassName());
235         } else if (FILE_FIELD.equals(upperField)) {
236             return ((info == null) ? EMPTY_STRING : info.getFileName());
237         } else if (LINE_FIELD.equals(upperField)) {
238             return ((info == null) ? EMPTY_STRING : info.getLineNumber());
239         } else if (METHOD_FIELD.equals(upperField)) {
240             return ((info == null) ? EMPTY_STRING : info.getMethodName());
241         }
242     }
243 
244     //there wasn't a match, so throw a runtime exception
245     throw new IllegalArgumentException("Unsupported field name: " + fieldName);
246   }
247 
248     /***
249      * Get message from throwable representation.
250      * @param exception exception
251      * @return message
252      */
253     private static String getExceptionMessage(final String[] exception) {
254         StringBuffer buff = new StringBuffer();
255         for (int i = 0; i < exception.length; i++) {
256             buff.append(exception[i]);
257         }
258         return buff.toString();
259     }
260 }