View Javadoc
1   package net.sf.logdistiller;
2   
3   /*
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  import java.io.*;
18  import java.util.*;
19  
20  import org.apache.commons.io.IOUtils;
21  import org.apache.commons.lang.ArrayUtils;
22  
23  /**
24   * The class executing a log distillation, using a log distiller, and processing every log events. A log distillation
25   * can be run either with LogDistiller's {@link net.sf.logdistiller.gui GUI} or with {@link net.sf.logdistiller.ant Ant
26   * tasks}.
27   *
28   * @see LogDistiller
29   */
30  public class LogDistillation
31      implements Serializable
32  {
33      private static final long serialVersionUID = -9098523382541148543L;
34  
35      public final static String LINE_SEPARATOR = System.getProperty( "line.separator" );
36  
37      private final LogDistiller ld;
38  
39      private final Group[] groups;
40  
41      private final Category[] categories;
42  
43      private final String content;
44  
45      private final File destinationDirectory;
46  
47      private final String version = LogDistiller.getVersion();
48  
49      private transient Map<String, Group> mapGroups;
50  
51      private transient Map<String, Category> mapCategories;
52  
53      private long beginTime;
54  
55      private long endTime;
56  
57      private int eventCount = -1;
58  
59      private long eventBytes = 0;
60  
61      private int skippedEventCount = 0;
62  
63      private long skippedEventBytes = 0;
64  
65      private transient LogType.Description logtypeDescription;
66  
67      public LogDistillation( LogDistiller ld )
68      {
69          this.ld = ld;
70  
71          // init categories
72          LogDistiller.Category[] categoryDefinitions = ld.getCategories();
73          int length = categoryDefinitions.length;
74          categories = new Category[length];
75          mapCategories = new HashMap<String, Category>();
76          for ( int i = 0; i < length; i++ )
77          {
78              categories[i] = new Category( categoryDefinitions[i] );
79              mapCategories.put( categoryDefinitions[i].getId(), categories[i] );
80          }
81  
82          // init groups
83          LogDistiller.Group[] groupDefinitions = ld.getGroups();
84          length = groupDefinitions.length;
85          groups = new Group[length];
86          for ( int i = 0; i < length; i++ )
87          {
88              LogDistiller.Group groupDefinition = groupDefinitions[i];
89              LogDistiller.Category categoryDefinition = groupDefinition.getCategory();
90              Category category =
91                  ( categoryDefinition == null ) ? null : (Category) mapCategories.get( categoryDefinition.getId() );
92              groups[i] = new Group( groupDefinition, category );
93          }
94  
95          // misc.
96          content = ld.getOutput().getContent();
97          destinationDirectory = new File( ld.getOutput().getDirectory() );
98          updateRef();
99      }
100 
101     /**
102      * Create a logtype description with parameters defined in LogDistiller configuration.
103      * @return a new logtype description
104      */
105     private LogType.Description newLogTypeDescription()
106     {
107         LogDistiller.LogType lt = ld.getLogType();
108 
109         LogType logtype = LogTypes.getLogType( lt.getId() );
110         LogType.Description logtypeDescription = logtype.newDescription( ld.getLogType().getParams() );
111         logtypeDescription.setExtensions( ld.getLogType().getExtensions() );
112         ld.checkLogtypeAttributes( logtypeDescription );
113 
114         return logtypeDescription;
115     }
116 
117     /**
118      * update internal maps, skip value and references from categories and groups
119      */
120     private void updateRef()
121     {
122         String skip = ld.getOutput().getSkip();
123 
124         // process categories
125         mapCategories = new HashMap<String, Category>();
126         Category skipCategory = null;
127         int len = categories.length;
128         for ( int i = 0; i < len; i++ )
129         {
130             Category category = categories[i];
131             mapCategories.put( category.getDefinition().getId(), category );
132             category.updateRef( this );
133 
134             if ( category.getDefinition().getId().equals( skip ) )
135             {
136                 skipCategory = category;
137             }
138         }
139 
140         // process groups
141         mapGroups = new HashMap<String, Group>();
142         len = groups.length;
143         for ( int i = 0; i < len; i++ )
144         {
145             Group group = groups[i];
146             mapGroups.put( group.getDefinition().getId(), group );
147             group.updateRef( this );
148 
149             if ( group.getDefinition().getId().equals( skip ) )
150             {
151                 group.setSkip( true );
152             }
153         }
154 
155         if ( skipCategory != null )
156         {
157             skipCategory.setSkip( true );
158         }
159 
160         logtypeDescription = newLogTypeDescription();
161     }
162 
163     private void readObject( ObjectInputStream in )
164         throws IOException, ClassNotFoundException
165     {
166         in.defaultReadObject();
167         if ( ld.getOutput() != null )
168         {
169             ld.updateRef(); // workaround: a problem with Group deserialization happens
170             updateRef();
171         }
172     }
173 
174     /**
175      * begin the distillation
176      *
177      * @throws IOException
178      */
179     public void begin()
180         throws IOException
181     {
182         destinationDirectory.mkdirs();
183         int count = groups.length;
184         for ( int i = 0; i < count; i++ )
185         {
186             groups[i].begin( destinationDirectory );
187         }
188         eventCount = 0;
189         beginTime = System.currentTimeMillis();
190     }
191 
192     /**
193      * Process a log event
194      *
195      * @param le LogEvent the log event
196      * @throws IOException if an error occurs when (eventually) saving the log event
197      */
198     public void processLogEvent( LogEvent le )
199         throws IOException
200     {
201         if ( eventCount < 0 )
202         {
203             throw new IllegalStateException( "LogDistiller not initialized: need to call begin() before processing" );
204         }
205         eventCount++;
206         int eventSize = le.getRawLog().length() + LINE_SEPARATOR.length();
207         eventBytes += eventSize;
208         int count = groups.length;
209         for ( int i = 0; i < count; i++ )
210         {
211             Group group = groups[i];
212             if ( group.accept( le ) )
213             {
214                 if ( group.isSkip() )
215                 {
216                     // logevent matched a skipped group => update counters
217                     eventCount--;
218                     eventBytes -= eventSize;
219                     skippedEventCount++;
220                     skippedEventBytes += eventSize;
221                     break;
222                 }
223                 if ( !group.getDefinition().continueProcessing() )
224                 {
225                     break;
226                 }
227             }
228         }
229     }
230 
231     /**
232      * end the distillation
233      *
234      * @throws IOException if an error occurs when closing the files where log events were (eventually) saved
235      */
236     public void end()
237         throws IOException
238     {
239         endTime = System.currentTimeMillis();
240         int count = groups.length;
241         for ( int i = 0; i < count; i++ )
242         {
243             Group group = groups[i];
244             group.end();
245         }
246         saveTo( newDestinationFile( "result.ser" ) );
247     }
248 
249     /**
250      * Saves the distillation to a file, in Java serialized format.
251      *
252      * @param file File destination file.
253      */
254     public void saveTo( File file )
255         throws IOException
256     {
257         OutputStream out = null;
258         try
259         {
260             out = new FileOutputStream( file );
261             saveTo( out );
262         }
263         finally
264         {
265             IOUtils.closeQuietly( out );
266         }
267     }
268 
269     public void saveTo( OutputStream out )
270         throws IOException
271     {
272         ObjectOutputStream obj = new ObjectOutputStream( out );
273         obj.writeObject( this );
274     }
275 
276     public List<Category> listCategoriesToReport()
277     {
278         List<Category> reportCategories = new ArrayList<Category>();
279         int count = categories.length;
280         for ( int i = 0; i < count; i++ )
281         {
282             Category category = categories[i];
283             if ( ( !category.isSkip() ) && ( category.sumEventCount() > 0 ) )
284             {
285                 reportCategories.add( category );
286             }
287         }
288         return reportCategories;
289     }
290 
291     public List<Group> listGroupsToReport()
292     {
293         List<Group> reportGroups = new ArrayList<Group>();
294         int count = groups.length;
295         for ( int i = 0; i < count; i++ )
296         {
297             Group group = groups[i];
298             if ( ( !group.isSkip() ) && ( group.getEventCount() > 0 ) && ( group.getCategory() == null ) )
299             {
300                 reportGroups.add( group );
301             }
302         }
303         return reportGroups;
304     }
305 
306     public LogDistiller getDefinition()
307     {
308         return ld;
309     }
310 
311     public Group[] getGroups()
312     {
313         return (Group[]) ArrayUtils.clone( groups );
314     }
315 
316     public Group getGroup( String id )
317     {
318         return mapGroups.get( id );
319     }
320 
321     public Category[] getCategories()
322     {
323         return (Category[]) ArrayUtils.clone( categories );
324     }
325 
326     public Category getCategory( String id )
327     {
328         return mapCategories.get( id );
329     }
330 
331     public String getContent()
332     {
333         return content;
334     }
335 
336     public int getEventCount()
337     {
338         return eventCount;
339     }
340 
341     public long getEventBytes()
342     {
343         return eventBytes;
344     }
345 
346     public long getBeginTime()
347     {
348         return beginTime;
349     }
350 
351     public long getEndTime()
352     {
353         return endTime;
354     }
355 
356     public int getSkippedEventCount()
357     {
358         return skippedEventCount;
359     }
360 
361     public long getSkippedEventBytes()
362     {
363         return skippedEventBytes;
364     }
365 
366     public File newDestinationFile( String filename )
367     {
368         return new File( destinationDirectory, filename );
369     }
370 
371     public LogType.Description getLogTypeDescription()
372     {
373         return logtypeDescription;
374     }
375 
376     /**
377      * What is the version of <b>LogDistiller</b> that was used to create this instance?
378      *
379      * @return String the version
380      */
381     public String getVersion()
382     {
383         return ( version == null ) ? "unknown" : version;
384     }
385 
386     public class Group
387         implements Serializable
388     {
389         private static final long serialVersionUID = 5788707827350407084L;
390 
391         private final LogDistiller.Group definition;
392 
393         private final Category category;
394 
395         private transient LogDistillation logdistillation;
396 
397         private boolean skip = false;
398 
399         /**
400          * maximum number of events saved, -1 = no limit
401          *
402          * @since 0.7
403          */
404         private final int maxSaveCount;
405 
406         /**
407          * maximum size sum of events saved in bytes, -1 = no limit
408          *
409          * @since 0.7
410          */
411         private final long maxSaveBytes;
412 
413         private final Plugin[] plugins;
414 
415         private File logFile;
416 
417         private transient PrintStream logStream;
418 
419         private int eventCount = 0;
420 
421         private int savedEventCount = 0;
422 
423         private long bytes = 0;
424 
425         public Group( LogDistiller.Group definition, Category category )
426         {
427             this.definition = definition;
428             this.category = category;
429             maxSaveCount = Integer.parseInt( definition.getParam( "maxSave.count", "-1" ) );
430             maxSaveBytes = Long.parseLong( definition.getParam( "maxSave.size", "-1" ) ) * 1024;
431             LogDistiller.Plugin[] pluginDefs = definition.getPlugins();
432             plugins = new Plugin[pluginDefs.length];
433             for ( int i = 0; i < pluginDefs.length; i++ )
434             {
435                 plugins[i] = Plugins.newInstance( pluginDefs[i] );
436             }
437         }
438 
439         private void updateRef( LogDistillation logdistillation )
440         {
441             this.logdistillation = logdistillation;
442             if ( category != null )
443             {
444                 category.addGroup( this );
445             }
446             int len = plugins.length;
447             for ( int i = 0; i < len; i++ )
448             {
449                 plugins[i].updateRef( this );
450             }
451         }
452 
453         public LogDistillation getLogdistillation()
454         {
455             return logdistillation;
456         }
457 
458         public Category getCategory()
459         {
460             return category;
461         }
462 
463         public void setSkip( boolean skip )
464         {
465             this.skip = skip;
466         }
467 
468         public boolean isSkip()
469         {
470             return skip;
471         }
472 
473         /**
474          * Check if given LogEvent meet one of the conditions defined.
475          *
476          * @param le LogEvent the LogEvent to check
477          * @throws IOException
478          * @return boolean <code>true</code> if the LogEvent meets a condition
479          */
480         public boolean accept( LogEvent le )
481             throws IOException
482         {
483             Condition condition = definition.accept( le );
484             if ( condition != null )
485             {
486                 addLogEvent( le, condition );
487                 return true;
488             }
489             return false;
490         }
491 
492         /**
493          * Add a LogEvent to the current Group.
494          *
495          * @param le LogEvent
496          * @param condition the condition that matched the LogEvent
497          * @throws IOException
498          */
499         protected void addLogEvent( LogEvent le, Condition condition )
500             throws IOException
501         {
502             eventCount++;
503             bytes += le.getRawLog().length() + LINE_SEPARATOR.length();
504             if ( definition.getSave() && ( ( maxSaveCount < 0 ) || ( eventCount <= maxSaveCount ) )
505                 && ( ( maxSaveBytes < 0 ) || ( bytes <= maxSaveBytes ) ) )
506             {
507                 if ( logStream == null )
508                 {
509                     logStream = new PrintStream( new FileOutputStream( logFile ) );
510                 }
511                 logStream.println( le.getRawLog() );
512                 savedEventCount++;
513             }
514             int len = plugins.length;
515             for ( int i = 0; i < len; i++ )
516             {
517                 plugins[i].addLogEvent( le, condition );
518             }
519         }
520 
521         void begin( File destinationDirectory )
522             throws IOException
523         {
524             logFile = newDestinationFile( definition.getId() + ".log" );
525             int len = plugins.length;
526             for ( int i = 0; i < len; i++ )
527             {
528                 plugins[i].begin( destinationDirectory );
529             }
530         }
531 
532         void end()
533             throws IOException
534         {
535             if ( logStream != null )
536             {
537                 logStream.close();
538             }
539             int len = plugins.length;
540             for ( int i = 0; i < len; i++ )
541             {
542                 plugins[i].end();
543             }
544         }
545 
546         public LogDistiller.Group getDefinition()
547         {
548             return definition;
549         }
550 
551         public int getEventCount()
552         {
553             return eventCount;
554         }
555 
556         public int getSavedEventCount()
557         {
558             return savedEventCount;
559         }
560 
561         public long getBytes()
562         {
563             return bytes;
564         }
565 
566         public File getLogFile()
567         {
568             return logFile;
569         }
570 
571         public int getMaxSaveCount()
572         {
573             return maxSaveCount;
574         }
575 
576         public long getMaxSaveBytes()
577         {
578             return maxSaveBytes;
579         }
580 
581         public Plugin[] getPlugins()
582         {
583             return (Plugin[]) ArrayUtils.clone( plugins );
584         }
585     }
586 
587     public static class Category
588         implements Serializable
589     {
590         private static final long serialVersionUID = 7098883103191641753L;
591 
592         private final LogDistiller.Category definition;
593 
594         private transient LogDistillation logdistillation;
595 
596         private transient List<Group> groups;
597 
598         private boolean skip = false;
599 
600         public Category( LogDistiller.Category definition )
601         {
602             this.definition = definition;
603         }
604 
605         private void updateRef( LogDistillation logdistillation )
606         {
607             this.logdistillation = logdistillation;
608             groups = new ArrayList<Group>();
609         }
610 
611         public LogDistillation getLogdistillation()
612         {
613             return logdistillation;
614         }
615 
616         public LogDistiller.Category getDefinition()
617         {
618             return definition;
619         }
620 
621         private void addGroup( Group group )
622         {
623             groups.add( group );
624         }
625 
626         public List<Group> getGroups()
627         {
628             return groups;
629         }
630 
631         public void setSkip( boolean skip )
632         {
633             this.skip = skip;
634             for ( Group group : groups )
635             {
636                 group.setSkip( skip );
637             }
638         }
639 
640         public boolean isSkip()
641         {
642             return skip;
643         }
644 
645         public int sumEventCount()
646         {
647             int sum = 0;
648             for ( Group group : groups )
649             {
650                 sum += group.getEventCount();
651             }
652             return sum;
653         }
654 
655         public long sumBytes()
656         {
657             long sum = 0;
658             for ( Group group : groups )
659             {
660                 sum += group.getBytes();
661             }
662             return sum;
663         }
664 
665         public List<Group> listGroupsToReport()
666         {
667             List<Group> reportGroups = new ArrayList<Group>();
668             for ( Group group : groups )
669             {
670                 if ( group.getEventCount() > 0 )
671                 {
672                     reportGroups.add( group );
673                 }
674             }
675             return reportGroups;
676         }
677     }
678 
679     public static abstract class Plugin
680         implements Serializable
681     {
682         private static final long serialVersionUID = -722318023109374109L;
683 
684         protected final LogDistiller.Plugin definition;
685 
686         protected transient Group group;
687 
688         public Plugin( LogDistiller.Plugin definition )
689         {
690             this.definition = definition;
691         }
692 
693         private void updateRef( Group group )
694         {
695             this.group = group;
696         }
697 
698         public Group getGroup()
699         {
700             return group;
701         }
702 
703         public LogDistiller.Plugin getDefinition()
704         {
705             return definition;
706         }
707 
708         public String getId()
709         {
710             return getDefinition().getType();
711         }
712 
713         /**
714          * Begin the log distillation for this plugin instance.
715          *
716          * @param destinationDirectory the directory where reports will be stored.
717          * @throws IOException
718          */
719         abstract public void begin( File destinationDirectory )
720             throws IOException;
721 
722         /**
723          * Add a log event to this plugin instance.
724          *
725          * @param le the log event.
726          * @throws IOException
727          */
728         abstract public void addLogEvent( LogEvent le )
729             throws IOException;
730 
731         /**
732          * Add a log event to this plugin instance, with information on which condition was matched.
733          *
734          * @param le the log event.
735          * @param condition the condition which was matched
736          * @since 1.1
737          */
738         public void addLogEvent( LogEvent le, Condition condition )
739             throws IOException
740         {
741             addLogEvent( le ); // default implementation does ignore condition
742         }
743 
744         /**
745          * End the log distillation for this plugin instance.
746          *
747          * @throws IOException
748          */
749         abstract public void end()
750             throws IOException;
751 
752         /**
753          * Append a summary of this plugin instance results to the group report.
754          *
755          * @param report the plugin report formatter
756          */
757         abstract public void appendGroupReport( ReportFormat.PluginReport report );
758 
759         /**
760          * Append a summary of this plugin instance results to the global report.
761          *
762          * @param report the plugin report formatter
763          */
764         abstract public void appendGlobalReport( ReportFormat.PluginReport report );
765     }
766 }