KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > server > logging > logviewer > backend > LogFilter


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.enterprise.server.logging.logviewer.backend;
24
25 import java.io.*;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Date JavaDoc;
28 import java.util.List JavaDoc;
29 import java.text.SimpleDateFormat JavaDoc;
30 import java.text.ParseException JavaDoc;
31 import java.util.Properties JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.StringTokenizer JavaDoc;
34 import java.util.*;
35 import javax.management.Attribute JavaDoc;
36 import javax.management.AttributeList JavaDoc;
37
38 import com.sun.enterprise.server.logging.ModuleToLoggerNameMapper;
39 import com.sun.enterprise.util.StringUtils;
40 import com.sun.enterprise.util.SystemPropertyConstants;
41
42 /**
43  * <p>
44  * LogFilter will be used by Admin Too Log Viewer Front End to filter the
45  * records. LogMBean delegates the getLogRecordsUsingQuery method to this
46  * class static method.
47  *
48  * @AUTHOR: Hemanth Puttaswamy and Ken Paulsen
49  */

50 public class LogFilter {
51
52     // This is the name of the Results Attribute that we send out to the
53
// Admin front end.
54
private static final String JavaDoc RESULTS_ATTRIBUTE = "Results";
55
56
57     private static final String JavaDoc NV_SEPARATOR = ";";
58
59     /**
60      * The public method that Log Viewer Front End will be calling on.
61      * The query will be run on records starting from the fromRecord.
62      * If any of the elements for the query is null, then that element will
63      * not be used for the query. If the user is interested in viewing only
64      * records whose Log Level is SEVERE and WARNING, then the query would
65      * look like:
66      *
67      * fromDate = null, toDate = null, logLevel = WARNING, onlyLevel = false,
68      * listOfModules = null, nameValueMap = null.
69      *
70      * @param logFileName The LogFile to use to run the query. If null
71      * the current server.log will be used. This is
72      * not the absolute file name, just the fileName
73      * needs to be passed. We will use the parent
74      * directory of the previous server.log to
75      * build the absolute file name.
76      * @param fromRecord The location within the LogFile
77      * @param next True to get the next set of results, false to
78      * get the previous set
79      * @param forward True to search forward through the log file
80      * @param requestedCount The # of desired return values
81      * @param fromDate The lower bound date
82      * @param toDate The upper bound date
83      * @param logLevel The minimum log level to display
84      * @param onlyLevel True to only display messsage for "logLevel"
85      * @param listOfModules List of modules to match
86      * @param nameValueMap NVP's to match
87      *
88      * @return
89      */

90     public static AttributeList JavaDoc getLogRecordsUsingQuery(
91         String JavaDoc logFileName, Long JavaDoc fromRecord, Boolean JavaDoc next, Boolean JavaDoc forward,
92         Integer JavaDoc requestedCount, Date JavaDoc fromDate, Date JavaDoc toDate,
93         String JavaDoc logLevel, Boolean JavaDoc onlyLevel, List JavaDoc listOfModules,
94         Properties JavaDoc nameValueMap)
95     {
96         LogFile logFile = null;
97         if( ( logFileName != null )
98           &&( logFileName.length() != 0 ) )
99         {
100             logFile = getLogFile( logFileName );
101         } else {
102             logFile = getLogFile( );
103         }
104         boolean forwd = (forward == null) ? true : forward.booleanValue();
105         boolean nxt = (next == null) ? true : next.booleanValue();
106         long reqCount = (requestedCount == null) ?
107             logFile.getIndexSize() : requestedCount.intValue();
108         long startingRecord;
109         if (fromRecord == null) {
110             // In this case next/previous (before/after) don't mean much since
111
// we don't have a reference record number. So set before/after
112
// according to the direction.
113
nxt = forwd;
114
115             // We +1 for reverse so that we see the very end of the file (the
116
// query will not go past the "startingRecord", so we have to put
117
// it after the end of the file)
118
startingRecord = forwd ?
119                 (-1) :((logFile.getLastIndexNumber()+1)*logFile.getIndexSize());
120         } else {
121             startingRecord = fromRecord.longValue();
122             if (startingRecord < -1) {
123                 
124                 throw new IllegalArgumentException JavaDoc(
125                     "fromRecord must be greater than 0!");
126             }
127         }
128
129         // TO DO: If the fromRecord count is zero and the fromDate entry is
130
// non-null, then the system should take advantage of file Indexing.
131
// It should move the file position to the marker where the DateTime
132
// query matches.
133
try {
134             return fetchRecordsUsingQuery(logFile, startingRecord, nxt, forwd,
135                 reqCount, fromDate, toDate, logLevel,
136                 onlyLevel.booleanValue(), listOfModules, nameValueMap);
137         } catch (Exception JavaDoc ex) {
138             System.err.println( "Exception in fetchRecordsUsingQuer.." + ex );
139             // FIXME: Handle this correctly...
140
throw new RuntimeException JavaDoc(ex);
141         }
142     }
143
144
145     /**
146      * Internal method that will be called from getLogRecordsUsingQuery()
147      */

148     protected static AttributeList JavaDoc fetchRecordsUsingQuery(
149         LogFile logFile, long startingRecord, boolean next, boolean forward,
150         long requestedCount, Date JavaDoc fromDate, Date JavaDoc toDate, String JavaDoc logLevel,
151         boolean onlyLevel, List JavaDoc listOfModules, Properties JavaDoc nameValueMap)
152     {
153         // If !next, then set to search in reverse
154
boolean origForward = forward;
155         if (next) {
156             startingRecord++;
157             forward = true;
158         } else {
159             forward = false;
160         }
161
162         // Setup forward/reverse stuff
163
int inc = 1;
164         int start = 0; // depends on length of results (for reverse)
165
int end = -1; // depends on length of results (for forward)
166
long searchChunkIncrement = requestedCount;
167         if (!forward) {
168             inc = -1;
169             // Move back to find records before the startingRecord
170
// -1 because we still want to see the starting record (only if in
171
// "next" mode)
172
startingRecord -=
173                 ((next) ? (searchChunkIncrement-1) : (searchChunkIncrement));
174             if (startingRecord < 0) {
175                 // Don't go past the original startingRecord
176
searchChunkIncrement += startingRecord;
177                 startingRecord = 0;
178             }
179         }
180
181         // Make sure the module names are correct
182
//updateModuleList(listOfModules);
183

184         // Keep pulling records to search through until we get enough matches
185
List JavaDoc results = new ArrayList JavaDoc();
186         List JavaDoc records = null;
187         LogFile.LogEntry entry = null;
188         while (results.size() < requestedCount) {
189             // The following will always return unfiltered forward records
190
records = logFile.getLogEntries(
191                 startingRecord, searchChunkIncrement);
192             if (records == null) {
193                 break;
194             }
195
196             // Determine end/start
197
if (forward) {
198                 end = records.size();
199             } else {
200                 start = records.size()-1;
201             }
202
203             // Loop through the records, filtering and storing the matches
204
for (int count=start;
205                 (count != end) && (results.size() < requestedCount);
206                 count += inc)
207             {
208                 entry = (LogFile.LogEntry)records.get(count);
209                 if (allChecks(entry, fromDate, toDate, logLevel, onlyLevel,
210                         listOfModules, nameValueMap)) {
211                     results.add(entry);
212                 }
213             }
214
215             // Update startingRecord / searchChunkIncrement & check for finish
216
if (forward) {
217                 // If the record size is smaller than requested, then there
218
// are no more records.
219
if (records.size() < searchChunkIncrement) {
220                     break;
221                 }
222
223                 // Get starting record BEFORE updating searchChunkIncrement to
224
// skip all the records we already saw
225
startingRecord += searchChunkIncrement*inc;
226                 searchChunkIncrement = requestedCount-results.size();
227             } else {
228                 // If we already searched from 0, then there are no more
229
if (startingRecord == 0) {
230                     break;
231                 }
232
233                 // Get starting record AFTER updating searchChunkIncrement
234
searchChunkIncrement = requestedCount-results.size();
235                 startingRecord += searchChunkIncrement*inc;
236                 if (startingRecord < 1) {
237                     searchChunkIncrement += startingRecord;
238                     startingRecord = 0;
239                 }
240             }
241         }
242
243         // Deal with previous&forward or next&reverse
244
if (next ^ origForward) {
245             List JavaDoc reversedResults = new ArrayList JavaDoc();
246             // Reverse the results
247
for (int count=results.size()-1; count>-1; count--) {
248                 reversedResults.add(results.get(count));
249             }
250             results = reversedResults;
251         }
252
253         // Return the matches. If this is less than requested, then there are
254
// no more.
255
return convertResultsToTheStructure( results );
256     }
257
258     /**
259      * This method converts the results to the appropriate structure for
260      * LogMBean to return to the Admin Front End.
261      *
262      * AttributeList Results contain 2 attributes
263      *
264      * Attribute 1: Contains the Header Information, that lists out all the
265      * Field Names and Positions
266      * Attribute 2: Contains the actual Results, Each Log record is an entry
267      * of this result. The LogRecord itself is an ArrayList of
268      * all fields.
269      *
270      */

271     private static AttributeList JavaDoc convertResultsToTheStructure( List JavaDoc results ) {
272         if( results == null ) { return null; }
273         AttributeList JavaDoc resultsInTemplate = new AttributeList JavaDoc( );
274         resultsInTemplate.add( LogRecordTemplate.getHeader( ) );
275         Iterator JavaDoc iterator = results.iterator( ) ;
276         ArrayList JavaDoc listOfResults = new ArrayList JavaDoc( );
277         Attribute JavaDoc resultsAttribute = new Attribute JavaDoc( RESULTS_ATTRIBUTE,
278             listOfResults );
279         resultsInTemplate.add( resultsAttribute );
280         while( iterator.hasNext() ) {
281             LogFile.LogEntry entry = (LogFile.LogEntry) iterator.next();
282             ArrayList JavaDoc logRecord = new ArrayList JavaDoc( );
283             logRecord.add( new Long JavaDoc(entry.getRecordNumber()) );
284             logRecord.add( entry.getLoggedDateTime() );
285             logRecord.add( entry.getLoggedLevel() );
286             logRecord.add( entry.getLoggedProduct() );
287             logRecord.add( entry.getLoggedLoggerName() );
288             logRecord.add( entry.getLoggedNameValuePairs() );
289             logRecord.add( entry.getMessageId() );
290             logRecord.add( entry.getLoggedMessage() );
291             listOfResults.add( logRecord );
292         }
293         return resultsInTemplate;
294     }
295    
296     
297
298
299     /**
300      * This provides access to the LogFile object.
301      */

302     public static LogFile getLogFile() {
303         return _logFile;
304     }
305
306     /**
307      * This fetches or updates logFileCache entries.
308      *
309      * _REVISIT_: We may want to limit the entries here as each logFile
310      * takes up so much of memory to maintain indexes
311      */

312     public static LogFile getLogFile( String JavaDoc fileName ) {
313         // No need to check for null or zero length string as the
314
// test is already done before.
315
String JavaDoc logFileName = fileName.trim( );
316         LogFile logFile = (LogFile) logFileCache.get( fileName );
317         String JavaDoc parent = null;
318         if( logFile == null ) {
319             try {
320                 // First check if the fileName provided is an absolute filename
321
// if yes, then we don't have to construct the parent element
322
// path with the parent.
323
if( new File( fileName ).exists( ) ) {
324                     logFile = new LogFile( fileName );
325                     logFileCache.put( fileName, logFile );
326                     return logFile;
327                 }
328                                                                                 
329                 // If the absolute path is not provided, the burden of
330
// constructing the parent path falls on us. We try
331
// using the default parent path used for the current LogFile.
332
if( getLogFile() != null ) {
333                     parent = new File(
334                         getLogFile().getLogFileName() ).getParent( );
335                 } else {
336                     String JavaDoc[] logPath = { System.getProperty(
337                         SystemPropertyConstants.INSTANCE_ROOT_PROPERTY ),
338                         "logs"};
339                     parent = StringUtils.makeFilePath( logPath, false );
340                 }
341             } catch( Exception JavaDoc e ) {
342                 System.err.println( "Exception " + e +
343                     "thrown in Logviewer backend" );
344             }
345             if( parent != null ) {
346                 // Just use the parent directory from the other server.log
347
// file.
348
String JavaDoc[] logFileNameParts = { parent, logFileName };
349                 logFileName = StringUtils.makeFilePath(
350                     logFileNameParts , false );
351             }
352             logFile = new LogFile( logFileName );
353             logFileCache.put( fileName, logFile );
354         }
355         return logFile;
356     }
357
358
359     /**
360      *
361      */

362     public static synchronized void setLogFile(LogFile logFile) {
363         _logFile=logFile;
364     }
365
366
367     /**
368      * Utility method to replace the Module Names with their actual logger
369      * names.
370      */

371     protected static void updateModuleList(List JavaDoc listOfModules) {
372         if (listOfModules == null) {
373             return;
374         }
375         Iterator JavaDoc iterator = listOfModules.iterator();
376         int index = 0;
377         while (iterator.hasNext()) {
378             String JavaDoc[] loggerNames = ModuleToLoggerNameMapper.getLoggerNames(
379                 ((String JavaDoc)iterator.next()).trim());
380             if (loggerNames!=null && loggerNames.length>0) {
381                listOfModules.set(index, loggerNames[0]); //todo: support multiple loggers per module
382
}
383             index++;
384         }
385     }
386
387
388     /**
389      * This method accepts the first line of the Log Record and checks
390      * to see if it matches the query.
391      */

392     protected static boolean allChecks(LogFile.LogEntry entry,
393             Date JavaDoc fromDate, Date JavaDoc toDate, String JavaDoc queryLevel, boolean onlyLevel,
394             List JavaDoc listOfModules, Properties JavaDoc nameValueMap) {
395
396         if ((!dateTimeCheck(entry.getLoggedDateTime(), fromDate, toDate))
397            || (!levelCheck(entry.getLoggedLevel(), queryLevel, onlyLevel))
398            || (!moduleCheck(entry.getLoggedLoggerName(), listOfModules))
399            || (!nameValueCheck(entry.getLoggedNameValuePairs(), nameValueMap)))
400         {
401             return false;
402         }
403
404         return true;
405     }
406
407
408     protected static boolean dateTimeCheck(Date JavaDoc loggedDateTime,
409             Date JavaDoc fromDateTime, Date JavaDoc toDateTime)
410     {
411         if ((fromDateTime == null) || (toDateTime == null)) {
412             // If user doesn't specify fromDate and toDate, then S/He is
413
// not interested in DateTime filter
414
return true;
415         }
416         // Now do a range check
417
if (!(loggedDateTime.before(fromDateTime) ||
418             loggedDateTime.after(toDateTime))) {
419             return true;
420         }
421
422         return false;
423     }
424
425
426     protected static boolean levelCheck(String JavaDoc loggedLevel, String JavaDoc queryLevel,
427             boolean isOnlyLevelFlag)
428     {
429         // If queryLevel is null, that means user is not interested in
430
// running the query on the Log Level field.
431
if (queryLevel == null) {
432             return true;
433         }
434         queryLevel.trim();
435         if (isOnlyLevelFlag) {
436             // This means the user is interested in seeing log messages whose
437
// log level is equal to what is specified
438
if (loggedLevel.equals(queryLevel)) {
439                 return true;
440             }
441         } else {
442 // FIXME: rework this...
443
for (int idx=0; idx<LOG_LEVELS.length; idx++) {
444                 if (loggedLevel.equals(LOG_LEVELS[idx])) {
445                     return true;
446                 }
447                 if (LOG_LEVELS[idx].equals(queryLevel)) {
448                     break;
449                 }
450             }
451         }
452         return false;
453     }
454
455     protected static boolean moduleCheck(String JavaDoc loggerName, List JavaDoc modules) {
456         if ((modules == null) || (modules.size() == 0)) {
457             return true;
458         }
459
460         Iterator JavaDoc iterator = modules.iterator();
461         while (iterator.hasNext()) {
462             if (loggerName.startsWith((String JavaDoc)iterator.next())) {
463                 return true;
464             }
465         }
466         return false;
467     }
468
469     protected static boolean nameValueCheck(String JavaDoc loggedNameValuePairs,
470             Properties JavaDoc queriedNameValueMap) {
471         if (queriedNameValueMap == null) {
472             return true;
473         }
474         if (loggedNameValuePairs == null) {
475             // We didn't match the name values...
476
return false;
477         }
478         StringTokenizer JavaDoc nvListTokenizer =
479             new StringTokenizer JavaDoc( loggedNameValuePairs, NV_SEPARATOR );
480         while( nvListTokenizer.hasMoreTokens( ) ) {
481             String JavaDoc nameandvalue = nvListTokenizer.nextToken( );
482             StringTokenizer JavaDoc nvToken = new StringTokenizer JavaDoc( nameandvalue, "=" );
483             if (nvToken.countTokens() < 2)
484                 continue;
485             String JavaDoc loggedName = nvToken.nextToken( );
486             String JavaDoc loggedValue = nvToken.nextToken( );
487
488             // Reset the iterator to start from the first entry AGAIN
489
// FIXME: Is there any other cleaner way to reset the iterator
490
// position to zero than recreating a new iterator everytime
491
Iterator JavaDoc queriedNameValueMapIterator =
492                 queriedNameValueMap.entrySet().iterator( );
493
494             while( queriedNameValueMapIterator.hasNext( ) ) {
495                 Map.Entry entry =
496                     (Map.Entry) queriedNameValueMapIterator.next();
497                 if(entry.getKey().equals( loggedName ) ) {
498                     Object JavaDoc value = entry.getValue( );
499                     // We have a key with multiple values to match.
500
// This will happen if the match condition is like
501
// _ThreadID=10 or _ThreadID=11
502
// _REVISIT_: There is an opportunity to improve performance
503
// for this search.
504
Iterator JavaDoc iterator = ((java.util.List JavaDoc) value).iterator( );
505                     while( iterator.hasNext( ) ) {
506                         if( ((String JavaDoc)iterator.next()).equals(
507                             loggedValue ) )
508                         {
509                             return true;
510                         }
511                     }
512                 }
513             }
514         }
515         return false;
516     }
517
518
519     protected static final String JavaDoc[] LOG_LEVELS = { "SEVERE", "WARNING",
520         "INFO", "CONFIG", "FINE", "FINER", "FINEST" };
521
522     private static SimpleDateFormat JavaDoc SIMPLE_DATE_FORMAT =
523         new SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
524
525     private static String JavaDoc[] serverLogElements =
526         {System.getProperty("com.sun.aas.instanceRoot"), "logs", "server.log"};
527
528     private static LogFile _logFile = new LogFile(
529         StringUtils.makeFilePath( serverLogElements, false ) );
530
531     private static Hashtable logFileCache = new Hashtable( );
532 }
533
Popular Tags