001package net.sf.logdistiller; 002 003/* 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017import java.io.*; 018import java.util.*; 019 020import org.apache.commons.io.IOUtils; 021import org.apache.commons.lang.ArrayUtils; 022 023/** 024 * The class executing a log distillation, using a log distiller, and processing every log events. A log distillation 025 * can be run either with LogDistiller's {@link net.sf.logdistiller.gui GUI} or with {@link net.sf.logdistiller.ant Ant 026 * tasks}. 027 * 028 * @see LogDistiller 029 */ 030public class LogDistillation 031 implements Serializable 032{ 033 private static final long serialVersionUID = -9098523382541148543L; 034 035 public final static String LINE_SEPARATOR = System.getProperty( "line.separator" ); 036 037 private final LogDistiller ld; 038 039 private final Group[] groups; 040 041 private final Category[] categories; 042 043 private final String content; 044 045 private final File destinationDirectory; 046 047 private final String version = LogDistiller.getVersion(); 048 049 private transient Map<String, Group> mapGroups; 050 051 private transient Map<String, Category> mapCategories; 052 053 private long beginTime; 054 055 private long endTime; 056 057 private int eventCount = -1; 058 059 private long eventBytes = 0; 060 061 private int skippedEventCount = 0; 062 063 private long skippedEventBytes = 0; 064 065 private transient LogType.Description logtypeDescription; 066 067 public LogDistillation( LogDistiller ld ) 068 { 069 this.ld = ld; 070 071 // init categories 072 LogDistiller.Category[] categoryDefinitions = ld.getCategories(); 073 int length = categoryDefinitions.length; 074 categories = new Category[length]; 075 mapCategories = new HashMap<String, Category>(); 076 for ( int i = 0; i < length; i++ ) 077 { 078 categories[i] = new Category( categoryDefinitions[i] ); 079 mapCategories.put( categoryDefinitions[i].getId(), categories[i] ); 080 } 081 082 // init groups 083 LogDistiller.Group[] groupDefinitions = ld.getGroups(); 084 length = groupDefinitions.length; 085 groups = new Group[length]; 086 for ( int i = 0; i < length; i++ ) 087 { 088 LogDistiller.Group groupDefinition = groupDefinitions[i]; 089 LogDistiller.Category categoryDefinition = groupDefinition.getCategory(); 090 Category category = 091 ( categoryDefinition == null ) ? null : (Category) mapCategories.get( categoryDefinition.getId() ); 092 groups[i] = new Group( groupDefinition, category ); 093 } 094 095 // misc. 096 content = ld.getOutput().getContent(); 097 destinationDirectory = new File( ld.getOutput().getDirectory() ); 098 updateRef(); 099 } 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}